From cd6561dd61b35fca3327b657a363eb1b0c8943a9 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 3 Sep 2022 19:49:39 +1000 Subject: [PATCH 01/26] rework guild operations --- network/mhfpacket/msg_mhf_operate_guild.go | 60 +++++++------- server/channelserver/handlers_guild.go | 93 ++++++++++------------ 2 files changed, 73 insertions(+), 80 deletions(-) diff --git a/network/mhfpacket/msg_mhf_operate_guild.go b/network/mhfpacket/msg_mhf_operate_guild.go index ea986cda2..f141796cb 100644 --- a/network/mhfpacket/msg_mhf_operate_guild.go +++ b/network/mhfpacket/msg_mhf_operate_guild.go @@ -1,39 +1,39 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) type OperateGuildAction uint8 const ( - OPERATE_GUILD_DISBAND = 0x01 - OPERATE_GUILD_APPLY = 0x02 - OPERATE_GUILD_LEAVE = 0x03 - OPERATE_GUILD_RESIGN = 0x04 - OPERATE_GUILD_SET_APPLICATION_DENY = 0x05 - OPERATE_GUILD_SET_APPLICATION_ALLOW = 0x06 - OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE = 0x07 - OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE = 0x08 - OPERATE_GUILD_UPDATE_COMMENT = 0x09 - OPERATE_GUILD_DONATE_RANK = 0x0a - OPERATE_GUILD_UPDATE_MOTTO = 0x0b - OPERATE_GUILD_RENAME_PUGI_1 = 0x0c - OPERATE_GUILD_RENAME_PUGI_2 = 0x0d - OPERATE_GUILD_RENAME_PUGI_3 = 0x0e - OPERATE_GUILD_CHANGE_PUGI_1 = 0x0f - OPERATE_GUILD_CHANGE_PUGI_2 = 0x10 - OPERATE_GUILD_CHANGE_PUGI_3 = 0x11 - // pugi something - OPERATE_GUILD_DONATE_EVENT = 0x15 - // pugi something - OPERATE_GUILD_CHANGE_DIVA_PUGI_1 = 0x19 - OPERATE_GUILD_CHANGE_DIVA_PUGI_2 = 0x1a - OPERATE_GUILD_CHANGE_DIVA_PUGI_3 = 0x1b + OPERATE_GUILD_DISBAND = 0x01 + OPERATE_GUILD_APPLY = 0x02 + OPERATE_GUILD_LEAVE = 0x03 + OPERATE_GUILD_RESIGN = 0x04 + OPERATE_GUILD_SET_APPLICATION_DENY = 0x05 + OPERATE_GUILD_SET_APPLICATION_ALLOW = 0x06 + OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE = 0x07 + OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE = 0x08 + OPERATE_GUILD_UPDATE_COMMENT = 0x09 + OPERATE_GUILD_DONATE_RANK = 0x0a + OPERATE_GUILD_UPDATE_MOTTO = 0x0b + OPERATE_GUILD_RENAME_PUGI_1 = 0x0c + OPERATE_GUILD_RENAME_PUGI_2 = 0x0d + OPERATE_GUILD_RENAME_PUGI_3 = 0x0e + OPERATE_GUILD_CHANGE_PUGI_1 = 0x0f + OPERATE_GUILD_CHANGE_PUGI_2 = 0x10 + OPERATE_GUILD_CHANGE_PUGI_3 = 0x11 + // pugi something + OPERATE_GUILD_DONATE_EVENT = 0x15 + OPERATE_GUILD_EVENT_EXCHANGE = 0x16 + OPERATE_GUILD_CHANGE_DIVA_PUGI_1 = 0x19 + OPERATE_GUILD_CHANGE_DIVA_PUGI_2 = 0x1a + OPERATE_GUILD_CHANGE_DIVA_PUGI_3 = 0x1b ) // MsgMhfOperateGuild represents the MSG_MHF_OPERATE_GUILD @@ -41,7 +41,8 @@ type MsgMhfOperateGuild struct { AckHandle uint32 GuildID uint32 Action OperateGuildAction - UnkData []byte + Data1 *byteframe.ByteFrame + Data2 *byteframe.ByteFrame } // Opcode returns the ID associated with this packet type. @@ -54,8 +55,9 @@ func (m *MsgMhfOperateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien m.AckHandle = bf.ReadUint32() m.GuildID = bf.ReadUint32() m.Action = OperateGuildAction(bf.ReadUint8()) - m.UnkData = bf.DataFromCurrent() - bf.Seek(int64(len(bf.Data()) - 2), 0) + dataLen := uint(bf.ReadUint8()) + m.Data1 = byteframe.NewByteFrameFromBytes(bf.ReadBytes(4)) + m.Data2 = byteframe.NewByteFrameFromBytes(bf.ReadBytes(dataLen)) return nil } diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 6bbee7eba..327f35781 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -634,11 +634,8 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { } bf.WriteUint32(uint32(response)) - doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) - return case mhfpacket.OPERATE_GUILD_RESIGN: guildMembers, err := GetGuildMembers(s, guild.ID, false) - success := false if err == nil { sort.Slice(guildMembers[:], func(i, j int) bool { return guildMembers[i].OrderIndex < guildMembers[j].OrderIndex @@ -651,29 +648,17 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { guildMembers[0].Save(s) guildMembers[i].Save(s) bf.WriteUint32(guildMembers[i].CharID) - success = true break } } guild.Save(s) - doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) } - if !success { - bf.WriteUint32(0) - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) - } - return case mhfpacket.OPERATE_GUILD_APPLY: err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil) - if err != nil { - // All successful acks return 0x01, assuming 0x00 is failure - bf.WriteUint32(0x00) - } else { + if err == nil { bf.WriteUint32(guild.LeaderCharID) } - doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) - return case mhfpacket.OPERATE_GUILD_LEAVE: var err error @@ -698,10 +683,8 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { } bf.WriteUint32(uint32(response)) - doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) - return case mhfpacket.OPERATE_GUILD_DONATE_RANK: - handleDonateRP(s, pkt, bf, guild, false) + bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, false)) case mhfpacket.OPERATE_GUILD_SET_APPLICATION_DENY: s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID) case mhfpacket.OPERATE_GUILD_SET_APPLICATION_ALLOW: @@ -711,46 +694,55 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE: handleAvoidLeadershipUpdate(s, pkt, false) case mhfpacket.OPERATE_GUILD_UPDATE_COMMENT: - pbf := byteframe.NewByteFrameFromBytes(pkt.UnkData) if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } - _ = pbf.ReadUint8() // len - _ = pbf.ReadUint32() - guild.Comment = stringsupport.SJISToUTF8(pbf.ReadNullTerminatedBytes()) + guild.Comment = stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes()) guild.Save(s) case mhfpacket.OPERATE_GUILD_UPDATE_MOTTO: if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } - guild.SubMotto = pkt.UnkData[3] - guild.MainMotto = pkt.UnkData[4] + _ = pkt.Data1.ReadUint16() + guild.SubMotto = pkt.Data1.ReadUint8() + guild.MainMotto = pkt.Data1.ReadUint8() guild.Save(s) case mhfpacket.OPERATE_GUILD_RENAME_PUGI_1: - handleRenamePugi(s, pkt.UnkData, guild, 1) + handleRenamePugi(s, pkt.Data2, guild, 1) case mhfpacket.OPERATE_GUILD_RENAME_PUGI_2: - handleRenamePugi(s, pkt.UnkData, guild, 2) + handleRenamePugi(s, pkt.Data2, guild, 2) case mhfpacket.OPERATE_GUILD_RENAME_PUGI_3: - handleRenamePugi(s, pkt.UnkData, guild, 3) + handleRenamePugi(s, pkt.Data2, guild, 3) case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_1: // TODO: decode guild poogie outfits case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_2: case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_3: case mhfpacket.OPERATE_GUILD_DONATE_EVENT: - handleDonateRP(s, pkt, bf, guild, true) + bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, true)) + case mhfpacket.OPERATE_GUILD_EVENT_EXCHANGE: + rp := uint16(pkt.Data1.ReadUint32()) + saveData, _ := GetCharacterSaveData(s, s.charID) + saveData.RP -= rp + transaction, err := s.server.db.Begin() + err = saveData.Save(s, transaction) + if err != nil { + transaction.Rollback() + } + bf.WriteUint32(uint32(saveData.RP)) default: panic(fmt.Sprintf("unhandled operate guild action '%d'", pkt.Action)) } - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + if len(bf.Data()) > 0 { + doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) + } else { + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + } } -func handleRenamePugi(s *Session, data []byte, guild *Guild, num int) { - bf := byteframe.NewByteFrameFromBytes(data) - _ = bf.ReadUint8() // len - _ = bf.ReadUint32() // unk +func handleRenamePugi(s *Session, bf *byteframe.ByteFrame, guild *Guild, num int) { name := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) switch num { case 1: @@ -763,33 +755,30 @@ func handleRenamePugi(s *Session, data []byte, guild *Guild, num int) { guild.Save(s) } -func handleDonateRP(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, bf *byteframe.ByteFrame, guild *Guild, isEvent bool) error { - rp := binary.BigEndian.Uint16(pkt.UnkData[3:5]) +func handleDonateRP(s *Session, amount uint16, guild *Guild, isEvent bool) []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint32(0) saveData, err := GetCharacterSaveData(s, s.charID) if err != nil { - return err + return bf.Data() } - saveData.RP -= rp + saveData.RP -= amount transaction, err := s.server.db.Begin() err = saveData.Save(s, transaction) if err != nil { transaction.Rollback() - return err + return bf.Data() + } else { + transaction.Commit() } updateSQL := "UPDATE guilds SET rank_rp = rank_rp + $1 WHERE id = $2" if isEvent { updateSQL = "UPDATE guilds SET event_rp = event_rp + $1 WHERE id = $2" } - _, err = s.server.db.Exec(updateSQL, rp, guild.ID) - if err != nil { - s.logger.Error("Failed to donate rank RP to guild", zap.Error(err), zap.Uint32("guildID", guild.ID)) - transaction.Rollback() - return err - } else { - transaction.Commit() - } + s.server.db.Exec(updateSQL, amount, guild.ID) + bf.Seek(0, 0) bf.WriteUint32(uint32(saveData.RP)) - return nil + return bf.Data() } func handleAvoidLeadershipUpdate(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, avoidLeadership bool) { @@ -1712,9 +1701,11 @@ func handleMsgMhfGetGuildWeeklyBonusMaster(s *Session, p mhfpacket.MHFPacket) { } func handleMsgMhfGetGuildWeeklyBonusActiveCount(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildWeeklyBonusActiveCount) - - // Values taken from brand new guild capture - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x03)) + bf := byteframe.NewByteFrame() + bf.WriteUint8(0x3C) // Active count + bf.WriteUint8(0x3C) // Current active count + bf.WriteUint8(0x00) // New active count + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) { From 90314fa411d8a8370dfbf0801dd5b0f99da959c1 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 4 Sep 2022 03:19:32 +1000 Subject: [PATCH 02/26] rework savedata --- server/channelserver/handlers.go | 12 +- server/channelserver/handlers_character.go | 135 +++++++++++---------- server/channelserver/handlers_data.go | 60 ++++----- server/channelserver/handlers_guild.go | 15 +-- 4 files changed, 101 insertions(+), 121 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 6c49ba0af..e5476ca5f 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -235,17 +235,11 @@ func logoutPlayer(s *Session) { saveData, err := GetCharacterSaveData(s, s.charID) if err != nil { - panic(err) + s.logger.Error("Failed to get savedata") + return } saveData.RP += uint16(rpGained) - transaction, err := s.server.db.Begin() - err = saveData.Save(s, transaction) - if err != nil { - transaction.Rollback() - panic(err) - } else { - transaction.Commit() - } + saveData.Save(s) } func handleMsgSysSetStatus(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index b712f960e..aa3ab5e72 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -1,7 +1,6 @@ package channelserver import ( - "database/sql" "encoding/binary" "erupe-ce/network/mhfpacket" @@ -10,120 +9,134 @@ import ( ) const ( - CharacterSaveRPPointer = 0x22D16 + pointerGender = 0x81 // +1 + pointerRP = 0x22D16 // +2 + pointerHouseTier = 0x1FB6C // +5 + pointerHouseData = 0x1FE01 // +195 + pointerBookshelfData = 0x22298 // +5576 + // Gallery data also exists at 0x21578, is this the contest submission? + pointerGalleryData = 0x22320 // +1748 + pointerToreData = 0x1FCB4 // +240 + pointerGardenData = 0x22C58 // +68 + pointerWeaponID = 0x1F60A // +2 + pointerHRP = 0x1FDF6 // +2 + pointerGRP = 0x1FDFC // +4 ) type CharacterSaveData struct { CharID uint32 Name string - RP uint16 IsNewCharacter bool - // Use provided setter/getter - baseSaveData []byte + Gender bool + RP uint16 + HouseTier []byte + HouseData []byte + BookshelfData []byte + GalleryData []byte + ToreData []byte + GardenData []byte + WeaponID uint16 + HRP uint16 + GRP uint32 + GR uint16 + + compSave []byte + decompSave []byte } 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 retrieve save data for character", zap.Error(err), zap.Uint32("charID", charID)) + s.logger.Error("Failed to get savedata", zap.Error(err), zap.Uint32("charID", charID)) return nil, err } - defer result.Close() + if !result.Next() { + s.logger.Error("No savedata found", zap.Uint32("charID", charID)) + return nil, err + } saveData := &CharacterSaveData{} - var compressedBaseSave []byte - - if !result.Next() { - s.logger.Error("no results found for character save data", zap.Uint32("charID", charID)) - return nil, err - } - - err = result.Scan(&saveData.CharID, &compressedBaseSave, &saveData.IsNewCharacter, &saveData.Name) - + err = result.Scan(&saveData.CharID, &saveData.compSave, &saveData.IsNewCharacter, &saveData.Name) if err != nil { - s.logger.Error( - "failed to retrieve save data for character", - zap.Error(err), - zap.Uint32("charID", charID), - ) - + s.logger.Error("Failed to scan savedata", zap.Error(err), zap.Uint32("charID", charID)) return nil, err } - if compressedBaseSave == nil { + if saveData.compSave == nil { return saveData, nil } - decompressedBaseSave, err := nullcomp.Decompress(compressedBaseSave) - + err = saveData.Decompress() if err != nil { - s.logger.Error("Failed to decompress savedata from db", zap.Error(err)) + s.logger.Error("Failed to decompress savedata", zap.Error(err)) return nil, err } - saveData.SetBaseSaveData(decompressedBaseSave) - return saveData, nil } -func (save *CharacterSaveData) Save(s *Session, transaction *sql.Tx) error { +func (save *CharacterSaveData) Save(s *Session) { // We need to update the save data byte array before we save it back to the DB save.updateSaveDataWithStruct() - compressedData, err := save.CompressedBaseData(s) - + err := save.Compress() if err != nil { - return err + s.logger.Error("Failed to compress savedata", zap.Error(err)) + return } - updateSQL := "UPDATE characters SET savedata=$1, is_new_character=$3 WHERE id=$2" + updateSQL := `UPDATE characters SET savedata=$1, is_new_character=$3 WHERE id=$2` - if transaction != nil { - _, err = transaction.Exec(updateSQL, compressedData, save.CharID, save.IsNewCharacter) - } else { - _, err = s.server.db.Exec(updateSQL, compressedData, save.CharID, save.IsNewCharacter) - } + _, err = s.server.db.Exec(updateSQL, save.compSave, save.CharID, save.IsNewCharacter) + if err != nil { + s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID)) + } +} + +func (save *CharacterSaveData) Compress() error { + var err error + save.compSave, err = nullcomp.Compress(save.decompSave) if err != nil { - s.logger.Error("failed to save character data", zap.Error(err), zap.Uint32("charID", save.CharID)) return err } return nil } -func (save *CharacterSaveData) CompressedBaseData(s *Session) ([]byte, error) { - compressedData, err := nullcomp.Compress(save.baseSaveData) - +func (save *CharacterSaveData) Decompress() error { + var err error + save.decompSave, err = nullcomp.Decompress(save.compSave) if err != nil { - s.logger.Error("failed to compress saveData", zap.Error(err), zap.Uint32("charID", save.CharID)) - return nil, err + return err } - return compressedData, nil + return nil } -func (save *CharacterSaveData) BaseSaveData() []byte { - return save.baseSaveData -} - -func (save *CharacterSaveData) SetBaseSaveData(data []byte) { - save.baseSaveData = data - // After setting the new save byte array, we can extract the values to update our struct - // This will be useful when we save it back, we use the struct values to overwrite the saveData - save.updateStructWithSaveData() -} - -// This will update the save struct with the values stored in the raw savedata arrays +// This will update the character save with the values stored in the save struct func (save *CharacterSaveData) updateSaveDataWithStruct() { rpBytes := make([]byte, 2) binary.LittleEndian.PutUint16(rpBytes, save.RP) - copy(save.baseSaveData[CharacterSaveRPPointer:CharacterSaveRPPointer+2], rpBytes) + copy(save.decompSave[pointerRP:pointerRP+2], rpBytes) } -// This will update the character save struct with the values stored in the raw savedata arrays +// This will update the save struct with the values stored in the character save func (save *CharacterSaveData) updateStructWithSaveData() { - save.RP = binary.LittleEndian.Uint16(save.baseSaveData[CharacterSaveRPPointer : CharacterSaveRPPointer+2]) + if save.decompSave[pointerGender] == 1 { + save.Gender = true + } else { + save.Gender = false + } + save.RP = binary.LittleEndian.Uint16(save.decompSave[pointerRP : pointerRP+2]) + save.HouseTier = save.decompSave[pointerHouseTier : pointerHouseTier+5] + save.HouseData = save.decompSave[pointerHouseData : pointerHouseData+195] + save.BookshelfData = save.decompSave[pointerBookshelfData : pointerBookshelfData+5576] + save.GalleryData = save.decompSave[pointerGalleryData : pointerGalleryData+1748] + save.ToreData = save.decompSave[pointerToreData : pointerToreData+240] + save.GardenData = save.decompSave[pointerGardenData : pointerGardenData+68] + save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[pointerWeaponID : pointerWeaponID+2]) + save.HRP = binary.LittleEndian.Uint16(save.decompSave[pointerHRP : pointerHRP+2]) + save.GRP = binary.LittleEndian.Uint32(save.decompSave[pointerGRP : pointerGRP+4]) } func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index 8ed55c54a..048aaff73 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -1,7 +1,6 @@ package channelserver import ( - "encoding/binary" "encoding/hex" "erupe-ce/common/stringsupport" "fmt" @@ -26,7 +25,6 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { return } // Var to hold the decompressed savedata for updating the launcher response fields. - var decompressedData []byte if pkt.SaveType == 1 { // Diff-based update. // diffs themselves are also potentially compressed @@ -36,43 +34,35 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { } // Perform diff. s.logger.Info("Diffing...") - characterSaveData.SetBaseSaveData(deltacomp.ApplyDataDiff(diff, characterSaveData.BaseSaveData())) + characterSaveData.decompSave = deltacomp.ApplyDataDiff(diff, characterSaveData.decompSave) } else { + dumpSaveData(s, pkt.RawDataPayload, "savedata") // Regular blob update. saveData, err := nullcomp.Decompress(pkt.RawDataPayload) if err != nil { s.logger.Fatal("Failed to decompress savedata from packet", zap.Error(err)) } s.logger.Info("Updating save with blob") - characterSaveData.SetBaseSaveData(saveData) + characterSaveData.decompSave = saveData } characterSaveData.IsNewCharacter = false - characterBaseSaveData := characterSaveData.BaseSaveData() - // Make a copy for updating the launcher fields. - decompressedData = make([]byte, len(characterBaseSaveData)) - copy(decompressedData, characterBaseSaveData) - err = characterSaveData.Save(s, nil) - if err != nil { - s.logger.Fatal("Failed to update savedata in db", zap.Error(err)) - } + characterSaveData.Save(s) s.logger.Info("Wrote recompressed savedata back to DB.") - dumpSaveData(s, pkt.RawDataPayload, "savedata") - _, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID) + _, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(characterSaveData.decompSave[128789]), s.charID) if err != nil { s.logger.Fatal("Failed to character weapon type in db", zap.Error(err)) } - s.myseries.houseTier = decompressedData[129900:129905] // 0x1FB6C + 5 - s.myseries.houseData = decompressedData[130561:130756] // 0x1FE01 + 195 - s.myseries.bookshelfData = decompressedData[139928:145504] // 0x22298 + 5576 - // Gallery data also exists at 0x21578, is this the contest submission? - s.myseries.galleryData = decompressedData[140064:141812] // 0x22320 + 1748 - s.myseries.toreData = decompressedData[130228:130468] // 0x1FCB4 + 240 - s.myseries.gardenData = decompressedData[142424:142492] // 0x22C58 + 68 + s.myseries.houseTier = characterSaveData.HouseTier + s.myseries.houseData = characterSaveData.HouseData + s.myseries.bookshelfData = characterSaveData.BookshelfData + s.myseries.galleryData = characterSaveData.GalleryData + s.myseries.toreData = characterSaveData.ToreData + s.myseries.gardenData = characterSaveData.GardenData - isFemale := decompressedData[81] // 0x51 - if isFemale == 1 { + isFemale := characterSaveData.Gender + if isFemale { _, err = s.server.db.Exec("UPDATE characters SET is_female=true WHERE id=$1", s.charID) } else { _, err = s.server.db.Exec("UPDATE characters SET is_female=false WHERE id=$1", s.charID) @@ -81,32 +71,26 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { s.logger.Fatal("Failed to character gender in db", zap.Error(err)) } - weaponId := binary.LittleEndian.Uint16(decompressedData[128522:128524]) // 0x1F60A - _, err = s.server.db.Exec("UPDATE characters SET weapon_id=$1 WHERE id=$2", weaponId, s.charID) + _, err = s.server.db.Exec("UPDATE characters SET weapon_id=$1 WHERE id=$2", characterSaveData.WeaponID, s.charID) if err != nil { s.logger.Fatal("Failed to update character weapon id in db", zap.Error(err)) } - hrp := binary.LittleEndian.Uint16(decompressedData[130550:130552]) // 0x1FDF6 - _, err = s.server.db.Exec("UPDATE characters SET hrp=$1 WHERE id=$2", hrp, s.charID) + _, err = s.server.db.Exec("UPDATE characters SET hrp=$1 WHERE id=$2", characterSaveData.HRP, s.charID) if err != nil { s.logger.Fatal("Failed to update character hrp in db", zap.Error(err)) } - grp := binary.LittleEndian.Uint32(decompressedData[130556:130560]) // 0x1FDFC - var gr uint16 - if grp > 0 { - gr = grpToGR(grp) - } else { - gr = 0 + if characterSaveData.GRP > 0 { + characterSaveData.GR = grpToGR(characterSaveData.GRP) } - _, err = s.server.db.Exec("UPDATE characters SET gr=$1 WHERE id=$2", gr, s.charID) + _, err = s.server.db.Exec("UPDATE characters SET gr=$1 WHERE id=$2", characterSaveData.GR, s.charID) if err != nil { s.logger.Fatal("Failed to update character gr in db", zap.Error(err)) } - characterName := s.clientContext.StrConv.MustDecode(bfutil.UpToNull(decompressedData[88:100])) - _, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterName, s.charID) + characterSaveData.Name = s.clientContext.StrConv.MustDecode(bfutil.UpToNull(characterSaveData.decompSave[88:100])) + _, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterSaveData.Name, s.charID) if err != nil { s.logger.Fatal("Failed to update character name in db", zap.Error(err)) } @@ -320,7 +304,7 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveScenarioData) - _, err := s.server.db.Exec("UPDATE characters SET scenariodata = $1 WHERE characters.id = $2", pkt.RawDataPayload, int(s.charID)) + _, err := s.server.db.Exec("UPDATE characters SET scenariodata = $1 WHERE id = $2", pkt.RawDataPayload, s.charID) if err != nil { s.logger.Fatal("Failed to update scenario data in db", zap.Error(err)) } @@ -337,7 +321,7 @@ func handleMsgMhfLoadScenarioData(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfLoadScenarioData) var scenarioData []byte bf := byteframe.NewByteFrame() - err := s.server.db.QueryRow("SELECT scenariodata FROM characters WHERE characters.id = $1", int(s.charID)).Scan(&scenarioData) + err := s.server.db.QueryRow("SELECT scenariodata FROM characters WHERE id = $1", s.charID).Scan(&scenarioData) if err != nil { s.logger.Fatal("Failed to get scenario data contents in db", zap.Error(err)) } else { diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 327f35781..f34005a66 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -725,11 +725,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { rp := uint16(pkt.Data1.ReadUint32()) saveData, _ := GetCharacterSaveData(s, s.charID) saveData.RP -= rp - transaction, err := s.server.db.Begin() - err = saveData.Save(s, transaction) - if err != nil { - transaction.Rollback() - } + saveData.Save(s) bf.WriteUint32(uint32(saveData.RP)) default: panic(fmt.Sprintf("unhandled operate guild action '%d'", pkt.Action)) @@ -763,14 +759,7 @@ func handleDonateRP(s *Session, amount uint16, guild *Guild, isEvent bool) []byt return bf.Data() } saveData.RP -= amount - transaction, err := s.server.db.Begin() - err = saveData.Save(s, transaction) - if err != nil { - transaction.Rollback() - return bf.Data() - } else { - transaction.Commit() - } + saveData.Save(s) updateSQL := "UPDATE guilds SET rank_rp = rank_rp + $1 WHERE id = $2" if isEvent { updateSQL = "UPDATE guilds SET event_rp = event_rp + $1 WHERE id = $2" From 2e6aa1f1e4d0fa5b04305fa098dcaa0249993e8d Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 4 Sep 2022 03:32:43 +1000 Subject: [PATCH 03/26] remove unused config options --- config.json | 1 - config/config.go | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/config.json b/config.json index 00e541adf..6c3b4ecd6 100644 --- a/config.json +++ b/config.json @@ -4,7 +4,6 @@ "DisableSoftCrash": false, "devmode": true, "devmodeoptions": { - "serverName" : "", "EnableLauncherServer": false, "hideLoginNotice": false, "loginNotice": "
Welcome to Erupe SU9.1 Beta!
Erupe is experimental software, we are not liable for any
issues caused by installing the software!

■Report bugs on Discord!

■Test everything!

■Don't talk to softlocking NPCs!

■Fork the code on GitHub!

Thank you to all of the contributors,

this wouldn't exist without you.", diff --git a/config/config.go b/config/config.go index bfbf8086a..33bf22abe 100644 --- a/config/config.go +++ b/config/config.go @@ -24,13 +24,11 @@ type Config struct { // DevModeOptions holds various debug/temporary options for use while developing Erupe. type DevModeOptions struct { - ServerName string // To get specific instance server about (Current Players/Event Week) EnableLauncherServer bool // Enables the launcher server to be served on port 80 HideLoginNotice bool // Hide the Erupe notice on login LoginNotice string // MHFML string of the login notice displayed CleanDB bool // Automatically wipes the DB on server reset. - MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds. - FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages + MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds. LogInboundMessages bool // Log all messages sent to the server LogOutboundMessages bool // Log all messages sent to the clients MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled @@ -53,10 +51,7 @@ type SaveDumpOptions struct { type Discord struct { Enabled bool BotToken string - ServerID string RealtimeChannelID string - DevRoles []string - DevMode bool } // Database holds the postgres database config. From 6c9e39a5cd68a77a2aa8c6181848f873f0111df1 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 4 Sep 2022 15:52:50 +1000 Subject: [PATCH 04/26] fix savedata bugs --- server/channelserver/handlers_character.go | 12 ++++--- server/channelserver/handlers_data.go | 40 ---------------------- 2 files changed, 7 insertions(+), 45 deletions(-) diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index aa3ab5e72..825924508 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -18,6 +18,7 @@ const ( pointerGalleryData = 0x22320 // +1748 pointerToreData = 0x1FCB4 // +240 pointerGardenData = 0x22C58 // +68 + pointerWeaponType = 0x1F715 // +1 pointerWeaponID = 0x1F60A // +2 pointerHRP = 0x1FDF6 // +2 pointerGRP = 0x1FDFC // +4 @@ -36,9 +37,9 @@ type CharacterSaveData struct { GalleryData []byte ToreData []byte GardenData []byte + WeaponType uint8 WeaponID uint16 HRP uint16 - GRP uint32 GR uint16 compSave []byte @@ -80,6 +81,7 @@ func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) func (save *CharacterSaveData) Save(s *Session) { // We need to update the save data byte array before we save it back to the DB save.updateSaveDataWithStruct() + save.updateStructWithSaveData() err := save.Compress() if err != nil { @@ -87,9 +89,8 @@ func (save *CharacterSaveData) Save(s *Session) { return } - updateSQL := `UPDATE characters SET savedata=$1, is_new_character=$3 WHERE id=$2` - - _, err = s.server.db.Exec(updateSQL, save.compSave, save.CharID, save.IsNewCharacter) + _, err = s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=$2, hrp=$3, gr=$4, is_female=$5, weapon_type=$6, weapon_id=$7 WHERE id=$8 + `, save.compSave, save.IsNewCharacter, save.HRP, save.GR, save.Gender, save.WeaponType, save.WeaponID, save.CharID) if err != nil { s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID)) } @@ -134,9 +135,10 @@ func (save *CharacterSaveData) updateStructWithSaveData() { save.GalleryData = save.decompSave[pointerGalleryData : pointerGalleryData+1748] save.ToreData = save.decompSave[pointerToreData : pointerToreData+240] save.GardenData = save.decompSave[pointerGardenData : pointerGardenData+68] + save.WeaponType = save.decompSave[pointerWeaponType] save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[pointerWeaponID : pointerWeaponID+2]) save.HRP = binary.LittleEndian.Uint16(save.decompSave[pointerHRP : pointerHRP+2]) - save.GRP = binary.LittleEndian.Uint32(save.decompSave[pointerGRP : pointerGRP+4]) + save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[pointerGRP : pointerGRP+4])) } func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index 048aaff73..57a2641f9 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -49,46 +49,6 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { characterSaveData.Save(s) s.logger.Info("Wrote recompressed savedata back to DB.") - _, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(characterSaveData.decompSave[128789]), s.charID) - if err != nil { - s.logger.Fatal("Failed to character weapon type in db", zap.Error(err)) - } - - s.myseries.houseTier = characterSaveData.HouseTier - s.myseries.houseData = characterSaveData.HouseData - s.myseries.bookshelfData = characterSaveData.BookshelfData - s.myseries.galleryData = characterSaveData.GalleryData - s.myseries.toreData = characterSaveData.ToreData - s.myseries.gardenData = characterSaveData.GardenData - - isFemale := characterSaveData.Gender - if isFemale { - _, err = s.server.db.Exec("UPDATE characters SET is_female=true WHERE id=$1", s.charID) - } else { - _, err = s.server.db.Exec("UPDATE characters SET is_female=false WHERE id=$1", s.charID) - } - if err != nil { - s.logger.Fatal("Failed to character gender in db", zap.Error(err)) - } - - _, err = s.server.db.Exec("UPDATE characters SET weapon_id=$1 WHERE id=$2", characterSaveData.WeaponID, s.charID) - if err != nil { - s.logger.Fatal("Failed to update character weapon id in db", zap.Error(err)) - } - - _, err = s.server.db.Exec("UPDATE characters SET hrp=$1 WHERE id=$2", characterSaveData.HRP, s.charID) - if err != nil { - s.logger.Fatal("Failed to update character hrp in db", zap.Error(err)) - } - - if characterSaveData.GRP > 0 { - characterSaveData.GR = grpToGR(characterSaveData.GRP) - } - _, err = s.server.db.Exec("UPDATE characters SET gr=$1 WHERE id=$2", characterSaveData.GR, s.charID) - if err != nil { - s.logger.Fatal("Failed to update character gr in db", zap.Error(err)) - } - characterSaveData.Name = s.clientContext.StrConv.MustDecode(bfutil.UpToNull(characterSaveData.decompSave[88:100])) _, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterSaveData.Name, s.charID) if err != nil { From 925947631616326a222fee87c7b6c94cc2423033 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 4 Sep 2022 15:53:24 +1000 Subject: [PATCH 05/26] implement persistent house data --- patch-schema/persistent-house.sql | 19 +++ server/channelserver/handlers_character.go | 3 + server/channelserver/handlers_house.go | 157 ++++++++------------- server/channelserver/handlers_users.go | 8 +- 4 files changed, 88 insertions(+), 99 deletions(-) create mode 100644 patch-schema/persistent-house.sql diff --git a/patch-schema/persistent-house.sql b/patch-schema/persistent-house.sql new file mode 100644 index 000000000..ee26333ee --- /dev/null +++ b/patch-schema/persistent-house.sql @@ -0,0 +1,19 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS public.user_binary +( + id serial NOT NULL PRIMARY KEY, + type2 bytea, + type3 bytea, + house_tier bytea, + house_state int, + house_password text, + house_data bytea, + house_furniture bytea, + bookshelf bytea, + gallery bytea, + tore bytea, + garden bytea +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index 825924508..13459bda9 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -94,6 +94,9 @@ func (save *CharacterSaveData) Save(s *Session) { if err != nil { s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID)) } + + s.server.db.Exec(`UPDATE user_binary SET house_tier=$1, house_data=$2, bookshelf=$3, gallery=$4, tore=$5, garden=$6 WHERE id=$7 + `, save.HouseTier, save.HouseData, save.BookshelfData, save.GalleryData, save.ToreData, save.GardenData, s.charID) } func (save *CharacterSaveData) Compress() error { diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 280f27238..3198b4c88 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -38,24 +38,26 @@ FROM warehouse func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateInterior) - _, err := s.server.db.Exec("UPDATE characters SET house=$1 WHERE id=$2", pkt.InteriorData, s.charID) - if err != nil { - panic(err) - } + s.server.db.Exec(`UPDATE user_binary SET house_furniture=$1 WHERE id=$2`, pkt.InteriorData, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } type HouseData struct { - CharID uint32 `db:"id"` - HRP uint16 `db:"hrp"` - GR uint16 `db:"gr"` - Name string `db:"name"` + CharID uint32 `db:"id"` + HRP uint16 `db:"hrp"` + GR uint16 `db:"gr"` + Name string `db:"name"` + HouseState uint8 `db:"house_state"` + HousePassword string `db:"house_password"` } func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateHouse) bf := byteframe.NewByteFrame() + bf.WriteUint16(0) var houses []HouseData + houseQuery := `SELECT c.id, hrp, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password + FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE c.id=$1` switch pkt.Method { case 1: var friendsList string @@ -63,17 +65,15 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) { cids := stringsupport.CSVElems(friendsList) for _, cid := range cids { house := HouseData{} - row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", cid) + row := s.server.db.QueryRowx(houseQuery, cid) err := row.StructScan(&house) - if err != nil { - panic(err) - } else { + if err == nil { houses = append(houses, house) } } case 2: guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil { + if err != nil || guild == nil { break } guildMembers, err := GetGuildMembers(s, guild.ID, false) @@ -82,58 +82,48 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) { } for _, member := range guildMembers { house := HouseData{} - row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", member.CharID) - err := row.StructScan(&house) - if err != nil { - panic(err) - } else { + row := s.server.db.QueryRowx(houseQuery, member.CharID) + err = row.StructScan(&house) + if err == nil { houses = append(houses, house) } } case 3: + houseQuery = `SELECT c.id, hrp, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password + FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE name ILIKE $1` house := HouseData{} - row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name ILIKE $1", fmt.Sprintf(`%%%s%%`, pkt.Name)) - err := row.StructScan(&house) - if err != nil { - panic(err) - } else { - houses = append(houses, house) + rows, _ := s.server.db.Queryx(houseQuery, fmt.Sprintf(`%%%s%%`, pkt.Name)) + for rows.Next() { + err := rows.StructScan(&house) + if err == nil { + houses = append(houses, house) + } } case 4: house := HouseData{} - row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", pkt.CharID) + row := s.server.db.QueryRowx(houseQuery, pkt.CharID) err := row.StructScan(&house) - if err != nil { - panic(err) - } else { + if err == nil { houses = append(houses, house) } case 5: // Recent visitors break } - var exists int for _, house := range houses { - for _, session := range s.server.sessions { - if session.charID == house.CharID { - exists++ - bf.WriteUint32(house.CharID) - bf.WriteUint8(session.myseries.state) - if len(session.myseries.password) > 0 { - bf.WriteUint8(3) - } else { - bf.WriteUint8(0) - } - bf.WriteUint16(house.HRP) - bf.WriteUint16(house.GR) - ps.Uint8(bf, house.Name, true) - break - } + bf.WriteUint32(house.CharID) + bf.WriteUint8(house.HouseState) + if len(house.HousePassword) > 0 { + bf.WriteUint8(3) + } else { + bf.WriteUint8(0) } + bf.WriteUint16(house.HRP) + bf.WriteUint16(house.GR) + ps.Uint8(bf, house.Name, true) } - resp := byteframe.NewByteFrame() - resp.WriteUint16(uint16(exists)) - resp.WriteBytes(bf.Data()) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + bf.Seek(0, 0) + bf.WriteUint16(uint16(len(houses))) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) { @@ -143,8 +133,7 @@ func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) { // 03 = open friends // 04 = open guild // 05 = open friends+guild - s.myseries.state = pkt.State - s.myseries.password = pkt.Password + s.server.db.Exec(`UPDATE user_binary SET house_state=$1, house_password=$2 WHERE id=$3`, pkt.State, pkt.Password, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -152,63 +141,41 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfLoadHouse) bf := byteframe.NewByteFrame() if pkt.Destination != 9 && len(pkt.Password) > 0 && pkt.CheckPass { - for _, session := range s.server.sessions { - if session.charID == pkt.CharID && pkt.Password != session.myseries.password { - doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) - return - } + var password string + s.server.db.Select(&password, `SELECT house_password FROM user_binary WHERE id=$1`, pkt.CharID) + if pkt.Password != password { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return } } - var furniture []byte - err := s.server.db.QueryRow("SELECT house FROM characters WHERE id=$1", pkt.CharID).Scan(&furniture) - if err != nil { - panic(err) - } - if furniture == nil { - furniture = make([]byte, 20) + var houseTier, houseData, houseFurniture, bookshelf, gallery, tore, garden []byte + s.server.db.QueryRow(`SELECT house_tier, house_data, house_furniture, bookshelf, gallery, tore, garden FROM user_binary WHERE id=$1 + `, pkt.CharID).Scan(&houseTier, &houseData, &houseFurniture, &bookshelf, &gallery, &tore, &garden) + if houseFurniture == nil { + houseFurniture = make([]byte, 20) } switch pkt.Destination { case 3: // Others house - for _, session := range s.server.sessions { - if session.charID == pkt.CharID { - bf.WriteBytes(session.myseries.houseTier) - bf.WriteBytes(session.myseries.houseData) - bf.WriteBytes(make([]byte, 19)) // Padding? - bf.WriteBytes(furniture) - } - } + bf.WriteBytes(houseTier) + bf.WriteBytes(houseData) + bf.WriteBytes(make([]byte, 19)) // Padding? + bf.WriteBytes(houseFurniture) case 4: // Bookshelf - for _, session := range s.server.sessions { - if session.charID == pkt.CharID { - bf.WriteBytes(session.myseries.bookshelfData) - } - } + bf.WriteBytes(bookshelf) case 5: // Gallery - for _, session := range s.server.sessions { - if session.charID == pkt.CharID { - bf.WriteBytes(session.myseries.galleryData) - } - } + bf.WriteBytes(gallery) case 8: // Tore - for _, session := range s.server.sessions { - if session.charID == pkt.CharID { - bf.WriteBytes(session.myseries.toreData) - } - } + bf.WriteBytes(tore) case 9: // Own house - bf.WriteBytes(furniture) + bf.WriteBytes(houseFurniture) case 10: // Garden - for _, session := range s.server.sessions { - if session.charID == pkt.CharID { - bf.WriteBytes(session.myseries.gardenData) - c, d := getGookData(s, pkt.CharID) - bf.WriteUint16(c) - bf.WriteUint16(0) - bf.WriteBytes(d) - } - } + bf.WriteBytes(garden) + c, d := getGookData(s, pkt.CharID) + bf.WriteUint16(c) + bf.WriteUint16(0) + bf.WriteBytes(d) } if len(bf.Data()) == 0 { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) diff --git a/server/channelserver/handlers_users.go b/server/channelserver/handlers_users.go index 3bebcc97a..c360b82a6 100644 --- a/server/channelserver/handlers_users.go +++ b/server/channelserver/handlers_users.go @@ -17,12 +17,12 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) { s.server.userBinaryPartsLock.Unlock() var exists []byte - err := s.server.db.QueryRow("SELECT type2 FROM user_binaries WHERE id=$1", s.charID).Scan(&exists) + err := s.server.db.QueryRow("SELECT type2 FROM user_binary WHERE id=$1", s.charID).Scan(&exists) if err != nil { - s.server.db.Exec("INSERT INTO user_binaries (id) VALUES ($1)", s.charID) + s.server.db.Exec("INSERT INTO user_binary (id) VALUES ($1)", s.charID) } - s.server.db.Exec(fmt.Sprintf("UPDATE user_binaries SET type%d=$1 WHERE id=$2", pkt.BinaryType), pkt.RawDataPayload, s.charID) + s.server.db.Exec(fmt.Sprintf("UPDATE user_binary SET type%d=$1 WHERE id=$2", pkt.BinaryType), pkt.RawDataPayload, s.charID) msg := &mhfpacket.MsgSysNotifyUserBinary{ CharID: s.charID, @@ -42,7 +42,7 @@ func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) { // If we can't get the real data, try to get it from the database. if !ok { - err := s.server.db.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binaries WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data) + err := s.server.db.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binary WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data) if err != nil { doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) } else { From 377bb39be6298c5578b31fb875f0570f33480095 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 4 Sep 2022 17:45:05 +1000 Subject: [PATCH 06/26] fix house password checking --- server/channelserver/handlers_house.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 3198b4c88..7d5bda529 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -142,7 +142,10 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() if pkt.Destination != 9 && len(pkt.Password) > 0 && pkt.CheckPass { var password string - s.server.db.Select(&password, `SELECT house_password FROM user_binary WHERE id=$1`, pkt.CharID) + err := s.server.db.Get(&password, `SELECT house_password FROM user_binary WHERE id=$1`, pkt.CharID) + if err != nil { + panic(err) + } if pkt.Password != password { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return From 670f8f7882fb2c8c03fcf8a2719656999b3ab740 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 4 Sep 2022 18:12:51 +1000 Subject: [PATCH 07/26] update schema to merge existing data, move trophy --- patch-schema/persistent-house.sql | 20 +++++++++++++++++++- server/channelserver/handlers_house.go | 14 +++----------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/patch-schema/persistent-house.sql b/patch-schema/persistent-house.sql index ee26333ee..43a02da91 100644 --- a/patch-schema/persistent-house.sql +++ b/patch-schema/persistent-house.sql @@ -13,7 +13,25 @@ CREATE TABLE IF NOT EXISTS public.user_binary bookshelf bytea, gallery bytea, tore bytea, - garden bytea + garden bytea, + mission bytea ); +-- Create entries for existing users +INSERT INTO public.user_binary (id) SELECT c.id FROM characters c; + +-- Copy existing data +UPDATE public.user_binary + SET house_furniture = (SELECT house FROM characters WHERE user_binary.id = characters.id); + +UPDATE public.user_binary + SET mission = (SELECT trophy FROM characters WHERE user_binary.id = characters.id); + +-- Drop old data location +ALTER TABLE public.characters + DROP COLUMN house; + +ALTER TABLE public.characters + DROP COLUMN trophy; + END; \ No newline at end of file diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 7d5bda529..5236f197d 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -189,26 +189,18 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetMyhouseInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetMyhouseInfo) - var data []byte - err := s.server.db.QueryRow("SELECT trophy FROM characters WHERE id = $1", s.charID).Scan(&data) - if err != nil { - panic(err) - } + s.server.db.QueryRow(`SELECT mission FROM user_binary WHERE id=$1`, s.charID).Scan(&data) if len(data) > 0 { doAckBufSucceed(s, pkt.AckHandle, data) } else { - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 9)) } } func handleMsgMhfUpdateMyhouseInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateMyhouseInfo) - - _, err := s.server.db.Exec("UPDATE characters SET trophy=$1 WHERE id=$2", pkt.Unk0, s.charID) - if err != nil { - panic(err) - } + s.server.db.Exec("UPDATE user_binary SET mission=$1 WHERE id=$2", pkt.Unk0, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } From bb12a890743a677c6fb6edd835abe674276adbbd Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 5 Sep 2022 14:29:25 +1000 Subject: [PATCH 08/26] fix nil character data error --- server/channelserver/handlers_guild.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index f34005a66..cfdef3f15 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1005,17 +1005,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { } applicants, err := GetGuildMembers(s, guild.ID, true) - if err != nil { - resp := byteframe.NewByteFrame() - resp.WriteUint32(0) // Count - resp.WriteUint8(0) // Unk, read if count == 0. - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - } - if err != nil || characterGuildData.IsApplicant { - bf.WriteUint16(0) - } else { bf.WriteUint16(uint16(len(applicants))) for _, applicant := range applicants { bf.WriteUint32(applicant.CharID) @@ -1025,9 +1015,11 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(applicant.GR) ps.Uint8(bf, applicant.Name, true) } + } else { + bf.WriteUint16(0) } - bf.WriteUint16(0x0000) + bf.WriteUint16(0x0000) // lenAllianceApplications /* alliance application format From 65d35a5188a082f6ef3138be73b01736ee879299 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 5 Sep 2022 14:34:48 +1000 Subject: [PATCH 09/26] dump navi savedata --- server/channelserver/handlers_mercenary.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index 66c7da046..5e9e7b62e 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -104,6 +104,7 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) { s.logger.Info("Wrote recompressed hunternavi back to DB.") } else { + dumpSaveData(s, pkt.RawDataPayload, "hunternavi") // simply update database, no extra processing _, err := s.server.db.Exec("UPDATE characters SET hunternavi=$1 WHERE id=$2", pkt.RawDataPayload, s.charID) if err != nil { From e38c8926140d73545e05761f37d2cc6b6d6bcffb Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 5 Sep 2022 15:24:07 +1000 Subject: [PATCH 10/26] fix guild application enumeration --- server/channelserver/handlers_guild.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index cfdef3f15..f7646926c 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1006,6 +1006,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { applicants, err := GetGuildMembers(s, guild.ID, true) if err != nil { + bf.WriteUint16(0) + } else { bf.WriteUint16(uint16(len(applicants))) for _, applicant := range applicants { bf.WriteUint32(applicant.CharID) @@ -1015,8 +1017,6 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(applicant.GR) ps.Uint8(bf, applicant.Name, true) } - } else { - bf.WriteUint16(0) } bf.WriteUint16(0x0000) // lenAllianceApplications From b1721684182545631572cf05ce3aedf671efa548 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 5 Sep 2022 15:26:57 +1000 Subject: [PATCH 11/26] implement proper house entry verification --- network/mhfpacket/msg_mhf_enumerate_house.go | 6 ++- server/channelserver/handlers_house.go | 47 +++++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/network/mhfpacket/msg_mhf_enumerate_house.go b/network/mhfpacket/msg_mhf_enumerate_house.go index 9bd1f30ef..da6a25de7 100644 --- a/network/mhfpacket/msg_mhf_enumerate_house.go +++ b/network/mhfpacket/msg_mhf_enumerate_house.go @@ -29,8 +29,10 @@ func (m *MsgMhfEnumerateHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli m.CharID = bf.ReadUint32() m.Method = bf.ReadUint8() m.Unk = bf.ReadUint16() - _ = bf.ReadUint8() // len - m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + lenName := bf.ReadUint8() + if lenName > 0 { + m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + } return nil } diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 5236f197d..03d961ef3 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -140,18 +140,55 @@ func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfLoadHouse) bf := byteframe.NewByteFrame() + + var state uint8 + var password string + s.server.db.QueryRow(`SELECT COALESCE(house_state, 2) as house_state, COALESCE(house_password, '') as house_password FROM user_binary WHERE id=$1 + `, pkt.CharID).Scan(&state, &password) + if pkt.Destination != 9 && len(pkt.Password) > 0 && pkt.CheckPass { - var password string - err := s.server.db.Get(&password, `SELECT house_password FROM user_binary WHERE id=$1`, pkt.CharID) - if err != nil { - panic(err) - } if pkt.Password != password { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } } + if pkt.Destination != 9 && state > 2 { + allowed := false + + // Friends list verification + if state == 3 || state == 5 { + var friendsList string + s.server.db.QueryRow(`SELECT friends FROM characters WHERE id=$1`, pkt.CharID).Scan(&friendsList) + cids := stringsupport.CSVElems(friendsList) + for _, cid := range cids { + if uint32(cid) == s.charID { + allowed = true + break + } + } + } + + // Guild verification + if state > 3 { + ownGuild, err := GetGuildInfoByCharacterId(s, s.charID) + isApplicant, _ := ownGuild.HasApplicationForCharID(s, s.charID) + if err == nil && ownGuild != nil { + othersGuild, err := GetGuildInfoByCharacterId(s, pkt.CharID) + if err == nil && othersGuild != nil { + if othersGuild.ID == ownGuild.ID && !isApplicant { + allowed = true + } + } + } + } + + if !allowed { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return + } + } + var houseTier, houseData, houseFurniture, bookshelf, gallery, tore, garden []byte s.server.db.QueryRow(`SELECT house_tier, house_data, house_furniture, bookshelf, gallery, tore, garden FROM user_binary WHERE id=$1 `, pkt.CharID).Scan(&houseTier, &houseData, &houseFurniture, &bookshelf, &gallery, &tore, &garden) From 616d58e70e3e8025bc97ad1c373371a999906846 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 5 Sep 2022 15:29:24 +1000 Subject: [PATCH 12/26] dump other savedata types --- server/channelserver/handlers_data.go | 1 + server/channelserver/handlers_mercenary.go | 1 + server/channelserver/handlers_quest.go | 1 + 3 files changed, 3 insertions(+) diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index 57a2641f9..ae0450419 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -264,6 +264,7 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveScenarioData) + dumpSaveData(s, pkt.RawDataPayload, "scenario") _, err := s.server.db.Exec("UPDATE characters SET scenariodata = $1 WHERE id = $2", pkt.RawDataPayload, s.charID) if err != nil { s.logger.Fatal("Failed to update scenario data in db", zap.Error(err)) diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index 5e9e7b62e..bdeb924de 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -163,6 +163,7 @@ func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveMercenary) + dumpSaveData(s, pkt.MercData, "mercenary") if len(pkt.MercData) > 0 { s.server.db.Exec("UPDATE characters SET savemercenary=$1 WHERE id=$2", pkt.MercData, s.charID) } diff --git a/server/channelserver/handlers_quest.go b/server/channelserver/handlers_quest.go index 86a0003b9..7ee552232 100644 --- a/server/channelserver/handlers_quest.go +++ b/server/channelserver/handlers_quest.go @@ -54,6 +54,7 @@ func handleMsgMhfLoadFavoriteQuest(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveFavoriteQuest(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveFavoriteQuest) + dumpSaveData(s, pkt.Data, "favquest") s.server.db.Exec("UPDATE characters SET savefavoritequest=$1 WHERE id=$2", pkt.Data, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } From c60385e61f9052aea37ec421999b106e0c2cfe79 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 5 Sep 2022 15:36:01 +1000 Subject: [PATCH 13/26] dump other savedata types --- server/channelserver/handlers.go | 1 + server/channelserver/handlers_plate.go | 6 +++--- server/channelserver/handlers_rengoku.go | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index e5476ca5f..cf7aecba9 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -1772,6 +1772,7 @@ func handleMsgMhfGetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSetEnhancedMinidata) + dumpSaveData(s, pkt.RawDataPayload, "minidata") _, err := s.server.db.Exec("UPDATE characters SET minidata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID) if err != nil { s.logger.Fatal("Failed to update minidata in db", zap.Error(err)) diff --git a/server/channelserver/handlers_plate.go b/server/channelserver/handlers_plate.go index 72121ef13..b1bfea5d2 100644 --- a/server/channelserver/handlers_plate.go +++ b/server/channelserver/handlers_plate.go @@ -25,7 +25,7 @@ func handleMsgMhfLoadPlateData(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSavePlateData(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSavePlateData) - dumpSaveData(s, pkt.RawDataPayload, "_platedata") + dumpSaveData(s, pkt.RawDataPayload, "platedata") if pkt.IsDataDiff { var data []byte @@ -90,7 +90,7 @@ func handleMsgMhfLoadPlateBox(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSavePlateBox(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSavePlateBox) - dumpSaveData(s, pkt.RawDataPayload, "_platebox") + dumpSaveData(s, pkt.RawDataPayload, "platebox") if pkt.IsDataDiff { var data []byte @@ -156,7 +156,7 @@ func handleMsgMhfLoadPlateMyset(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSavePlateMyset(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSavePlateMyset) // looks to always return the full thing, simply update database, no extra processing - + dumpSaveData(s, pkt.RawDataPayload, "platemyset") _, err := s.server.db.Exec("UPDATE characters SET platemyset=$1 WHERE id=$2", pkt.RawDataPayload, s.charID) if err != nil { s.logger.Fatal("Failed to update platemyset savedata in db", zap.Error(err)) diff --git a/server/channelserver/handlers_rengoku.go b/server/channelserver/handlers_rengoku.go index 6fdce7c22..9bfe464cf 100644 --- a/server/channelserver/handlers_rengoku.go +++ b/server/channelserver/handlers_rengoku.go @@ -15,6 +15,7 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) { // saved every floor on road, holds values such as floors progressed, points etc. // can be safely handled by the client pkt := p.(*mhfpacket.MsgMhfSaveRengokuData) + dumpSaveData(s, pkt.RawDataPayload, "rengoku") _, err := s.server.db.Exec("UPDATE characters SET rengokudata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID) if err != nil { s.logger.Fatal("Failed to update rengokudata savedata in db", zap.Error(err)) From 88815c0a05b84ed3a792dce7c7c2ef1c052b8a1e Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 5 Sep 2022 15:42:16 +1000 Subject: [PATCH 14/26] dump other savedata types --- server/channelserver/handlers.go | 1 + server/channelserver/handlers_house.go | 1 + 2 files changed, 2 insertions(+) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index cf7aecba9..2e348c138 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -1743,6 +1743,7 @@ func handleMsgMhfUpdateEquipSkinHist(s *Session, p mhfpacket.MHFPacket) { byteInd := (bit / 8) bitInByte := bit % 8 data[startByte+byteInd] |= bits.Reverse8((1 << uint(bitInByte))) + dumpSaveData(s, data, "skinhist") _, err = s.server.db.Exec("UPDATE characters SET skin_hist=$1 WHERE id=$2", data, s.charID) if err != nil { panic(err) diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 03d961ef3..b3aadf372 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -310,6 +310,7 @@ func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) { } loadData[1] = savedSets // update set count } + dumpSaveData(s, loadData, "decomyset") _, err := s.server.db.Exec("UPDATE characters SET decomyset=$1 WHERE id=$2", loadData, s.charID) if err != nil { s.logger.Fatal("Failed to update decomyset savedata in db", zap.Error(err)) From 580bfb12feeec68c32dccff6e011123c975639fd Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 8 Sep 2022 15:15:23 +1000 Subject: [PATCH 15/26] decode dsgn --- server/signserver/dsgn_resp.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index a169d93a1..091abc687 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -112,14 +112,17 @@ func (s *Session) makeSignInResp(uid int) []byte { bf.WriteUint32(s.server.getLastCID(uid)) bf.WriteUint32(s.server.getUserRights(uid)) ps.Uint16(bf, "", false) // filters - bf.WriteUint32(0xCA104E20) - ps.Uint16(bf, "", false) // encryption + bf.WriteUint16(0xCA10) + bf.WriteUint16(0x4E20) + ps.Uint16(bf, "", false) // unk key bf.WriteUint8(0x00) - bf.WriteUint32(0xCA110001) - bf.WriteUint32(0x4E200000) + bf.WriteUint16(0xCA11) + bf.WriteUint16(0x0001) + bf.WriteUint16(0x4E20) + ps.Uint16(bf, "", false) // unk ipv4 bf.WriteUint32(uint32(returnExpiry.Unix())) bf.WriteUint32(0x00000000) - bf.WriteUint32(0x0A5197DF) + bf.WriteUint32(0x0A5197DF) // unk id mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent alt := s.server.erupeConfig.DevModeOptions.MezFesAlt From d81e55cab44b2de6f32741bab9187fa339b0e53d Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 8 Sep 2022 15:16:44 +1000 Subject: [PATCH 16/26] stub GetGemInfo --- network/mhfpacket/msg_mhf_get_gem_info.go | 19 +++++++++++++------ server/channelserver/handlers_tower.go | 5 ++++- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/network/mhfpacket/msg_mhf_get_gem_info.go b/network/mhfpacket/msg_mhf_get_gem_info.go index 516aea6be..36eb7948e 100644 --- a/network/mhfpacket/msg_mhf_get_gem_info.go +++ b/network/mhfpacket/msg_mhf_get_gem_info.go @@ -1,15 +1,19 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfGetGemInfo represents the MSG_MHF_GET_GEM_INFO -type MsgMhfGetGemInfo struct{} +type MsgMhfGetGemInfo struct { + AckHandle uint32 + Unk uint32 + Unk1 []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetGemInfo) Opcode() network.PacketID { @@ -18,7 +22,10 @@ func (m *MsgMhfGetGemInfo) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetGemInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk = bf.ReadUint32() + m.Unk1 = bf.ReadBytes(24) + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers_tower.go b/server/channelserver/handlers_tower.go index 49029753f..10c3070f9 100644 --- a/server/channelserver/handlers_tower.go +++ b/server/channelserver/handlers_tower.go @@ -58,6 +58,9 @@ func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } -func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetGemInfo) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 8)) +} func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {} From 128f375cd2e3ae13ad2e92ddce3d67bde3e44b5a Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 10 Sep 2022 22:43:23 +1000 Subject: [PATCH 17/26] dump savedata without name --- network/mhfpacket/msg_mhf_get_tower_info.go | 10 +++++----- server/channelserver/handlers_data.go | 4 ++-- server/channelserver/handlers_tower.go | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/network/mhfpacket/msg_mhf_get_tower_info.go b/network/mhfpacket/msg_mhf_get_tower_info.go index 37ac8417b..a0b686485 100644 --- a/network/mhfpacket/msg_mhf_get_tower_info.go +++ b/network/mhfpacket/msg_mhf_get_tower_info.go @@ -1,11 +1,11 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // The server sends different responses based on these values. @@ -13,7 +13,7 @@ const ( TowerInfoTypeUnk0 = iota TowerInfoTypeTowerRankPoint TowerInfoTypeGetOwnTowerSkill - TowerInfoTypeUnk3 + TowerInfoTypeGetOwnTowerLevelV3 TowerInfoTypeTowerTouhaHistory TowerInfoTypeUnk5 ) diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index ae0450419..5d179e50c 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -219,8 +219,8 @@ func dumpSaveData(s *Session, data []byte, suffix string) { if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled { return } else { - dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name)) - path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name), fmt.Sprintf("%d_%s_%s.bin", s.charID, s.Name, suffix)) + dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID)) + path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID), fmt.Sprintf("%d_%s.bin", s.charID, suffix)) if _, err := os.Stat(dir); os.IsNotExist(err) { os.Mkdir(dir, os.ModeDir) diff --git a/server/channelserver/handlers_tower.go b/server/channelserver/handlers_tower.go index 10c3070f9..24cc64033 100644 --- a/server/channelserver/handlers_tower.go +++ b/server/channelserver/handlers_tower.go @@ -13,7 +13,7 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) { type: 1 == TOWER_RANK_POINT, 2 == GET_OWN_TOWER_SKILL - 3 == ? + 3 == GET_OWN_TOWER_LEVEL_V3 4 == TOWER_TOUHA_HISTORY 5 = ? @@ -39,8 +39,8 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) { case mhfpacket.TowerInfoTypeGetOwnTowerSkill: //data, err = hex.DecodeString("0A218EAD000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") data, err = hex.DecodeString("0A218EAD0000000000000000000000010000001C0000000500050000000000020000000000000000000000000000000000030003000000000003000500050000000300030003000300030003000200030001000300020002000300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") - case mhfpacket.TowerInfoTypeUnk3: - panic("No known response values for TowerInfoTypeUnk3") + case mhfpacket.TowerInfoTypeGetOwnTowerLevelV3: + panic("No known response values for GetOwnTowerLevelV3") case mhfpacket.TowerInfoTypeTowerTouhaHistory: data, err = hex.DecodeString("0A218EAD0000000000000000000000010000000000000000000000000000000000000000") case mhfpacket.TowerInfoTypeUnk5: From a35dfa21b5ee2471432c178c5380485f9acb94de Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 10 Sep 2022 23:15:08 +1000 Subject: [PATCH 18/26] create default response on rengoku ranking --- server/channelserver/handlers_rengoku.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_rengoku.go b/server/channelserver/handlers_rengoku.go index 9bfe464cf..d9f58df57 100644 --- a/server/channelserver/handlers_rengoku.go +++ b/server/channelserver/handlers_rengoku.go @@ -271,8 +271,20 @@ func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) { bf.WriteBytes(make([]byte, 11)) } } - bf.WriteUint8(uint8(i) - 1) - bf.WriteBytes(scoreData.Data()) + if i == 1 { + bf.WriteUint32(1) + bf.WriteUint32(0) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + bf.WriteUint8(1) + bf.WriteUint32(1) + bf.WriteUint32(0) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } else { + bf.WriteUint8(uint8(i) - 1) + bf.WriteBytes(scoreData.Data()) + } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } From e90e3da142c062892de3acc02d6a6afc78c6b678 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 30 Sep 2022 03:47:59 +1000 Subject: [PATCH 19/26] add KQF command --- config.json | 4 +++ server/channelserver/handlers_cast_binary.go | 31 ++++++++++++++++---- server/channelserver/handlers_character.go | 13 +++++++- server/channelserver/sys_session.go | 2 ++ 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/config.json b/config.json index ba952620b..98ea1c0ef 100644 --- a/config.json +++ b/config.json @@ -46,6 +46,10 @@ "name": "Reload", "enabled": true, "prefix": "!reload" + }, { + "name": "KeyQuest", + "enabled": false, + "prefix": "!kqf" } ], "database": { diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 6910a01ae..3791ea241 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -1,17 +1,17 @@ package channelserver import ( + "encoding/hex" + "erupe-ce/common/byteframe" + "erupe-ce/config" + "erupe-ce/network/binpacket" + "erupe-ce/network/mhfpacket" "fmt" "math" "math/rand" "strings" "time" - "erupe-ce/common/byteframe" - "erupe-ce/config" - "erupe-ce/network/binpacket" - "erupe-ce/network/mhfpacket" - "go.uber.org/zap" ) @@ -259,6 +259,27 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { } } + if strings.HasPrefix(chatMessage.Message, commands["KeyQuest"].Prefix) { + if commands["KeyQuest"].Enabled { + if strings.HasPrefix(chatMessage.Message, "!kqf get") { + sendServerChatMessage(s, fmt.Sprintf("KQF: %x", s.kqf)) + } else if strings.HasPrefix(chatMessage.Message, "!kqf set") { + var hexs string + n, numerr := fmt.Sscanf(chatMessage.Message, "!kqf set %s", &hexs) + if numerr != nil || n != 1 || len(hexs) != 16 { + sendServerChatMessage(s, "Error in command. Format: !kqf set xxxxxxxxxxxxxxxx") + } else { + hexd, _ := hex.DecodeString(hexs) + s.kqf = hexd + s.kqfOverride = true + sendServerChatMessage(s, "KQF set, please switch Land/World") + } + } + } else { + sendDisabledCommandMessage(s, commands["KeyQuest"]) + } + } + if strings.HasPrefix(chatMessage.Message, commands["Rights"].Prefix) { // Set account rights if commands["Rights"].Enabled { diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index 13459bda9..b3ad40fcd 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -22,6 +22,7 @@ const ( pointerWeaponID = 0x1F60A // +2 pointerHRP = 0x1FDF6 // +2 pointerGRP = 0x1FDFC // +4 + pointerKQF = 0x23D20 // +8 ) type CharacterSaveData struct { @@ -41,6 +42,7 @@ type CharacterSaveData struct { WeaponID uint16 HRP uint16 GR uint16 + KQF []byte compSave []byte decompSave []byte @@ -80,9 +82,16 @@ func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) func (save *CharacterSaveData) Save(s *Session) { // We need to update the save data byte array before we save it back to the DB - save.updateSaveDataWithStruct() save.updateStructWithSaveData() + if !s.kqfOverride { + s.kqf = save.KQF + } else { + save.KQF = s.kqf + } + + save.updateSaveDataWithStruct() + err := save.Compress() if err != nil { s.logger.Error("Failed to compress savedata", zap.Error(err)) @@ -122,6 +131,7 @@ func (save *CharacterSaveData) updateSaveDataWithStruct() { rpBytes := make([]byte, 2) binary.LittleEndian.PutUint16(rpBytes, save.RP) copy(save.decompSave[pointerRP:pointerRP+2], rpBytes) + copy(save.decompSave[pointerKQF:pointerKQF+8], save.KQF) } // This will update the save struct with the values stored in the character save @@ -142,6 +152,7 @@ func (save *CharacterSaveData) updateStructWithSaveData() { save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[pointerWeaponID : pointerWeaponID+2]) save.HRP = binary.LittleEndian.Uint16(save.decompSave[pointerHRP : pointerHRP+2]) save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[pointerGRP : pointerGRP+4])) + save.KQF = save.decompSave[pointerKQF : pointerKQF+8] } func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index 20fc432f8..ec122a521 100644 --- a/server/channelserver/sys_session.go +++ b/server/channelserver/sys_session.go @@ -39,6 +39,8 @@ type Session struct { sessionStart int64 rights uint32 token string + kqf []byte + kqfOverride bool semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet. From e0c658363c1db1fe7e8361ecc39169877ae7d4b9 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 30 Sep 2022 17:40:23 +1000 Subject: [PATCH 20/26] fix struct save data --- server/channelserver/handlers_character.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index b3ad40fcd..78a0b4754 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -77,13 +77,12 @@ func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) return nil, err } + saveData.updateStructWithSaveData() + return saveData, nil } func (save *CharacterSaveData) Save(s *Session) { - // We need to update the save data byte array before we save it back to the DB - save.updateStructWithSaveData() - if !s.kqfOverride { s.kqf = save.KQF } else { From ca9f2de457440b17943765c64eb399bb81a2a02f Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 30 Sep 2022 17:59:56 +1000 Subject: [PATCH 21/26] fix return not expiring --- server/signserver/dsgn_resp.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 091abc687..cbad6405c 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -120,7 +120,12 @@ func (s *Session) makeSignInResp(uid int) []byte { bf.WriteUint16(0x0001) bf.WriteUint16(0x4E20) ps.Uint16(bf, "", false) // unk ipv4 - bf.WriteUint32(uint32(returnExpiry.Unix())) + if returnExpiry.Before(time.Now()) { + // Hack to make Return work while having a non-adjusted expiry + bf.WriteUint32(0) + } else { + bf.WriteUint32(uint32(returnExpiry.Unix())) + } bf.WriteUint32(0x00000000) bf.WriteUint32(0x0A5197DF) // unk id From 40a86364d9a91cceb524b1479f13ac7d04c65689 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 30 Sep 2022 23:05:10 +1000 Subject: [PATCH 22/26] handle alliance enumeration --- network/mhfpacket/msg_mhf_info_joint.go | 19 ++- server/channelserver/handlers_guild.go | 110 +++++++++++++----- .../channelserver/handlers_guild_alliance.go | 51 +++++++- 3 files changed, 146 insertions(+), 34 deletions(-) diff --git a/network/mhfpacket/msg_mhf_info_joint.go b/network/mhfpacket/msg_mhf_info_joint.go index 6426b9211..17e468c7c 100644 --- a/network/mhfpacket/msg_mhf_info_joint.go +++ b/network/mhfpacket/msg_mhf_info_joint.go @@ -1,15 +1,19 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfInfoJoint represents the MSG_MHF_INFO_JOINT -type MsgMhfInfoJoint struct{} +type MsgMhfInfoJoint struct { + AckHandle uint32 + AllianceID uint32 + Unk uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfInfoJoint) Opcode() network.PacketID { @@ -18,7 +22,10 @@ func (m *MsgMhfInfoJoint) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfInfoJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.AllianceID = bf.ReadUint32() + m.Unk = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index f7646926c..5f56baf6d 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1065,6 +1065,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuild) var guilds []*Guild + var alliances []*GuildAlliance var rows *sqlx.Rows var err error bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) @@ -1159,41 +1160,98 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { guilds = append(guilds, guild) } } - case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME: - // - case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME: - // - case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_ID: - // - case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS: - // - case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION: - // - default: - panic(fmt.Sprintf("no handler for guild search type '%d'", pkt.Type)) } - if err != nil || guilds == nil { + if pkt.Type > 8 { + var tempAlliances []*GuildAlliance + rows, err = s.server.db.Queryx(allianceInfoSelectQuery) + if err == nil { + for rows.Next() { + alliance, _ := buildAllianceObjectFromDbResult(rows, err, s) + tempAlliances = append(tempAlliances, alliance) + } + } + switch pkt.Type { + case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME: + bf.ReadBytes(10) + searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + for _, alliance := range tempAlliances { + if strings.Contains(alliance.Name, searchTerm) { + alliances = append(alliances, alliance) + } + } + case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME: + bf.ReadBytes(10) + searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + for _, alliance := range tempAlliances { + if strings.Contains(alliance.ParentGuild.LeaderName, searchTerm) { + alliances = append(alliances, alliance) + } + } + case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_ID: + bf.ReadBytes(2) + ID := bf.ReadUint32() + for _, alliance := range tempAlliances { + if alliance.ParentGuild.LeaderCharID == ID { + alliances = append(alliances, alliance) + } + } + case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS: + sort.Slice(tempAlliances, func(i, j int) bool { + return tempAlliances[i].TotalMembers < tempAlliances[j].TotalMembers + }) + alliances = tempAlliances + case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION: + sort.Slice(tempAlliances, func(i, j int) bool { + return tempAlliances[i].CreatedAt.Unix() < tempAlliances[j].CreatedAt.Unix() + }) + alliances = tempAlliances + } + } + + if err != nil || (guilds == nil && alliances == nil) { stubEnumerateNoResults(s, pkt.AckHandle) return } bf = byteframe.NewByteFrame() - bf.WriteUint16(uint16(len(guilds))) - bf.WriteUint8(0x01) // Unk - - for _, guild := range guilds { - bf.WriteUint32(guild.ID) - bf.WriteUint32(guild.LeaderCharID) - bf.WriteUint16(guild.MemberCount) - bf.WriteUint16(0x0000) // Unk - bf.WriteUint16(guild.Rank) // OR guilds in alliance - bf.WriteUint32(uint32(guild.CreatedAt.Unix())) - ps.Uint8(bf, guild.Name, true) - ps.Uint8(bf, guild.LeaderName, true) + if pkt.Type > 8 { + bf.WriteUint16(uint16(len(alliances))) + for _, alliance := range alliances { + bf.WriteUint8(0x00) // Unk + bf.WriteUint32(alliance.ID) + bf.WriteUint32(alliance.ParentGuild.LeaderCharID) + bf.WriteUint16(alliance.TotalMembers) + bf.WriteUint16(0x0000) + if alliance.SubGuild1ID == 0 && alliance.SubGuild2ID == 0 { + bf.WriteUint16(1) + } else if alliance.SubGuild1ID > 0 && alliance.SubGuild2ID == 0 || alliance.SubGuild1ID == 0 && alliance.SubGuild2ID > 0 { + bf.WriteUint16(2) + } else { + bf.WriteUint16(3) + } + bf.WriteUint32(uint32(alliance.CreatedAt.Unix())) + ps.Uint8(bf, alliance.Name, true) + ps.Uint8(bf, alliance.ParentGuild.LeaderName, true) + bf.WriteUint8(0x01) // Unk + bf.WriteBool(true) // TODO: Enable GuildAlliance applications + } + } else { bf.WriteUint8(0x01) // Unk - bf.WriteBool(!guild.Recruiting) + bf.WriteUint16(uint16(len(guilds))) + for _, guild := range guilds { + bf.WriteUint32(guild.ID) + bf.WriteUint32(guild.LeaderCharID) + bf.WriteUint16(guild.MemberCount) + bf.WriteUint16(0x0000) // Unk + bf.WriteUint16(guild.Rank) // OR guilds in alliance + bf.WriteUint32(uint32(guild.CreatedAt.Unix())) + ps.Uint8(bf, guild.Name, true) + ps.Uint8(bf, guild.LeaderName, true) + bf.WriteUint8(0x01) // Unk + bf.WriteBool(!guild.Recruiting) + } } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) diff --git a/server/channelserver/handlers_guild_alliance.go b/server/channelserver/handlers_guild_alliance.go index 77f204adc..8f3a6313e 100644 --- a/server/channelserver/handlers_guild_alliance.go +++ b/server/channelserver/handlers_guild_alliance.go @@ -1,6 +1,8 @@ package channelserver import ( + "erupe-ce/common/byteframe" + ps "erupe-ce/common/pascalstring" "fmt" "time" @@ -149,9 +151,54 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) } default: - panic(fmt.Sprintf("Unhandled operate joint action '%d'", pkt.Action)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + panic(fmt.Sprintf("Unhandled operate joint action '%d'", pkt.Action)) } } -func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfInfoJoint) + bf := byteframe.NewByteFrame() + alliance, err := GetAllianceData(s, pkt.AllianceID) + if err != nil { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + } else { + bf.WriteUint32(alliance.ID) + bf.WriteUint32(uint32(alliance.CreatedAt.Unix())) + bf.WriteUint16(alliance.TotalMembers) + bf.WriteUint16(0x0000) // Unk + ps.Uint16(bf, alliance.Name, true) + if alliance.SubGuild1ID > 0 { + if alliance.SubGuild2ID > 0 { + bf.WriteUint8(3) + } else { + bf.WriteUint8(2) + } + } else { + bf.WriteUint8(1) + } + bf.WriteUint32(alliance.ParentGuildID) + bf.WriteUint32(alliance.ParentGuild.LeaderCharID) + bf.WriteUint16(alliance.ParentGuild.Rank) + bf.WriteUint16(alliance.ParentGuild.MemberCount) + ps.Uint16(bf, alliance.ParentGuild.Name, true) + ps.Uint16(bf, alliance.ParentGuild.LeaderName, true) + if alliance.SubGuild1ID > 0 { + bf.WriteUint32(alliance.SubGuild1ID) + bf.WriteUint32(alliance.SubGuild1.LeaderCharID) + bf.WriteUint16(alliance.SubGuild1.Rank) + bf.WriteUint16(alliance.SubGuild1.MemberCount) + ps.Uint16(bf, alliance.SubGuild1.Name, true) + ps.Uint16(bf, alliance.SubGuild1.LeaderName, true) + } + if alliance.SubGuild2ID > 0 { + bf.WriteUint32(alliance.SubGuild2ID) + bf.WriteUint32(alliance.SubGuild2.LeaderCharID) + bf.WriteUint16(alliance.SubGuild2.Rank) + bf.WriteUint16(alliance.SubGuild2.MemberCount) + ps.Uint16(bf, alliance.SubGuild2.Name, true) + ps.Uint16(bf, alliance.SubGuild2.LeaderName, true) + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + } +} From 34f0eb83158c3d97e14fb5ce0784554ff6a25021 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 30 Sep 2022 23:54:16 +1000 Subject: [PATCH 23/26] handle leaving alliance --- server/channelserver/handlers_guild_alliance.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_guild_alliance.go b/server/channelserver/handlers_guild_alliance.go index 8f3a6313e..1d0960780 100644 --- a/server/channelserver/handlers_guild_alliance.go +++ b/server/channelserver/handlers_guild_alliance.go @@ -141,8 +141,15 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) { } case mhfpacket.OPERATE_JOINT_LEAVE: if guild.LeaderCharID == s.charID { - // delete alliance application - // or leave alliance + if guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 { + s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID) + } else if guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 { + s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID) + } else { + s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID) + } + // TODO: Handle deleting Alliance applications + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } else { s.logger.Warn( "Non-owner of guild attempted alliance leave", From 665c2dd32fc7b5f9f5c912132064f9fce2e61e00 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 1 Oct 2022 00:10:00 +1000 Subject: [PATCH 24/26] handle alliance kicking --- network/mhfpacket/msg_mhf_operate_joint.go | 36 +++++++++---------- .../channelserver/handlers_guild_alliance.go | 20 +++++++++++ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/network/mhfpacket/msg_mhf_operate_joint.go b/network/mhfpacket/msg_mhf_operate_joint.go index 9733a2278..1fa360d01 100644 --- a/network/mhfpacket/msg_mhf_operate_joint.go +++ b/network/mhfpacket/msg_mhf_operate_joint.go @@ -1,28 +1,28 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) type OperateJointAction uint8 const ( - OPERATE_JOINT_DISBAND = 0x01 - OPERATE_JOINT_LEAVE = 0x03 - OPERATE_JOINT_KICK = 0x09 + OPERATE_JOINT_DISBAND = 0x01 + OPERATE_JOINT_LEAVE = 0x03 + OPERATE_JOINT_KICK = 0x09 ) // MsgMhfOperateJoint represents the MSG_MHF_OPERATE_JOINT type MsgMhfOperateJoint struct { - AckHandle uint32 - AllianceID uint32 - GuildID uint32 - Action OperateJointAction - UnkData []byte + AckHandle uint32 + AllianceID uint32 + GuildID uint32 + Action OperateJointAction + UnkData *byteframe.ByteFrame } // Opcode returns the ID associated with this packet type. @@ -32,13 +32,13 @@ func (m *MsgMhfOperateJoint) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfOperateJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.AllianceID = bf.ReadUint32() - m.GuildID = bf.ReadUint32() - m.Action = OperateJointAction(bf.ReadUint8()) - m.UnkData = bf.DataFromCurrent() - bf.Seek(int64(len(bf.Data()) - 2), 0) - return nil + m.AckHandle = bf.ReadUint32() + m.AllianceID = bf.ReadUint32() + m.GuildID = bf.ReadUint32() + m.Action = OperateJointAction(bf.ReadUint8()) + m.UnkData = byteframe.NewByteFrameFromBytes(bf.DataFromCurrent()) + bf.Seek(int64(len(bf.Data())-2), 0) + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers_guild_alliance.go b/server/channelserver/handlers_guild_alliance.go index 1d0960780..7e2a9a2ac 100644 --- a/server/channelserver/handlers_guild_alliance.go +++ b/server/channelserver/handlers_guild_alliance.go @@ -157,6 +157,26 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) { ) doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) } + case mhfpacket.OPERATE_JOINT_KICK: + if alliance.ParentGuild.LeaderCharID == s.charID { + _ = pkt.UnkData.ReadUint8() + kickedGuildID := pkt.UnkData.ReadUint32() + if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 { + s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID) + } else if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 { + s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID) + } else { + s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID) + } + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + } else { + s.logger.Warn( + "Non-owner of alliance attempted kick", + zap.Uint32("CharID", s.charID), + zap.Uint32("AllyID", alliance.ID), + ) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + } default: doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) panic(fmt.Sprintf("Unhandled operate joint action '%d'", pkt.Action)) From 8f9648d9d8af169151375b49c8a74e7e5e687def Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 1 Oct 2022 01:26:00 +1000 Subject: [PATCH 25/26] fix alliance enumeration bugs --- server/channelserver/handlers_guild.go | 28 +++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 5f56baf6d..53df91adb 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1197,14 +1197,28 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS: - sort.Slice(tempAlliances, func(i, j int) bool { - return tempAlliances[i].TotalMembers < tempAlliances[j].TotalMembers - }) + sorting := bf.ReadBool() + if sorting { + sort.Slice(tempAlliances, func(i, j int) bool { + return tempAlliances[i].TotalMembers > tempAlliances[j].TotalMembers + }) + } else { + sort.Slice(tempAlliances, func(i, j int) bool { + return tempAlliances[i].TotalMembers < tempAlliances[j].TotalMembers + }) + } alliances = tempAlliances case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION: - sort.Slice(tempAlliances, func(i, j int) bool { - return tempAlliances[i].CreatedAt.Unix() < tempAlliances[j].CreatedAt.Unix() - }) + sorting := bf.ReadBool() + if sorting { + sort.Slice(tempAlliances, func(i, j int) bool { + return tempAlliances[i].CreatedAt.Unix() > tempAlliances[j].CreatedAt.Unix() + }) + } else { + sort.Slice(tempAlliances, func(i, j int) bool { + return tempAlliances[i].CreatedAt.Unix() < tempAlliances[j].CreatedAt.Unix() + }) + } alliances = tempAlliances } } @@ -1218,8 +1232,8 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { if pkt.Type > 8 { bf.WriteUint16(uint16(len(alliances))) + bf.WriteUint8(0x00) // Unk for _, alliance := range alliances { - bf.WriteUint8(0x00) // Unk bf.WriteUint32(alliance.ID) bf.WriteUint32(alliance.ParentGuild.LeaderCharID) bf.WriteUint16(alliance.TotalMembers) From a5b47310d134d7d027712f7c83f778e05c91d695 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 4 Oct 2022 20:23:13 +1100 Subject: [PATCH 26/26] clean up guild queries --- server/channelserver/handlers_guild.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 53df91adb..8891f23ee 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -164,7 +164,7 @@ func (guild *Guild) Save(s *Session) error { func (guild *Guild) CreateApplication(s *Session, charID uint32, applicationType GuildApplicationType, transaction *sql.Tx) error { - sql := ` + query := ` INSERT INTO guild_applications (guild_id, character_id, actor_id, application_type) VALUES ($1, $2, $3, $4) ` @@ -172,9 +172,9 @@ func (guild *Guild) CreateApplication(s *Session, charID uint32, applicationType var err error if transaction == nil { - _, err = s.server.db.Exec(sql, guild.ID, charID, s.charID, applicationType) + _, err = s.server.db.Exec(query, guild.ID, charID, s.charID, applicationType) } else { - _, err = transaction.Exec(sql, guild.ID, charID, s.charID, applicationType) + _, err = transaction.Exec(query, guild.ID, charID, s.charID, applicationType) } if err != nil { @@ -222,7 +222,7 @@ func (guild *Guild) Disband(s *Session) error { return err } - _, err = transaction.Exec("UPDATE guild_alliances SET sub1_id=NULL WHERE sub1_id=$1", guild.ID) + _, err = transaction.Exec("UPDATE guild_alliances SET sub1_id=sub2_id, sub2_id=NULL WHERE sub1_id=$1", guild.ID) if err != nil { s.logger.Error("failed to remove guild from alliance", zap.Error(err), zap.Uint32("guildID", guild.ID))