Files
Erupe/server/channelserver/handlers_character.go
Houmgaor 2738b19c32 refactor(channelserver): extract Goocoo, Diva, Misc, Scenario, and Mercenary repositories
Move remaining raw s.server.db.* queries from handler files into
dedicated repository structs, completing the repository extraction
effort. Also adds SaveCharacterData and SaveHouseData to
CharacterRepository.

Fixes guild_hunts query to select both cats_used and start columns
to match the existing two-column Scan call. Adds slot index
validation in GoocooRepository to prevent SQL injection via
fmt.Sprintf.
2026-02-21 13:27:08 +01:00

91 lines
2.5 KiB
Go

package channelserver
import (
"errors"
cfg "erupe-ce/config"
"erupe-ce/network/mhfpacket"
"go.uber.org/zap"
)
// GetCharacterSaveData loads a character's save data from the database.
func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) {
result, err := s.server.db.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID)
if err != nil {
s.logger.Error("Failed to get savedata", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
defer func() { _ = result.Close() }()
if !result.Next() {
err = errors.New("no savedata found")
s.logger.Error("No savedata found", zap.Uint32("charID", charID))
return nil, err
}
saveData := &CharacterSaveData{
Mode: s.server.erupeConfig.RealClientMode,
Pointers: getPointers(s.server.erupeConfig.RealClientMode),
}
err = result.Scan(&saveData.CharID, &saveData.compSave, &saveData.IsNewCharacter, &saveData.Name)
if err != nil {
s.logger.Error("Failed to scan savedata", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
if saveData.compSave == nil {
return saveData, nil
}
err = saveData.Decompress()
if err != nil {
s.logger.Error("Failed to decompress savedata", zap.Error(err))
return nil, err
}
saveData.updateStructWithSaveData()
return saveData, nil
}
func (save *CharacterSaveData) Save(s *Session) {
if save.decompSave == nil {
s.logger.Warn("No decompressed save data, skipping save",
zap.Uint32("charID", save.CharID),
)
return
}
if !s.kqfOverride {
s.kqf = save.KQF
} else {
save.KQF = s.kqf
}
save.updateSaveDataWithStruct()
if s.server.erupeConfig.RealClientMode >= cfg.G1 {
err := save.Compress()
if err != nil {
s.logger.Error("Failed to compress savedata", zap.Error(err))
return
}
} else {
// Saves were not compressed
save.compSave = save.decompSave
}
if err := s.server.charRepo.SaveCharacterData(save.CharID, save.compSave, save.HR, save.GR, save.Gender, save.WeaponType, save.WeaponID); err != nil {
s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
}
if err := s.server.charRepo.SaveHouseData(s.charID, save.HouseTier, save.HouseData, save.BookshelfData, save.GalleryData, save.ToreData, save.GardenData); err != nil {
s.logger.Error("Failed to update user binary house data", zap.Error(err))
}
}
func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSexChanger)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}