From 4826882bcda56586e4b7374128371365be65ccda Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 19 Jul 2023 21:42:54 +1000 Subject: [PATCH 01/22] correctly parse SexChanger --- network/mhfpacket/msg_mhf_sex_changer.go | 14 ++++++++++---- server/channelserver/handlers_character.go | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/network/mhfpacket/msg_mhf_sex_changer.go b/network/mhfpacket/msg_mhf_sex_changer.go index 74186d84b..a8754feca 100644 --- a/network/mhfpacket/msg_mhf_sex_changer.go +++ b/network/mhfpacket/msg_mhf_sex_changer.go @@ -1,17 +1,20 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfSexChanger represents the MSG_MHF_SEX_CHANGER type MsgMhfSexChanger struct { AckHandle uint32 Gender uint8 + Unk0 uint8 + Unk1 uint8 + Unk2 uint8 } // Opcode returns the ID associated with this packet type. @@ -23,6 +26,9 @@ func (m *MsgMhfSexChanger) Opcode() network.PacketID { func (m *MsgMhfSexChanger) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Gender = bf.ReadUint8() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint8() + m.Unk2 = bf.ReadUint8() return nil } diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index 7a4c164fd..9541e1d30 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -203,5 +203,5 @@ func (save *CharacterSaveData) updateStructWithSaveData() { func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSexChanger) - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } From cd189e7ca3a8188f886de6f77ef6021426119007 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 19 Jul 2023 21:43:41 +1000 Subject: [PATCH 02/22] parse SetCaAchievementHist --- .../msg_mhf_set_ca_achievement_hist.go | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/network/mhfpacket/msg_mhf_set_ca_achievement_hist.go b/network/mhfpacket/msg_mhf_set_ca_achievement_hist.go index a04d6b89f..0b4fad343 100644 --- a/network/mhfpacket/msg_mhf_set_ca_achievement_hist.go +++ b/network/mhfpacket/msg_mhf_set_ca_achievement_hist.go @@ -1,18 +1,24 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) -// MsgMhfSetCaAchievementHist represents the MSG_MHF_SET_CA_ACHIEVEMENT_HIST -type MsgMhfSetCaAchievementHist struct{ - AckHandle uint32 +type CaAchievementHist struct { Unk0 uint32 - Unk1 uint32 + Unk1 uint8 +} + +// MsgMhfSetCaAchievementHist represents the MSG_MHF_SET_CA_ACHIEVEMENT_HIST +type MsgMhfSetCaAchievementHist struct { + AckHandle uint32 + Unk0 uint16 + Unk1 uint8 + Unk2 []CaAchievementHist } // Opcode returns the ID associated with this packet type. @@ -23,8 +29,14 @@ func (m *MsgMhfSetCaAchievementHist) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfSetCaAchievementHist) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() - m.Unk1 = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint8() + for i := 0; i < int(m.Unk1); i++ { + var temp CaAchievementHist + temp.Unk0 = bf.ReadUint32() + temp.Unk1 = bf.ReadUint8() + m.Unk2 = append(m.Unk2, temp) + } return nil } From 5c68dc1ddc59c5a7cee184a0ef825da524c7d26e Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 19 Jul 2023 21:44:00 +1000 Subject: [PATCH 03/22] parse ReadLastWeekBeatRanking --- .../msg_mhf_read_last_week_beat_ranking.go | 19 +++++++++++++------ server/channelserver/handlers_seibattle.go | 10 +++++++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/network/mhfpacket/msg_mhf_read_last_week_beat_ranking.go b/network/mhfpacket/msg_mhf_read_last_week_beat_ranking.go index 90635dd3f..b52195578 100644 --- a/network/mhfpacket/msg_mhf_read_last_week_beat_ranking.go +++ b/network/mhfpacket/msg_mhf_read_last_week_beat_ranking.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" ) // MsgMhfReadLastWeekBeatRanking represents the MSG_MHF_READ_LAST_WEEK_BEAT_RANKING -type MsgMhfReadLastWeekBeatRanking struct{} +type MsgMhfReadLastWeekBeatRanking struct { + AckHandle uint32 + Unk0 uint32 + Unk1 int32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfReadLastWeekBeatRanking) Opcode() network.PacketID { @@ -18,7 +22,10 @@ func (m *MsgMhfReadLastWeekBeatRanking) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfReadLastWeekBeatRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.Unk1 = bf.ReadInt32() + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers_seibattle.go b/server/channelserver/handlers_seibattle.go index 4df05c67d..caf5c19c9 100644 --- a/server/channelserver/handlers_seibattle.go +++ b/server/channelserver/handlers_seibattle.go @@ -68,7 +68,15 @@ func handleMsgMhfReadBeatLevel(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } -func handleMsgMhfReadLastWeekBeatRanking(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfReadLastWeekBeatRanking(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfReadLastWeekBeatRanking) + bf := byteframe.NewByteFrame() + bf.WriteInt32(0) + bf.WriteInt32(0) + bf.WriteInt32(0) + bf.WriteInt32(0) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} func handleMsgMhfUpdateBeatLevel(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateBeatLevel) From db67746c30fddb9a6740683b4d575d134b590fad Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 19 Jul 2023 21:44:24 +1000 Subject: [PATCH 04/22] parse & handle GetSenyuDailyCount --- .../mhfpacket/msg_mhf_get_senyu_daily_count.go | 15 +++++++++------ server/channelserver/handlers.go | 8 +++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/network/mhfpacket/msg_mhf_get_senyu_daily_count.go b/network/mhfpacket/msg_mhf_get_senyu_daily_count.go index c4d34089e..4bc9f41d2 100644 --- a/network/mhfpacket/msg_mhf_get_senyu_daily_count.go +++ b/network/mhfpacket/msg_mhf_get_senyu_daily_count.go @@ -1,15 +1,17 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfGetSenyuDailyCount represents the MSG_MHF_GET_SENYU_DAILY_COUNT -type MsgMhfGetSenyuDailyCount struct{} +type MsgMhfGetSenyuDailyCount struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetSenyuDailyCount) Opcode() network.PacketID { @@ -18,7 +20,8 @@ func (m *MsgMhfGetSenyuDailyCount) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetSenyuDailyCount) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index a8a37d181..9c25638b5 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -1630,7 +1630,13 @@ func handleMsgMhfDebugPostValue(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetRandFromTable(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetSenyuDailyCount) + bf := byteframe.NewByteFrame() + bf.WriteUint16(0) + bf.WriteUint16(0) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} type SeibattleTimetable struct { Start time.Time From d700b8bd910cf009a480eb75aa9338017459f1d5 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 19 Jul 2023 22:13:01 +1000 Subject: [PATCH 05/22] simplify Savedata pointers --- server/channelserver/handlers_character.go | 133 +++++++++++---------- 1 file changed, 69 insertions(+), 64 deletions(-) diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index 9541e1d30..0dac950ff 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -12,41 +12,29 @@ import ( "go.uber.org/zap" ) +type SavePointer int + const ( - pointerGender = 0x51 // +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 - pointerWeaponType = 0x1F715 // +1 - pointerWeaponID = 0x1F60A // +2 - pointerHRP = 0x1FDF6 // +2 - pointerGRP = 0x1FDFC // +4 - pointerKQF = 0x23D20 // +8 - - pointerRPZ = 0x1A076 - pointerHouseTierZ = 0x16ECC - pointerHouseDataZ = 0x17161 - pointerBookshelfDataZ = 0x195F8 - pointerGalleryDataZ = 0x19680 - pointerToreDataZ = 0x17014 - pointerGardenDataZ = 0x19FB8 - pointerWeaponTypeZ = 0x16A75 - pointerWeaponIDZ = 0x1696A - pointerHRPZ = 0x17156 - pointerGRPZ = 0x1715C - pointerKQFZ = 0x1B080 + pGender = iota // +1 + pRP // +2 + pHouseTier // +5 + pHouseData // +195 + pBookshelfData // +5576 + pGalleryData // +1748 + pToreData // +240 + pGardenData // +68 + pWeaponType // +1 + pWeaponID // +2 + pHRP // +2 + pGRP // +4 + pKQF // +8 ) type CharacterSaveData struct { CharID uint32 Name string IsNewCharacter bool + Pointers map[SavePointer]int Gender bool RP uint16 @@ -66,6 +54,39 @@ type CharacterSaveData struct { decompSave []byte } +func getPointers() map[SavePointer]int { + pointers := map[SavePointer]int{pGender: 81} + switch _config.ErupeConfig.RealClientMode { + case _config.ZZ: + pointers[pWeaponID] = 128522 + pointers[pWeaponType] = 128789 + pointers[pHouseTier] = 129900 + pointers[pToreData] = 130228 + pointers[pHRP] = 130550 + pointers[pGRP] = 130556 + pointers[pHouseData] = 130561 + pointers[pBookshelfData] = 139928 + pointers[pGalleryData] = 140064 + pointers[pGardenData] = 142424 + pointers[pRP] = 142614 + pointers[pKQF] = 146720 + case _config.Z2, _config.Z1, _config.G101, _config.G10: + pointers[pWeaponID] = 92522 + pointers[pWeaponType] = 92789 + pointers[pHouseTier] = 93900 + pointers[pToreData] = 94228 + pointers[pHRP] = 94550 + pointers[pGRP] = 94556 + pointers[pHouseData] = 94561 + pointers[pBookshelfData] = 103928 + pointers[pGalleryData] = 104064 + pointers[pGardenData] = 106424 + pointers[pRP] = 106614 + pointers[pKQF] = 110720 + } + return pointers +} + 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 { @@ -79,7 +100,9 @@ func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) return nil, err } - saveData := &CharacterSaveData{} + saveData := &CharacterSaveData{ + Pointers: getPointers(), + } 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)) @@ -148,54 +171,36 @@ func (save *CharacterSaveData) Decompress() error { func (save *CharacterSaveData) updateSaveDataWithStruct() { rpBytes := make([]byte, 2) binary.LittleEndian.PutUint16(rpBytes, save.RP) - if _config.ErupeConfig.RealClientMode == _config.ZZ { - copy(save.decompSave[pointerRP:pointerRP+2], rpBytes) - copy(save.decompSave[pointerKQF:pointerKQF+8], save.KQF) - } else if _config.ErupeConfig.RealClientMode >= _config.Z1 { - copy(save.decompSave[pointerRPZ:pointerRPZ+2], rpBytes) - copy(save.decompSave[pointerKQFZ:pointerKQFZ+8], save.KQF) + if _config.ErupeConfig.RealClientMode >= _config.G10 { + copy(save.decompSave[save.Pointers[pRP]:save.Pointers[pRP]+2], rpBytes) + copy(save.decompSave[save.Pointers[pKQF]:save.Pointers[pKQF]+8], save.KQF) } } // This will update the save struct with the values stored in the character save func (save *CharacterSaveData) updateStructWithSaveData() { save.Name = stringsupport.SJISToUTF8(bfutil.UpToNull(save.decompSave[88:100])) - if save.decompSave[pointerGender] == 1 { + if save.decompSave[save.Pointers[pGender]] == 1 { save.Gender = true } else { save.Gender = false } if !save.IsNewCharacter { - if _config.ErupeConfig.RealClientMode == _config.ZZ { - 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.WeaponType = save.decompSave[pointerWeaponType] - save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[pointerWeaponID : pointerWeaponID+2]) - save.HRP = binary.LittleEndian.Uint16(save.decompSave[pointerHRP : pointerHRP+2]) + if _config.ErupeConfig.RealClientMode >= _config.G10 { + save.RP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pRP] : save.Pointers[pRP]+2]) + save.HouseTier = save.decompSave[save.Pointers[pHouseTier] : save.Pointers[pHouseTier]+5] + save.HouseData = save.decompSave[save.Pointers[pHouseData] : save.Pointers[pHouseData]+195] + save.BookshelfData = save.decompSave[save.Pointers[pBookshelfData] : save.Pointers[pBookshelfData]+5576] + save.GalleryData = save.decompSave[save.Pointers[pGalleryData] : save.Pointers[pGalleryData]+1748] + save.ToreData = save.decompSave[save.Pointers[pToreData] : save.Pointers[pToreData]+240] + save.GardenData = save.decompSave[save.Pointers[pGardenData] : save.Pointers[pGardenData]+68] + save.WeaponType = save.decompSave[save.Pointers[pWeaponType]] + save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2]) + save.HRP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHRP] : save.Pointers[pHRP]+2]) if save.HRP == uint16(999) { - save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[pointerGRP : pointerGRP+4])) + save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pGRP] : save.Pointers[pGRP]+4])) } - save.KQF = save.decompSave[pointerKQF : pointerKQF+8] - } else if _config.ErupeConfig.RealClientMode >= _config.Z1 { - save.RP = binary.LittleEndian.Uint16(save.decompSave[pointerRPZ : pointerRPZ+2]) - save.HouseTier = save.decompSave[pointerHouseTierZ : pointerHouseTierZ+5] - save.HouseData = save.decompSave[pointerHouseDataZ : pointerHouseDataZ+195] - save.BookshelfData = save.decompSave[pointerBookshelfDataZ : pointerBookshelfDataZ+5576] - save.GalleryData = save.decompSave[pointerGalleryDataZ : pointerGalleryDataZ+1748] - save.ToreData = save.decompSave[pointerToreDataZ : pointerToreDataZ+240] - save.GardenData = save.decompSave[pointerGardenDataZ : pointerGardenDataZ+68] - save.WeaponType = save.decompSave[pointerWeaponTypeZ] - save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[pointerWeaponIDZ : pointerWeaponIDZ+2]) - save.HRP = binary.LittleEndian.Uint16(save.decompSave[pointerHRPZ : pointerHRPZ+2]) - if save.HRP == uint16(999) { - save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[pointerGRPZ : pointerGRPZ+4])) - } - save.KQF = save.decompSave[pointerKQFZ : pointerKQFZ+8] + save.KQF = save.decompSave[save.Pointers[pKQF] : save.Pointers[pKQF]+8] } } return From d3f2dcf7ed5255082c6c5af069e25ed45270f43e Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 19 Jul 2023 22:14:17 +1000 Subject: [PATCH 06/22] change default SaveDumps destination --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 1e09daddd..dcf800fdb 100644 --- a/config.json +++ b/config.json @@ -32,7 +32,7 @@ "EarthMonsterOverride": 0, "SaveDumps": { "Enabled": true, - "OutputDir": "savedata" + "OutputDir": "save-backups" } }, "GameplayOptions": { From 6a34fc9a76ded8e9491026685a1d5ef9f48734ef Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 20 Jul 2023 22:51:05 +1000 Subject: [PATCH 07/22] add KaijiEvent GameplayOption --- config.json | 1 + config/config.go | 1 + server/channelserver/handlers_quest.go | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/config.json b/config.json index dcf800fdb..6e69bb0a8 100644 --- a/config.json +++ b/config.json @@ -55,6 +55,7 @@ "MaterialMultiplier": 1.00, "ExtraCarves": 0, "DisableHunterNavi": false, + "EnableKaijiEvent": false, "EnableHiganjimaEvent": false, "EnableNierEvent": false, "DisableRoad": false diff --git a/config/config.go b/config/config.go index 7ef6c9bad..c02296b26 100644 --- a/config/config.go +++ b/config/config.go @@ -140,6 +140,7 @@ type GameplayOptions struct { MaterialMultiplier float32 // Adjusts the multiplier of Monster Materials rewarded for quest completion ExtraCarves uint16 // Grant n extra chances to carve ALL carcasses DisableHunterNavi bool // Disables the Hunter Navi + EnableKaijiEvent bool // Enables the Kaiji event in the Rasta Bar EnableHiganjimaEvent bool // Enables the Higanjima event in the Rasta Bar EnableNierEvent bool // Enables the Nier event in the Rasta Bar DisableRoad bool // Disables the Hunting Road diff --git a/server/channelserver/handlers_quest.go b/server/channelserver/handlers_quest.go index 8f40f7539..f295b37fb 100644 --- a/server/channelserver/handlers_quest.go +++ b/server/channelserver/handlers_quest.go @@ -186,7 +186,6 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { {ID: 1102, Value: 5}, {ID: 1103, Value: 2}, {ID: 1104, Value: 10}, - {ID: 1106, Value: 0}, {ID: 1145, Value: 200}, {ID: 1146, Value: 0}, // isTower_invisible {ID: 1147, Value: 0}, // isVenom_playable @@ -458,6 +457,12 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { tuneValues = append(tuneValues, tuneValue{1037, 1}) } + if s.server.erupeConfig.GameplayOptions.EnableKaijiEvent { + tuneValues = append(tuneValues, tuneValue{1106, 1}) + } else { + tuneValues = append(tuneValues, tuneValue{1106, 0}) + } + if s.server.erupeConfig.GameplayOptions.EnableHiganjimaEvent { tuneValues = append(tuneValues, tuneValue{1144, 1}) } else { From 2bcd3eb4f0ee2e3f2a859beba747da2158321d97 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 20 Jul 2023 22:51:26 +1000 Subject: [PATCH 08/22] fix EnumerateShop on G1 --- network/mhfpacket/msg_mhf_enumerate_shop.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/network/mhfpacket/msg_mhf_enumerate_shop.go b/network/mhfpacket/msg_mhf_enumerate_shop.go index 80ad5f432..153095db4 100644 --- a/network/mhfpacket/msg_mhf_enumerate_shop.go +++ b/network/mhfpacket/msg_mhf_enumerate_shop.go @@ -1,11 +1,12 @@ package mhfpacket -import ( - "errors" +import ( + "errors" + _config "erupe-ce/config" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfEnumerateShop represents the MSG_MHF_ENUMERATE_SHOP @@ -31,8 +32,10 @@ func (m *MsgMhfEnumerateShop) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clie m.ShopID = bf.ReadUint32() m.Unk2 = bf.ReadUint16() m.Unk3 = bf.ReadUint8() - m.Unk4 = bf.ReadUint8() - m.Unk5 = bf.ReadUint32() + if _config.ErupeConfig.RealClientMode >= _config.G2 { + m.Unk4 = bf.ReadUint8() + m.Unk5 = bf.ReadUint32() + } return nil } From 123434c374baab7eb786bda912a25e3806b012be Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 21 Jul 2023 20:18:14 +1000 Subject: [PATCH 09/22] add S6 Entrance compatibility --- server/entranceserver/make_resp.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/server/entranceserver/make_resp.go b/server/entranceserver/make_resp.go index 0e2466865..86256d02c 100644 --- a/server/entranceserver/make_resp.go +++ b/server/entranceserver/make_resp.go @@ -3,12 +3,11 @@ package entranceserver import ( "encoding/binary" "encoding/hex" + "erupe-ce/common/stringsupport" _config "erupe-ce/config" "fmt" "net" - "erupe-ce/common/stringsupport" - "erupe-ce/common/byteframe" "erupe-ce/server/channelserver" ) @@ -53,9 +52,15 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { bf.WriteUint16(uint16(len(si.Channels))) bf.WriteUint8(si.Type) bf.WriteUint8(season) - bf.WriteUint8(si.Recommended) + if s.erupeConfig.RealClientMode >= _config.G1 { + bf.WriteUint8(si.Recommended) + } - if s.erupeConfig.RealClientMode <= _config.GG { + if s.erupeConfig.RealClientMode <= _config.S6 { + combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...) + combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...) + bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false)) + } else if s.erupeConfig.RealClientMode <= _config.GG { combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...) combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...) bf.WriteUint8(uint8(len(combined))) From a66a32936f7d9a6b264ad5ff24291a2996be546a Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 21 Jul 2023 20:54:36 +1000 Subject: [PATCH 10/22] add S6 Savedata compatibility --- network/mhfpacket/msg_mhf_savedata.go | 13 ++++++++----- server/channelserver/handlers_character.go | 15 ++++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/network/mhfpacket/msg_mhf_savedata.go b/network/mhfpacket/msg_mhf_savedata.go index e0fca29c5..cf41416f3 100644 --- a/network/mhfpacket/msg_mhf_savedata.go +++ b/network/mhfpacket/msg_mhf_savedata.go @@ -1,11 +1,12 @@ package mhfpacket -import ( - "errors" +import ( + "errors" + _config "erupe-ce/config" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfSavedata represents the MSG_MHF_SAVEDATA @@ -29,7 +30,9 @@ func (m *MsgMhfSavedata) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientCon m.AllocMemSize = bf.ReadUint32() m.SaveType = bf.ReadUint8() m.Unk1 = bf.ReadUint32() - m.DataSize = bf.ReadUint32() + if _config.ErupeConfig.RealClientMode >= _config.G1 { + m.DataSize = bf.ReadUint32() + } if m.DataSize == 0 { // seems to be used when DataSize = 0 rather than on savetype? m.RawDataPayload = bf.ReadBytes(uint(m.AllocMemSize)) } else { diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index 0dac950ff..bdbd2f744 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -133,13 +133,18 @@ func (save *CharacterSaveData) Save(s *Session) { save.updateSaveDataWithStruct() - err := save.Compress() - if err != nil { - s.logger.Error("Failed to compress savedata", zap.Error(err)) - return + if _config.ErupeConfig.RealClientMode >= _config.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 } - _, err = s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hrp=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7 + _, err := s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hrp=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7 `, save.compSave, 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)) From 4c1393100035bdc547bebded2cdc488b5c8f0478 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 21 Jul 2023 21:10:11 +1000 Subject: [PATCH 11/22] fix TerminalLog on S6 --- network/mhfpacket/msg_sys_terminal_log.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/network/mhfpacket/msg_sys_terminal_log.go b/network/mhfpacket/msg_sys_terminal_log.go index 536d35f18..27cc5b1f6 100644 --- a/network/mhfpacket/msg_sys_terminal_log.go +++ b/network/mhfpacket/msg_sys_terminal_log.go @@ -2,6 +2,7 @@ package mhfpacket import ( "errors" + _config "erupe-ce/config" "erupe-ce/common/byteframe" "erupe-ce/network" @@ -37,12 +38,16 @@ func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client m.EntryCount = bf.ReadUint16() m.Unk0 = bf.ReadUint16() + values := 15 + if _config.ErupeConfig.RealClientMode <= _config.S6 { + values = 7 + } for i := 0; i < int(m.EntryCount); i++ { e := &TerminalLogEntry{} e.Index = bf.ReadUint32() e.Type1 = bf.ReadUint8() e.Type2 = bf.ReadUint8() - for j := 0; j < 15; j++ { + for j := 0; j < values; j++ { e.Data = append(e.Data, bf.ReadInt16()) } m.Entries = append(m.Entries, e) From b3876f771fb2b9a0b338e761683bc79f8489e569 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 21 Jul 2023 23:41:56 +1000 Subject: [PATCH 12/22] fix CreateAcquireSemaphore on S6 --- network/mhfpacket/msg_sys_create_acquire_semaphore.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/network/mhfpacket/msg_sys_create_acquire_semaphore.go b/network/mhfpacket/msg_sys_create_acquire_semaphore.go index 9f014ced7..65ec3580d 100644 --- a/network/mhfpacket/msg_sys_create_acquire_semaphore.go +++ b/network/mhfpacket/msg_sys_create_acquire_semaphore.go @@ -2,10 +2,9 @@ package mhfpacket import ( "errors" - "fmt" - - "erupe-ce/common/byteframe" "erupe-ce/common/bfutil" + "erupe-ce/common/byteframe" + _config "erupe-ce/config" "erupe-ce/network" "erupe-ce/network/clientctx" ) @@ -27,8 +26,9 @@ func (m *MsgSysCreateAcquireSemaphore) Opcode() network.PacketID { func (m *MsgSysCreateAcquireSemaphore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint16() - m.PlayerCount = bf.ReadUint8() - fmt.Printf("PLAYER COUNT :: %d", m.PlayerCount) + if _config.ErupeConfig.RealClientMode >= _config.G1 { + m.PlayerCount = bf.ReadUint8() + } SemaphoreIDLength := bf.ReadUint8() m.SemaphoreID = string(bfutil.UpToNull(bf.ReadBytes(uint(SemaphoreIDLength)))) return nil From 0b4dca5c453684f66bf876dcabcafbf8a0dd5293 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 22 Jul 2023 03:00:07 +1000 Subject: [PATCH 13/22] fix RP not being consistent between clients --- server/channelserver/handlers_guild.go | 54 +++++++++++++------ .../channelserver/handlers_guild_alliance.go | 6 +-- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 0858d66c2..879969e79 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -63,7 +63,6 @@ type Guild struct { Recruiting bool `db:"recruiting"` FestivalColour FestivalColour `db:"festival_colour"` Souls uint32 `db:"souls"` - Rank uint16 `db:"rank"` AllianceID uint32 `db:"alliance_id"` Icon *GuildIcon `db:"icon"` @@ -116,6 +115,35 @@ func (gi *GuildIcon) Value() (valuer driver.Value, err error) { return json.Marshal(gi) } +func (g *Guild) Rank() uint16 { + rpMap := []uint32{ + 24, 48, 96, 144, 192, 240, 288, 360, 432, + 504, 600, 696, 792, 888, 984, 1080, 1200, + } + if _config.ErupeConfig.RealClientMode <= _config.Z2 { + rpMap = []uint32{ + 3500, 6000, 8500, 11000, 13500, 16000, 20000, 24000, 28000, + 33000, 38000, 43000, 48000, 55000, 70000, 90000, 120000, + } + } + for i, u := range rpMap { + if g.RankRP < u { + if _config.ErupeConfig.RealClientMode <= _config.S6 && i >= 12 { + return 12 + } else if _config.ErupeConfig.RealClientMode <= _config.G32 && i >= 14 { + return 14 + } + return uint16(i) + } + } + if _config.ErupeConfig.RealClientMode <= _config.S6 { + return 12 + } else if _config.ErupeConfig.RealClientMode <= _config.G32 { + return 14 + } + return 17 +} + const guildInfoSelectQuery = ` SELECT g.id, @@ -138,14 +166,6 @@ SELECT recruiting, COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_colour, (SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id) AS souls, - CASE - WHEN rank_rp <= 48 THEN rank_rp/24 - WHEN rank_rp <= 288 THEN rank_rp/48+1 - WHEN rank_rp <= 504 THEN rank_rp/72+3 - WHEN rank_rp <= 1080 THEN (rank_rp-24)/96+5 - WHEN rank_rp < 1200 THEN 16 - ELSE 17 - END rank, COALESCE(( SELECT id FROM guild_alliances ga WHERE ga.parent_id = g.id OR @@ -912,7 +932,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(guild.ID) bf.WriteUint32(guild.LeaderCharID) - bf.WriteUint16(guild.Rank) + bf.WriteUint16(guild.Rank()) bf.WriteUint16(guild.MemberCount) bf.WriteUint8(guild.MainMotto) @@ -966,11 +986,11 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { } bf.WriteUint32(guild.PugiOutfits) - if guild.Rank >= 3 { + if guild.Rank() >= 3 { bf.WriteUint8(40) - } else if guild.Rank >= 7 { + } else if guild.Rank() >= 7 { bf.WriteUint8(50) - } else if guild.Rank >= 10 { + } else if guild.Rank() >= 10 { bf.WriteUint8(60) } else { bf.WriteUint8(30) @@ -1008,7 +1028,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { } else { bf.WriteUint16(0) } - bf.WriteUint16(alliance.ParentGuild.Rank) + bf.WriteUint16(alliance.ParentGuild.Rank()) bf.WriteUint16(alliance.ParentGuild.MemberCount) ps.Uint16(bf, alliance.ParentGuild.Name, true) ps.Uint16(bf, alliance.ParentGuild.LeaderName, true) @@ -1020,7 +1040,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { } else { bf.WriteUint16(0) } - bf.WriteUint16(alliance.SubGuild1.Rank) + bf.WriteUint16(alliance.SubGuild1.Rank()) bf.WriteUint16(alliance.SubGuild1.MemberCount) ps.Uint16(bf, alliance.SubGuild1.Name, true) ps.Uint16(bf, alliance.SubGuild1.LeaderName, true) @@ -1033,7 +1053,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { } else { bf.WriteUint16(0) } - bf.WriteUint16(alliance.SubGuild2.Rank) + bf.WriteUint16(alliance.SubGuild2.Rank()) bf.WriteUint16(alliance.SubGuild2.MemberCount) ps.Uint16(bf, alliance.SubGuild2.Name, true) ps.Uint16(bf, alliance.SubGuild2.LeaderName, true) @@ -1309,7 +1329,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(guild.LeaderCharID) bf.WriteUint16(guild.MemberCount) bf.WriteUint16(0x0000) // Unk - bf.WriteUint16(guild.Rank) + bf.WriteUint16(guild.Rank()) bf.WriteUint32(uint32(guild.CreatedAt.Unix())) ps.Uint8(bf, guild.Name, true) ps.Uint8(bf, guild.LeaderName, true) diff --git a/server/channelserver/handlers_guild_alliance.go b/server/channelserver/handlers_guild_alliance.go index fbb285e0a..f27d67026 100644 --- a/server/channelserver/handlers_guild_alliance.go +++ b/server/channelserver/handlers_guild_alliance.go @@ -209,14 +209,14 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) { } bf.WriteUint32(alliance.ParentGuildID) bf.WriteUint32(alliance.ParentGuild.LeaderCharID) - bf.WriteUint16(alliance.ParentGuild.Rank) + 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.Rank()) bf.WriteUint16(alliance.SubGuild1.MemberCount) ps.Uint16(bf, alliance.SubGuild1.Name, true) ps.Uint16(bf, alliance.SubGuild1.LeaderName, true) @@ -224,7 +224,7 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) { if alliance.SubGuild2ID > 0 { bf.WriteUint32(alliance.SubGuild2ID) bf.WriteUint32(alliance.SubGuild2.LeaderCharID) - bf.WriteUint16(alliance.SubGuild2.Rank) + bf.WriteUint16(alliance.SubGuild2.Rank()) bf.WriteUint16(alliance.SubGuild2.MemberCount) ps.Uint16(bf, alliance.SubGuild2.Name, true) ps.Uint16(bf, alliance.SubGuild2.LeaderName, true) From c3ee5ef7598b76de82c910b30dabb69d648a8755 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 22 Jul 2023 13:34:29 +1000 Subject: [PATCH 14/22] rewrite TransitMessage Find Party --- server/channelserver/handlers.go | 89 +++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 9c25638b5..3fc1cd976 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -6,6 +6,7 @@ import ( "erupe-ce/common/mhfcourse" ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" + _config "erupe-ce/config" "fmt" "io" "net" @@ -447,48 +448,78 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { } } case 4: // Find Party + type FindPartyParams struct { + StagePrefix string + RankRestriction uint16 + Targets []uint16 + } + findPartyParams := FindPartyParams{} bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) - setting := bf.ReadUint8() + numParams := int(bf.ReadUint8()) maxResults := bf.ReadUint16() - bf.Seek(2, 1) - partyType := bf.ReadUint16() - rankRestriction := uint16(0) - if setting >= 2 { - bf.Seek(2, 1) - rankRestriction = bf.ReadUint16() - } - targets := make([]uint16, 4) - if setting >= 3 { - bf.Seek(1, 1) - lenTargets := int(bf.ReadUint8()) - for i := 0; i < lenTargets; i++ { - targets[i] = bf.ReadUint16() + for i := 0; i < numParams; i++ { + switch bf.ReadUint8() { + case 0: + values := int(bf.ReadUint8()) + for i := 0; i < values; i++ { + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + findPartyParams.RankRestriction = bf.ReadUint16() + } else { + findPartyParams.RankRestriction = uint16(bf.ReadInt8()) + } + } + case 1: + values := int(bf.ReadUint8()) + for i := 0; i < values; i++ { + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + findPartyParams.Targets = append(findPartyParams.Targets, bf.ReadUint16()) + } else { + findPartyParams.Targets = append(findPartyParams.Targets, uint16(bf.ReadInt8())) + } + } + case 2: + values := int(bf.ReadUint8()) + for i := 0; i < values; i++ { + var value int16 + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + value = bf.ReadInt16() + } else { + value = int16(bf.ReadInt8()) + } + switch value { + case 0: // Public Bar + findPartyParams.StagePrefix = "sl2Ls210" + case 1: // Tokotoko Partnya + findPartyParams.StagePrefix = "sl2Ls463" + case 2: // Hunting Prowess Match + findPartyParams.StagePrefix = "sl2Ls286" + case 3: // Volpakkun Together + findPartyParams.StagePrefix = "sl2Ls465" + case 5: // Quick Party + // Unk + } + } } } - var stagePrefix string - switch partyType { - case 0: // Public Bar - stagePrefix = "sl2Ls210" - case 1: // Tokotoko Partnya - stagePrefix = "sl2Ls463" - case 2: // Hunting Prowess Match - stagePrefix = "sl2Ls286" - case 3: // Volpakkun Together - stagePrefix = "sl2Ls465" - case 5: // Quick Party - // Unk - } for _, c := range s.server.Channels { for _, stage := range c.stages { if count == maxResults { break } - if strings.HasPrefix(stage.id, stagePrefix) { + if strings.HasPrefix(stage.id, findPartyParams.StagePrefix) { sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}]) sb3.Seek(4, 0) stageRankRestriction := sb3.ReadUint16() stageTarget := sb3.ReadUint16() - if rankRestriction != 0xFFFF && stageRankRestriction < rankRestriction { + if stageRankRestriction > findPartyParams.RankRestriction { + continue + } + if len(findPartyParams.Targets) > 0 { + for _, target := range findPartyParams.Targets { + if target == stageTarget { + break + } + } continue } count++ From c625f595e3c201b7764182966d3f0af07de9e5db Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 22 Jul 2023 13:40:01 +1000 Subject: [PATCH 15/22] set default StagePrefix for Find Party --- server/channelserver/handlers.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 3fc1cd976..04e08a8e8 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -453,7 +453,9 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { RankRestriction uint16 Targets []uint16 } - findPartyParams := FindPartyParams{} + findPartyParams := FindPartyParams{ + StagePrefix: "sl2Ls210", + } bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) numParams := int(bf.ReadUint8()) maxResults := bf.ReadUint16() From cc428d85d35ec012ab7fe0f5ab3a83aac24f2126 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 22 Jul 2023 13:49:30 +1000 Subject: [PATCH 16/22] read additional Find Party data into struct --- server/channelserver/handlers.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 04e08a8e8..81cd1838a 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -452,6 +452,9 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { StagePrefix string RankRestriction uint16 Targets []uint16 + Unk0 []uint16 + Unk1 []uint16 + QuestID []uint16 } findPartyParams := FindPartyParams{ StagePrefix: "sl2Ls210", @@ -501,6 +504,33 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { // Unk } } + case 3: // Unknown + values := int(bf.ReadUint8()) + for i := 0; i < values; i++ { + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + findPartyParams.Unk0 = append(findPartyParams.Unk0, bf.ReadUint16()) + } else { + findPartyParams.Unk0 = append(findPartyParams.Unk0, uint16(bf.ReadInt8())) + } + } + case 4: // Looking for n or already have n + values := int(bf.ReadUint8()) + for i := 0; i < values; i++ { + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + findPartyParams.Unk1 = append(findPartyParams.Unk1, bf.ReadUint16()) + } else { + findPartyParams.Unk1 = append(findPartyParams.Unk1, uint16(bf.ReadInt8())) + } + } + case 5: + values := int(bf.ReadUint8()) + for i := 0; i < values; i++ { + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + findPartyParams.QuestID = append(findPartyParams.QuestID, bf.ReadUint16()) + } else { + findPartyParams.QuestID = append(findPartyParams.QuestID, uint16(bf.ReadInt8())) + } + } } } for _, c := range s.server.Channels { From dbedab4d33b8712d5feb942349239c23808662e0 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 22 Jul 2023 16:47:44 +1000 Subject: [PATCH 17/22] add ClientMode support based on Forward.4 --- network/crypt_conn.go | 8 +++++--- network/mhfpacket/msg_mhf_acquire_cafe_item.go | 9 +++++++-- network/mhfpacket/msg_sys_terminal_log.go | 2 +- server/channelserver/handlers_guild.go | 4 ++++ server/entranceserver/make_resp.go | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/network/crypt_conn.go b/network/crypt_conn.go index 439e9e3a0..3a952ee19 100644 --- a/network/crypt_conn.go +++ b/network/crypt_conn.go @@ -47,8 +47,10 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) { return nil, err } + dataSize := uint32(cph.DataSize) + (uint32(cph.Pf0-0x03) * 0x1000) + // Now read the encrypted packet body after getting its size from the header. - encryptedPacketBody := make([]byte, cph.DataSize) + encryptedPacketBody := make([]byte, dataSize) _, err = io.ReadFull(cc.conn, encryptedPacketBody) if err != nil { return nil, err @@ -56,7 +58,7 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) { // Update the key rotation before decrypting. if cph.KeyRotDelta != 0 { - cc.readKeyRot = (uint32(cph.KeyRotDelta) * (cc.readKeyRot + 1)) + cc.readKeyRot = uint32(cph.KeyRotDelta) * (cc.readKeyRot + 1) } out, combinedCheck, check0, check1, check2 := crypto.Decrypt(encryptedPacketBody, cc.readKeyRot, nil) @@ -94,7 +96,7 @@ func (cc *CryptConn) SendPacket(data []byte) error { keyRotDelta := byte(3) if keyRotDelta != 0 { - cc.sendKeyRot = (uint32(keyRotDelta) * (cc.sendKeyRot + 1)) + cc.sendKeyRot = uint32(keyRotDelta) * (cc.sendKeyRot + 1) } // Encrypt the data diff --git a/network/mhfpacket/msg_mhf_acquire_cafe_item.go b/network/mhfpacket/msg_mhf_acquire_cafe_item.go index 8dcad0b53..04e603a44 100644 --- a/network/mhfpacket/msg_mhf_acquire_cafe_item.go +++ b/network/mhfpacket/msg_mhf_acquire_cafe_item.go @@ -2,10 +2,11 @@ package mhfpacket import ( "errors" + _config "erupe-ce/config" + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfAcquireCafeItem represents the MSG_MHF_ACQUIRE_CAFE_ITEM @@ -30,7 +31,11 @@ func (m *MsgMhfAcquireCafeItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl m.ItemType = bf.ReadUint16() m.ItemID = bf.ReadUint16() m.Quant = bf.ReadUint16() - m.PointCost = bf.ReadUint32() + if _config.ErupeConfig.RealClientMode >= _config.G1 { + m.PointCost = bf.ReadUint32() + } else { + m.PointCost = uint32(bf.ReadUint16()) + } m.Unk0 = bf.ReadUint16() return nil } diff --git a/network/mhfpacket/msg_sys_terminal_log.go b/network/mhfpacket/msg_sys_terminal_log.go index 27cc5b1f6..43bf43dd3 100644 --- a/network/mhfpacket/msg_sys_terminal_log.go +++ b/network/mhfpacket/msg_sys_terminal_log.go @@ -39,7 +39,7 @@ func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client m.Unk0 = bf.ReadUint16() values := 15 - if _config.ErupeConfig.RealClientMode <= _config.S6 { + if _config.ErupeConfig.RealClientMode <= _config.F4 { values = 7 } for i := 0; i < int(m.EntryCount); i++ { diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 879969e79..3c28a7bed 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -130,6 +130,8 @@ func (g *Guild) Rank() uint16 { if g.RankRP < u { if _config.ErupeConfig.RealClientMode <= _config.S6 && i >= 12 { return 12 + } else if _config.ErupeConfig.RealClientMode <= _config.F4 && i >= 13 { + return 13 } else if _config.ErupeConfig.RealClientMode <= _config.G32 && i >= 14 { return 14 } @@ -138,6 +140,8 @@ func (g *Guild) Rank() uint16 { } if _config.ErupeConfig.RealClientMode <= _config.S6 { return 12 + } else if _config.ErupeConfig.RealClientMode <= _config.F4 { + return 13 } else if _config.ErupeConfig.RealClientMode <= _config.G32 { return 14 } diff --git a/server/entranceserver/make_resp.go b/server/entranceserver/make_resp.go index 86256d02c..3d64dd9f2 100644 --- a/server/entranceserver/make_resp.go +++ b/server/entranceserver/make_resp.go @@ -56,7 +56,7 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { bf.WriteUint8(si.Recommended) } - if s.erupeConfig.RealClientMode <= _config.S6 { + if s.erupeConfig.RealClientMode <= _config.F4 { combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...) combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...) bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false)) From aad9425a73f9838c558522d5f101e3947ae9eaf0 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 22 Jul 2023 18:22:21 +1000 Subject: [PATCH 18/22] guess Forward.5 compatibility --- network/mhfpacket/msg_sys_terminal_log.go | 2 +- server/channelserver/handlers_guild.go | 4 ++-- server/entranceserver/make_resp.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/network/mhfpacket/msg_sys_terminal_log.go b/network/mhfpacket/msg_sys_terminal_log.go index 43bf43dd3..079ba5bd6 100644 --- a/network/mhfpacket/msg_sys_terminal_log.go +++ b/network/mhfpacket/msg_sys_terminal_log.go @@ -39,7 +39,7 @@ func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client m.Unk0 = bf.ReadUint16() values := 15 - if _config.ErupeConfig.RealClientMode <= _config.F4 { + if _config.ErupeConfig.RealClientMode <= _config.F5 { values = 7 } for i := 0; i < int(m.EntryCount); i++ { diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 3c28a7bed..5fdd2dac1 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -130,7 +130,7 @@ func (g *Guild) Rank() uint16 { if g.RankRP < u { if _config.ErupeConfig.RealClientMode <= _config.S6 && i >= 12 { return 12 - } else if _config.ErupeConfig.RealClientMode <= _config.F4 && i >= 13 { + } else if _config.ErupeConfig.RealClientMode <= _config.F5 && i >= 13 { return 13 } else if _config.ErupeConfig.RealClientMode <= _config.G32 && i >= 14 { return 14 @@ -140,7 +140,7 @@ func (g *Guild) Rank() uint16 { } if _config.ErupeConfig.RealClientMode <= _config.S6 { return 12 - } else if _config.ErupeConfig.RealClientMode <= _config.F4 { + } else if _config.ErupeConfig.RealClientMode <= _config.F5 { return 13 } else if _config.ErupeConfig.RealClientMode <= _config.G32 { return 14 diff --git a/server/entranceserver/make_resp.go b/server/entranceserver/make_resp.go index 3d64dd9f2..619296fdb 100644 --- a/server/entranceserver/make_resp.go +++ b/server/entranceserver/make_resp.go @@ -56,7 +56,7 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { bf.WriteUint8(si.Recommended) } - if s.erupeConfig.RealClientMode <= _config.F4 { + if s.erupeConfig.RealClientMode <= _config.F5 { combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...) combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...) bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false)) From da3686b6536d476273536fb51640ec02ffcef344 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 22 Jul 2023 19:00:25 +1000 Subject: [PATCH 19/22] fix encryption not working with S6 --- network/crypt_conn.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/network/crypt_conn.go b/network/crypt_conn.go index 3a952ee19..2fc302b18 100644 --- a/network/crypt_conn.go +++ b/network/crypt_conn.go @@ -3,6 +3,7 @@ package network import ( "encoding/hex" "errors" + _config "erupe-ce/config" "fmt" "io" "net" @@ -47,10 +48,15 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) { return nil, err } - dataSize := uint32(cph.DataSize) + (uint32(cph.Pf0-0x03) * 0x1000) - // Now read the encrypted packet body after getting its size from the header. - encryptedPacketBody := make([]byte, dataSize) + var encryptedPacketBody []byte + + // Don't know when support for this was added, works in Forward.4, doesn't work in Season 6.0 + if _config.ErupeConfig.RealClientMode < _config.F1 { + encryptedPacketBody = make([]byte, cph.DataSize) + } else { + encryptedPacketBody = make([]byte, uint32(cph.DataSize)+(uint32(cph.Pf0-0x03)*0x1000)) + } _, err = io.ReadFull(cc.conn, encryptedPacketBody) if err != nil { return nil, err From e6393d73dc993515a5e7d7a9cea9e28ed0161e13 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 22 Jul 2023 21:11:53 +1000 Subject: [PATCH 20/22] fix and structise InfoFesta --- server/channelserver/handlers_festa.go | 108 +++++++++++++++++++------ 1 file changed, 82 insertions(+), 26 deletions(-) diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index 155cee3d8..df972c0a9 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -1,7 +1,6 @@ package channelserver import ( - "encoding/hex" "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" "erupe-ce/common/token" @@ -140,13 +139,26 @@ func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 { return timestamps } -type Trial struct { +type FestaTrial struct { ID uint32 `db:"id"` - Objective uint8 `db:"objective"` + Objective uint16 `db:"objective"` GoalID uint32 `db:"goal_id"` TimesReq uint16 `db:"times_req"` Locale uint16 `db:"locale_req"` Reward uint16 `db:"reward"` + Monopoly uint16 + Unk uint16 +} + +type FestaReward struct { + Unk0 uint8 + Unk1 uint8 + Unk2 uint16 + Unk3 uint16 + Unk4 uint16 + Unk5 uint16 + Unk6 uint16 + Unk7 uint8 } func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { @@ -190,36 +202,70 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(blueSouls) bf.WriteUint32(redSouls) + var trials []FestaTrial + var trial FestaTrial rows, _ = s.server.db.Queryx("SELECT * FROM festa_trials") - trialData := byteframe.NewByteFrame() - var count uint16 for rows.Next() { - trial := &Trial{} err := rows.StructScan(&trial) if err != nil { continue } - count++ - trialData.WriteUint32(trial.ID) - trialData.WriteUint8(0) // Unk - trialData.WriteUint8(trial.Objective) - trialData.WriteUint32(trial.GoalID) - trialData.WriteUint16(trial.TimesReq) - trialData.WriteUint16(trial.Locale) - trialData.WriteUint16(trial.Reward) - trialData.WriteUint8(0xFF) // Unk - trialData.WriteUint8(0xFF) // MonopolyState - trialData.WriteUint16(0) // Unk + trials = append(trials, trial) + } + bf.WriteUint16(uint16(len(trials))) + for _, trial := range trials { + bf.WriteUint32(trial.ID) + bf.WriteUint16(trial.Objective) + bf.WriteUint32(trial.GoalID) + bf.WriteUint16(trial.TimesReq) + bf.WriteUint16(trial.Locale) + bf.WriteUint16(trial.Reward) + trial.Monopoly = 0xFFFF // NYI + bf.WriteUint16(trial.Monopoly) + bf.WriteUint16(trial.Unk) } - bf.WriteUint16(count) - bf.WriteBytes(trialData.Data()) - // Static bonus rewards - rewards, _ := hex.DecodeString("001901000007015E05F000000000000100000703E81B6300000000010100000C03E8000000000000000100000D0000000000000000000100000100000000000000000002000007015E05F000000000000200000703E81B6300000000010200000C03E8000000000000000200000D0000000000000000000200000400000000000000000003000007015E05F000000000000300000703E81B6300000000010300000C03E8000000000000000300000D0000000000000000000300000100000000000000000004000007015E05F000000000000400000703E81B6300000000010400000C03E8000000000000000400000D0000000000000000000400000400000000000000000005000007015E05F000000000000500000703E81B6300000000010500000C03E8000000000000000500000D00000000000000000005000001000000000000000000") - bf.WriteBytes(rewards) + rewards := []FestaReward{ + {1, 0, 7, 350, 1520, 0, 0, 0}, + {1, 0, 7, 1000, 7011, 0, 0, 1}, + {1, 0, 12, 1000, 0, 0, 0, 0}, + {1, 0, 13, 0, 0, 0, 0, 0}, + {1, 0, 1, 0, 0, 0, 0, 0}, + {2, 0, 7, 350, 1520, 0, 0, 0}, + {2, 0, 7, 1000, 7011, 0, 0, 1}, + {2, 0, 12, 1000, 0, 0, 0, 0}, + {2, 0, 13, 0, 0, 0, 0, 0}, + {2, 0, 4, 0, 0, 0, 0, 0}, + {3, 0, 7, 350, 1520, 0, 0, 0}, + {3, 0, 7, 1000, 7011, 0, 0, 1}, + {3, 0, 12, 1000, 0, 0, 0, 0}, + {3, 0, 13, 0, 0, 0, 0, 0}, + {3, 0, 1, 0, 0, 0, 0, 0}, + {4, 0, 7, 350, 1520, 0, 0, 0}, + {4, 0, 7, 1000, 7011, 0, 0, 1}, + {4, 0, 12, 1000, 0, 0, 0, 0}, + {4, 0, 13, 0, 0, 0, 0, 0}, + {4, 0, 4, 0, 0, 0, 0, 0}, + {5, 0, 7, 350, 1520, 0, 0, 0}, + {5, 0, 7, 1000, 7011, 0, 0, 1}, + {5, 0, 12, 1000, 0, 0, 0, 0}, + {5, 0, 13, 0, 0, 0, 0, 0}, + {5, 0, 1, 0, 0, 0, 0, 0}, + } + bf.WriteUint16(uint16(len(rewards))) + for _, reward := range rewards { + bf.WriteUint8(reward.Unk0) + bf.WriteUint8(reward.Unk1) + bf.WriteUint16(reward.Unk2) + bf.WriteUint16(reward.Unk3) + bf.WriteUint16(reward.Unk4) + bf.WriteUint16(reward.Unk5) + bf.WriteUint16(reward.Unk6) + bf.WriteUint8(reward.Unk7) + } - bf.WriteUint16(0x0001) - bf.WriteUint32(0xD4C001F4) + bf.WriteUint32(120000) + bf.WriteUint16(500) categoryWinners := uint16(0) // NYI bf.WriteUint16(categoryWinners) @@ -239,8 +285,18 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { ps.Uint8(bf, "", true) // Guild Name } - d, _ := hex.DecodeString("000000000000000100001388000007D0000003E800000064012C00C8009600640032") - bf.WriteBytes(d) + // Unknown values + bf.WriteUint32(1) + bf.WriteUint32(5000) + bf.WriteUint32(2000) + bf.WriteUint32(1000) + bf.WriteUint32(100) + bf.WriteUint16(300) + bf.WriteUint16(200) + bf.WriteUint16(150) + bf.WriteUint16(100) + bf.WriteUint16(50) + ps.Uint16(bf, "", false) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } From 769f989c918649ee92366743017f65629dd13139 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 22 Jul 2023 23:12:26 +1000 Subject: [PATCH 21/22] add MaximumFP option, hide null Festa Armor --- config.json | 1 + config/config.go | 1 + server/channelserver/handlers_festa.go | 35 +++++++++++++------------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/config.json b/config.json index 6e69bb0a8..e752a93f1 100644 --- a/config.json +++ b/config.json @@ -39,6 +39,7 @@ "FeaturedWeapons": 1, "MaximumNP": 100000, "MaximumRP": 50000, + "MaximumFP": 120000, "DisableLoginBoost": false, "DisableBoostTime": false, "BoostTimeDuration": 120, diff --git a/config/config.go b/config/config.go index c02296b26..78ddd9e8a 100644 --- a/config/config.go +++ b/config/config.go @@ -124,6 +124,7 @@ type GameplayOptions struct { FeaturedWeapons int // Number of Active Feature weapons to generate daily MaximumNP int // Maximum number of NP held by a player MaximumRP uint16 // Maximum number of RP held by a player + MaximumFP uint32 // Maximum number of FP held by a player DisableLoginBoost bool // Disables the Login Boost system DisableBoostTime bool // Disables the daily NetCafe Boost Time BoostTimeDuration int // The number of minutes NetCafe Boost Time lasts for diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index df972c0a9..0f590b5d9 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -151,14 +151,14 @@ type FestaTrial struct { } type FestaReward struct { - Unk0 uint8 - Unk1 uint8 - Unk2 uint16 - Unk3 uint16 - Unk4 uint16 - Unk5 uint16 - Unk6 uint16 - Unk7 uint8 + Unk0 uint8 + Unk1 uint8 + ItemType uint16 + Quantity uint16 + ItemID uint16 + Unk5 uint16 + Unk6 uint16 + Unk7 uint8 } func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { @@ -225,46 +225,47 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(trial.Unk) } + // The Winner and Loser Armor IDs are missing rewards := []FestaReward{ {1, 0, 7, 350, 1520, 0, 0, 0}, {1, 0, 7, 1000, 7011, 0, 0, 1}, {1, 0, 12, 1000, 0, 0, 0, 0}, {1, 0, 13, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 0, 0}, + //{1, 0, 1, 0, 0, 0, 0, 0}, {2, 0, 7, 350, 1520, 0, 0, 0}, {2, 0, 7, 1000, 7011, 0, 0, 1}, {2, 0, 12, 1000, 0, 0, 0, 0}, {2, 0, 13, 0, 0, 0, 0, 0}, - {2, 0, 4, 0, 0, 0, 0, 0}, + //{2, 0, 4, 0, 0, 0, 0, 0}, {3, 0, 7, 350, 1520, 0, 0, 0}, {3, 0, 7, 1000, 7011, 0, 0, 1}, {3, 0, 12, 1000, 0, 0, 0, 0}, {3, 0, 13, 0, 0, 0, 0, 0}, - {3, 0, 1, 0, 0, 0, 0, 0}, + //{3, 0, 1, 0, 0, 0, 0, 0}, {4, 0, 7, 350, 1520, 0, 0, 0}, {4, 0, 7, 1000, 7011, 0, 0, 1}, {4, 0, 12, 1000, 0, 0, 0, 0}, {4, 0, 13, 0, 0, 0, 0, 0}, - {4, 0, 4, 0, 0, 0, 0, 0}, + //{4, 0, 4, 0, 0, 0, 0, 0}, {5, 0, 7, 350, 1520, 0, 0, 0}, {5, 0, 7, 1000, 7011, 0, 0, 1}, {5, 0, 12, 1000, 0, 0, 0, 0}, {5, 0, 13, 0, 0, 0, 0, 0}, - {5, 0, 1, 0, 0, 0, 0, 0}, + //{5, 0, 1, 0, 0, 0, 0, 0}, } bf.WriteUint16(uint16(len(rewards))) for _, reward := range rewards { bf.WriteUint8(reward.Unk0) bf.WriteUint8(reward.Unk1) - bf.WriteUint16(reward.Unk2) - bf.WriteUint16(reward.Unk3) - bf.WriteUint16(reward.Unk4) + bf.WriteUint16(reward.ItemType) + bf.WriteUint16(reward.Quantity) + bf.WriteUint16(reward.ItemID) bf.WriteUint16(reward.Unk5) bf.WriteUint16(reward.Unk6) bf.WriteUint8(reward.Unk7) } - bf.WriteUint32(120000) + bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MaximumFP) bf.WriteUint16(500) categoryWinners := uint16(0) // NYI From 19e1eae5e2fc3302bd3639c92a642f45ca00bb6c Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 23 Jul 2023 13:27:10 +1000 Subject: [PATCH 22/22] simplify and expand Stage Object indexer --- server/channelserver/sys_stage.go | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/server/channelserver/sys_stage.go b/server/channelserver/sys_stage.go index b69995724..7b8fb15eb 100644 --- a/server/channelserver/sys_stage.go +++ b/server/channelserver/sys_stage.go @@ -30,7 +30,7 @@ type Stage struct { // Objects objects map[uint32]*Object - objectIndex uint8 + objectIndex uint16 // Map of session -> charID. // These are clients that are CURRENTLY in the stage @@ -56,7 +56,6 @@ func NewStage(ID string) *Stage { clients: make(map[*Session]uint32), reservedClientSlots: make(map[uint32]bool), objects: make(map[uint32]*Object), - objectIndex: 0, rawBinaryData: make(map[stageBinaryKey][]byte), maxPlayers: 4, } @@ -97,16 +96,10 @@ func (s *Stage) isQuest() bool { } func (s *Stage) NextObjectID() uint32 { - s.objectIndex = s.objectIndex + 1 - // Objects beyond 127 do not duplicate correctly - // Indexes 0 and 127 does not update position correctly - if s.objectIndex == 127 { - s.objectIndex = 1 - } + s.objectIndex++ bf := byteframe.NewByteFrame() - bf.WriteUint8(0) - bf.WriteUint8(s.objectIndex) - bf.WriteUint16(0) - obj := uint32(bf.Data()[3]) | uint32(bf.Data()[2])<<8 | uint32(bf.Data()[1])<<16 | uint32(bf.Data()[0])<<24 - return obj + bf.WriteUint16(127) + bf.WriteUint16(s.objectIndex) + bf.Seek(0, 0) + return bf.ReadUint32() }