From ce31b248eb4150092a94828db18f137224dd61b4 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 12 Aug 2022 15:13:31 +1000 Subject: [PATCH 01/36] loaddata fallback --- server/channelserver/handlers_data.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index e98578da7..d9cdcca17 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -297,10 +297,11 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) { } var data []byte - err := s.server.db.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.charID).Scan(&data) - if err != nil { - s.logger.Fatal("Failed to get savedata from db", zap.Error(err)) + if err != nil || len(data) == 0 { + s.logger.Warn(fmt.Sprintf("Failed to load savedata (CID: %d)", s.charID), zap.Error(err)) + s.rawConn.Close() // Terminate the connection + return } doAckBufSucceed(s, pkt.AckHandle, data) @@ -310,11 +311,11 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) { } bf := byteframe.NewByteFrameFromBytes(decompSaveData) bf.Seek(88, io.SeekStart) - binary1 := bf.ReadNullTerminatedBytes() + name := bf.ReadNullTerminatedBytes() s.server.userBinaryPartsLock.Lock() - s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: 1}] = append(binary1, []byte{0x00}...) + s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: 1}] = append(name, []byte{0x00}...) s.server.userBinaryPartsLock.Unlock() - s.Name = stringsupport.SJISToUTF8(binary1) + s.Name = stringsupport.SJISToUTF8(name) } func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) { From 0bf19c8ac8358f941dec51fc58f901341774afcc Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 12 Aug 2022 16:38:00 +1000 Subject: [PATCH 02/36] temp handle monthly guild allowance --- .../mhfpacket/msg_mhf_acquire_monthly_item.go | 23 ++++++++++++++----- .../mhfpacket/msg_mhf_check_monthly_item.go | 17 +++++++++----- server/channelserver/handlers.go | 4 ---- server/channelserver/handlers_guild.go | 11 +++++++++ 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/network/mhfpacket/msg_mhf_acquire_monthly_item.go b/network/mhfpacket/msg_mhf_acquire_monthly_item.go index a8d8e2f04..acc10b42a 100644 --- a/network/mhfpacket/msg_mhf_acquire_monthly_item.go +++ b/network/mhfpacket/msg_mhf_acquire_monthly_item.go @@ -1,15 +1,21 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfAcquireMonthlyItem represents the MSG_MHF_ACQUIRE_MONTHLY_ITEM -type MsgMhfAcquireMonthlyItem struct{} +type MsgMhfAcquireMonthlyItem struct { + AckHandle uint32 + Unk0 uint16 + Unk1 uint16 + Unk2 uint32 + Unk3 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfAcquireMonthlyItem) Opcode() network.PacketID { @@ -18,7 +24,12 @@ func (m *MsgMhfAcquireMonthlyItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfAcquireMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() + m.Unk2 = bf.ReadUint32() + m.Unk3 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_check_monthly_item.go b/network/mhfpacket/msg_mhf_check_monthly_item.go index 2e04c3226..d79c65240 100644 --- a/network/mhfpacket/msg_mhf_check_monthly_item.go +++ b/network/mhfpacket/msg_mhf_check_monthly_item.go @@ -1,15 +1,18 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfCheckMonthlyItem represents the MSG_MHF_CHECK_MONTHLY_ITEM -type MsgMhfCheckMonthlyItem struct{} +type MsgMhfCheckMonthlyItem struct { + AckHandle uint32 + Unk uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfCheckMonthlyItem) Opcode() network.PacketID { @@ -18,7 +21,9 @@ func (m *MsgMhfCheckMonthlyItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfCheckMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk = 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 58ea6b401..d3497eae9 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -702,10 +702,6 @@ func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfCheckMonthlyItem(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfAcquireMonthlyItem(s *Session, p mhfpacket.MHFPacket) {} - func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index f7aa9cdc5..2dd84f659 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1886,6 +1886,17 @@ func handleMsgMhfSetGuildManageRight(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) } +func handleMsgMhfCheckMonthlyItem(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfCheckMonthlyItem) + doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + // TODO: Implement month-by-month tracker, 0 = Not claimed, 1 = Claimed +} + +func handleMsgMhfAcquireMonthlyItem(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfAcquireMonthlyItem) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} + func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateInvGuild) stubEnumerateNoResults(s, pkt.AckHandle) From 18989e9435d70a481279166b3e77f07890f94d9e Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 16:25:32 +1000 Subject: [PATCH 03/36] initial netcafe implementation --- ...g_mhf_post_cafe_duration_bonus_received.go | 19 +- network/mhfpacket/msg_mhf_start_boost_time.go | 17 +- patch-schema/netcafe.sql | 21 +++ server/channelserver/handlers.go | 67 +------ server/channelserver/handlers_cafe.go | 178 ++++++++++++++++++ server/channelserver/handlers_event.go | 2 - server/channelserver/handlers_tactics.go | 9 - 7 files changed, 226 insertions(+), 87 deletions(-) create mode 100644 patch-schema/netcafe.sql create mode 100644 server/channelserver/handlers_cafe.go diff --git a/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go b/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go index c89159c0c..be8947f2c 100644 --- a/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go +++ b/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.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" ) // MsgMhfPostCafeDurationBonusReceived represents the MSG_MHF_POST_CAFE_DURATION_BONUS_RECEIVED -type MsgMhfPostCafeDurationBonusReceived struct{} +type MsgMhfPostCafeDurationBonusReceived struct { + AckHandle uint32 + Unk0 uint32 + CafeBonusID uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfPostCafeDurationBonusReceived) Opcode() network.PacketID { @@ -18,7 +22,10 @@ func (m *MsgMhfPostCafeDurationBonusReceived) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPostCafeDurationBonusReceived) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.CafeBonusID = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_start_boost_time.go b/network/mhfpacket/msg_mhf_start_boost_time.go index ff424aca9..583ab3ab0 100644 --- a/network/mhfpacket/msg_mhf_start_boost_time.go +++ b/network/mhfpacket/msg_mhf_start_boost_time.go @@ -1,15 +1,18 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfStartBoostTime represents the MSG_MHF_START_BOOST_TIME -type MsgMhfStartBoostTime struct{} +type MsgMhfStartBoostTime struct { + AckHandle uint32 + Unk0 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfStartBoostTime) Opcode() network.PacketID { @@ -18,7 +21,9 @@ func (m *MsgMhfStartBoostTime) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfStartBoostTime) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql new file mode 100644 index 000000000..6bef530cc --- /dev/null +++ b/patch-schema/netcafe.sql @@ -0,0 +1,21 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.characters + ADD COLUMN cafe_time integer DEFAULT 0; + +CREATE TABLE IF NOT EXISTS public.cafebonus +( + id integer NOT NULL PRIMARY KEY, + line integer NOT NULL, + itemclass integer NOT NULL, + itemid integer NOT NULL, + tradequantity integer NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.cafe_accepted +( + cafe_id integer NOT NULL, + character_id integer NOT NULL +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index d3497eae9..d409fdde8 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -10,16 +10,14 @@ import ( "net" "strings" - "io/ioutil" - "math/bits" - "math/rand" - "time" - "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" "go.uber.org/zap" "golang.org/x/text/encoding/japanese" "golang.org/x/text/transform" + "io/ioutil" + "math/bits" + "math/rand" ) // Temporary function to just return no results for a MSG_MHF_ENUMERATE* packet @@ -641,65 +639,6 @@ func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } -func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem) - var netcafe_points int - err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafe_points) - if err != nil { - s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) - } - resp := byteframe.NewByteFrame() - resp.WriteUint32(uint32(netcafe_points)) - doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) -} - -func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint) - var netcafe_points int - err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafe_points) - if err != nil { - s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) - } - resp := byteframe.NewByteFrame() - resp.WriteUint32(0) - resp.WriteUint32(uint32(netcafe_points)) - doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) -} - -func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint) - - // I am not sure exactly what this does, but all responses I have seen include this exact sequence of bytes - // 1 daily, 5 daily halk pots, 3 point boosted quests, also adds 5 netcafe points but not sent to client - // available once after midday every day - - // get next midday - var t = Time_static() - year, month, day := t.Date() - midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location()) - if t.After(midday) { - midday = midday.Add(24 * time.Hour) - } - - // get time after which daily claiming would be valid from db - var dailyTime time.Time - err := s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime) - if err != nil { - s.logger.Fatal("Failed to get daily_time savedata from db", zap.Error(err)) - } - - if t.After(dailyTime) { - // +5 netcafe points and setting next valid window - _, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points::int + 5 WHERE id=$2", midday, s.charID) - if err != nil { - s.logger.Fatal("Failed to update daily_time and netcafe_points savedata in db", zap.Error(err)) - } - doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01}) - } else { - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - } -} - func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go new file mode 100644 index 000000000..8692864cf --- /dev/null +++ b/server/channelserver/handlers_cafe.go @@ -0,0 +1,178 @@ +package channelserver + +import ( + "erupe-ce/common/byteframe" + ps "erupe-ce/common/pascalstring" + "erupe-ce/network/mhfpacket" + "go.uber.org/zap" + "time" +) + +func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem) + var netcafe_points int + err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafe_points) + if err != nil { + s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) + } + resp := byteframe.NewByteFrame() + resp.WriteUint32(uint32(netcafe_points)) + doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) +} + +func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint) + var netcafe_points int + err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafe_points) + if err != nil { + s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) + } + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) + resp.WriteUint32(uint32(netcafe_points)) + doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) +} + +func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint) + + // I am not sure exactly what this does, but all responses I have seen include this exact sequence of bytes + // 1 daily, 5 daily halk pots, 3 point boosted quests, also adds 5 netcafe points but not sent to client + // available once after midday every day + + // get next midday + var t = Time_static() + year, month, day := t.Date() + midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location()) + if t.After(midday) { + midday = midday.Add(24 * time.Hour) + } + + // get time after which daily claiming would be valid from db + var dailyTime time.Time + err := s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime) + if err != nil { + s.logger.Fatal("Failed to get daily_time savedata from db", zap.Error(err)) + } + + if t.After(dailyTime) { + // +5 netcafe points and setting next valid window + _, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points::int + 5 WHERE id=$2", midday, s.charID) + if err != nil { + s.logger.Fatal("Failed to update daily_time and netcafe_points savedata in db", zap.Error(err)) + } + doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01}) + } else { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + } +} + +func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetCafeDuration) + bf := byteframe.NewByteFrame() + + var cafeTime uint32 + err := s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime) + if err != nil { + panic(err) + } + cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime + bf.WriteUint32(cafeTime) // Total hours + bf.WriteUint16(0) + ps.Uint16(bf, "Resets at next maintenance", true) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} + +type CafeBonus struct { + ID uint32 `db:"id"` + Line uint32 `db:"line"` + ItemClass uint32 `db:"itemclass"` + ItemID uint32 `db:"itemid"` + TradeQuantity uint32 `db:"tradequantity"` + FlagCount uint8 `db:"flagcount"` +} + +func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetCafeDurationBonusInfo) + bf := byteframe.NewByteFrame() + + var count uint32 + rows, err := s.server.db.Queryx(` + SELECT cb.id, line, itemclass, itemid, tradequantity, + ( + SELECT count(*) + FROM cafe_accepted ca + WHERE cb.id = ca.cafe_id AND ca.character_id = $1 + ) AS flagcount + FROM cafebonus cb ORDER BY id ASC;`, s.charID) + if err != nil { + s.logger.Error("Error getting cafebonus", zap.Error(err)) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + } else { + for rows.Next() { + count++ + Cafes := &CafeBonus{} + err = rows.StructScan(&Cafes) + if err != nil { + s.logger.Error("Error scanning cafebonus", zap.Error(err)) + } + bf.WriteUint32(Cafes.Line) // Time + bf.WriteUint32(0) // Unk + bf.WriteUint32(Cafes.ItemID) + bf.WriteUint32(Cafes.TradeQuantity) + bf.WriteUint8(Cafes.FlagCount) + } + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) + resp.WriteUint32(uint32(time.Now().Unix())) //timestamp + resp.WriteUint32(count) + resp.WriteBytes(bf.Data()) + doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + } +} + +func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfReceiveCafeDurationBonus) + bf := byteframe.NewByteFrame() + + row := s.server.db.QueryRowx(` + SELECT c.id, c.line, itemclass, itemid, tradequantity + FROM cafebonus c + WHERE ( + SELECT count(*) + FROM cafe_accepted ca + WHERE c.id = ca.cafe_id AND ca.character_id = $1 + ) < 1 AND ( + SELECT ch.cafe_time + $2 + FROM characters ch + WHERE ch.id = $1 + ) >= c.line LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) + Cafe := &CafeBonus{} + err := row.StructScan(Cafe) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 8)) + return + } + bf.WriteUint32(1) + bf.WriteUint32(Cafe.ID) + bf.WriteUint32(Cafe.ItemClass) + bf.WriteUint32(Cafe.ItemID) + bf.WriteUint32(Cafe.TradeQuantity) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} + +func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPostCafeDurationBonusReceived) + + _, err := s.server.db.Exec("INSERT INTO public.cafe_accepted VALUES ($1, $2)", pkt.CafeBonusID, s.charID) + if err != nil { + s.logger.Error("Error updating accepted CafeBonus count", zap.Error(err)) + } + + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} + +func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfStartBoostTime) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index af16799c7..87ae246af 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -325,8 +325,6 @@ func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } -func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {} - func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_tactics.go b/server/channelserver/handlers_tactics.go index 585c0ef79..4991ce846 100644 --- a/server/channelserver/handlers_tactics.go +++ b/server/channelserver/handlers_tactics.go @@ -2,7 +2,6 @@ package channelserver import ( "encoding/hex" - "erupe-ce/network/mhfpacket" ) @@ -58,11 +57,3 @@ func handleMsgMhfGetUdTacticsRanking(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdTacticsLog(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) {} From 3bb0ab62955ad30358f233baba04d36dd10285ef Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 17:21:24 +1000 Subject: [PATCH 04/36] cleanup logic and update cafe time --- patch-schema/netcafe.sql | 8 ++-- server/channelserver/handlers.go | 25 +++++----- server/channelserver/handlers_cafe.go | 66 +++++++++++++-------------- 3 files changed, 51 insertions(+), 48 deletions(-) diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index 6bef530cc..cfd85e5f8 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -6,10 +6,10 @@ ALTER TABLE IF EXISTS public.characters CREATE TABLE IF NOT EXISTS public.cafebonus ( id integer NOT NULL PRIMARY KEY, - line integer NOT NULL, - itemclass integer NOT NULL, - itemid integer NOT NULL, - tradequantity integer NOT NULL + minutes_req integer NOT NULL, + item_type integer NOT NULL, + item_id integer NOT NULL, + quantity integer NOT NULL ); CREATE TABLE IF NOT EXISTS public.cafe_accepted diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index d409fdde8..26447a2fb 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -190,8 +190,12 @@ func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) { } func logoutPlayer(s *Session) { - delete(s.server.sessions, s.rawConn) - s.rawConn.Close() + if _, exists := s.server.sessions[s.rawConn]; exists { + delete(s.server.sessions, s.rawConn) + s.rawConn.Close() + } else { + return // Prevent re-running logout logic on real logouts + } _, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token) if err != nil { @@ -204,13 +208,13 @@ func logoutPlayer(s *Session) { } var timePlayed int + var sessionTime int _ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed) - - timePlayed = (int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)) + timePlayed + sessionTime = int(Time_Current_Adjusted().Unix()) - int(s.sessionStart) + timePlayed += sessionTime var rpGained int - - if s.rights > 0x40000000 { // N Course + if s.rights >= 0x40000000 { // N Course rpGained = timePlayed / 900 timePlayed = timePlayed % 900 } else { @@ -218,10 +222,10 @@ func logoutPlayer(s *Session) { timePlayed = timePlayed % 1800 } - _, err = s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID) - if err != nil { - panic(err) - } + s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID) + s.server.db.Exec("UPDATE characters SET cafe_time=cafe_time+$1 WHERE id=$2", sessionTime, s.charID) + + treasureHuntUnregister(s) if s.stage == nil { return @@ -241,7 +245,6 @@ func logoutPlayer(s *Session) { removeSessionFromSemaphore(s) removeSessionFromStage(s) - treasureHuntUnregister(s) saveData, err := GetCharacterSaveData(s, s.charID) if err != nil { diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 8692864cf..76529ce4f 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -10,26 +10,26 @@ import ( func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem) - var netcafe_points int - err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafe_points) + var netcafePoints uint32 + err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafePoints) if err != nil { - s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) + s.logger.Fatal("Failed to get netcafe points from db", zap.Error(err)) } resp := byteframe.NewByteFrame() - resp.WriteUint32(uint32(netcafe_points)) + resp.WriteUint32(netcafePoints) doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) } func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint) - var netcafe_points int - err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafe_points) + var netcafePoints uint32 + err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafePoints) if err != nil { - s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) + s.logger.Fatal("Failed to get netcate points from db", zap.Error(err)) } resp := byteframe.NewByteFrame() resp.WriteUint32(0) - resp.WriteUint32(uint32(netcafe_points)) + resp.WriteUint32(netcafePoints) doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) } @@ -77,19 +77,19 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { panic(err) } cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime - bf.WriteUint32(cafeTime) // Total hours + bf.WriteUint32(cafeTime) // Total cafe time bf.WriteUint16(0) ps.Uint16(bf, "Resets at next maintenance", true) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } type CafeBonus struct { - ID uint32 `db:"id"` - Line uint32 `db:"line"` - ItemClass uint32 `db:"itemclass"` - ItemID uint32 `db:"itemid"` - TradeQuantity uint32 `db:"tradequantity"` - FlagCount uint8 `db:"flagcount"` + ID uint32 `db:"id"` + Minutes uint32 `db:"minutes_req"` + ItemType uint32 `db:"item_type"` + ItemID uint32 `db:"item_id"` + Quantity uint32 `db:"quantity"` + Claimed bool `db:"claimed"` } func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { @@ -98,12 +98,12 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { var count uint32 rows, err := s.server.db.Queryx(` - SELECT cb.id, line, itemclass, itemid, tradequantity, + SELECT cb.id, minutes_req, item_type, item_id, quantity, ( SELECT count(*) FROM cafe_accepted ca WHERE cb.id = ca.cafe_id AND ca.character_id = $1 - ) AS flagcount + )::int::bool AS claimed FROM cafebonus cb ORDER BY id ASC;`, s.charID) if err != nil { s.logger.Error("Error getting cafebonus", zap.Error(err)) @@ -111,20 +111,20 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { } else { for rows.Next() { count++ - Cafes := &CafeBonus{} - err = rows.StructScan(&Cafes) + cafeBonus := &CafeBonus{} + err = rows.StructScan(&cafeBonus) if err != nil { s.logger.Error("Error scanning cafebonus", zap.Error(err)) } - bf.WriteUint32(Cafes.Line) // Time - bf.WriteUint32(0) // Unk - bf.WriteUint32(Cafes.ItemID) - bf.WriteUint32(Cafes.TradeQuantity) - bf.WriteUint8(Cafes.FlagCount) + bf.WriteUint32(cafeBonus.Minutes) + bf.WriteUint32(0) // Unk + bf.WriteUint32(cafeBonus.ItemID) + bf.WriteUint32(cafeBonus.Quantity) + bf.WriteBool(cafeBonus.Claimed) } resp := byteframe.NewByteFrame() resp.WriteUint32(0) - resp.WriteUint32(uint32(time.Now().Unix())) //timestamp + resp.WriteUint32(uint32(time.Now().Unix())) resp.WriteUint32(count) resp.WriteBytes(bf.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) @@ -136,7 +136,7 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() row := s.server.db.QueryRowx(` - SELECT c.id, c.line, itemclass, itemid, tradequantity + SELECT c.id, minutes_req, item_type, item_id, quantity FROM cafebonus c WHERE ( SELECT count(*) @@ -146,18 +146,18 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { SELECT ch.cafe_time + $2 FROM characters ch WHERE ch.id = $1 - ) >= c.line LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) - Cafe := &CafeBonus{} - err := row.StructScan(Cafe) + ) >= minutes_req LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) + cafeBonus := &CafeBonus{} + err := row.StructScan(cafeBonus) if err != nil { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 8)) return } bf.WriteUint32(1) - bf.WriteUint32(Cafe.ID) - bf.WriteUint32(Cafe.ItemClass) - bf.WriteUint32(Cafe.ItemID) - bf.WriteUint32(Cafe.TradeQuantity) + bf.WriteUint32(cafeBonus.ID) + bf.WriteUint32(cafeBonus.ItemType) + bf.WriteUint32(cafeBonus.ItemID) + bf.WriteUint32(cafeBonus.Quantity) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } From ac90c2dd09aae0103aa91b57ddcab35536cd69f8 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 17:23:33 +1000 Subject: [PATCH 05/36] correct minutes->seconds --- patch-schema/netcafe.sql | 2 +- server/channelserver/handlers_cafe.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index cfd85e5f8..e56633c46 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -6,7 +6,7 @@ ALTER TABLE IF EXISTS public.characters CREATE TABLE IF NOT EXISTS public.cafebonus ( id integer NOT NULL PRIMARY KEY, - minutes_req integer NOT NULL, + seconds_req integer NOT NULL, item_type integer NOT NULL, item_id integer NOT NULL, quantity integer NOT NULL diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 76529ce4f..8e7deaf5e 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -85,7 +85,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { type CafeBonus struct { ID uint32 `db:"id"` - Minutes uint32 `db:"minutes_req"` + Seconds uint32 `db:"time_req"` ItemType uint32 `db:"item_type"` ItemID uint32 `db:"item_id"` Quantity uint32 `db:"quantity"` @@ -98,7 +98,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { var count uint32 rows, err := s.server.db.Queryx(` - SELECT cb.id, minutes_req, item_type, item_id, quantity, + SELECT cb.id, seconds_req, item_type, item_id, quantity, ( SELECT count(*) FROM cafe_accepted ca @@ -116,7 +116,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { if err != nil { s.logger.Error("Error scanning cafebonus", zap.Error(err)) } - bf.WriteUint32(cafeBonus.Minutes) + bf.WriteUint32(cafeBonus.Seconds) bf.WriteUint32(0) // Unk bf.WriteUint32(cafeBonus.ItemID) bf.WriteUint32(cafeBonus.Quantity) @@ -136,7 +136,7 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() row := s.server.db.QueryRowx(` - SELECT c.id, minutes_req, item_type, item_id, quantity + SELECT c.id, seconds_req, item_type, item_id, quantity FROM cafebonus c WHERE ( SELECT count(*) @@ -146,7 +146,7 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { SELECT ch.cafe_time + $2 FROM characters ch WHERE ch.id = $1 - ) >= minutes_req LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) + ) >= seconds_req LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) cafeBonus := &CafeBonus{} err := row.StructScan(cafeBonus) if err != nil { From 0ba9adcc88534dba575a27e8bfe946ad52303ff6 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 17:46:17 +1000 Subject: [PATCH 06/36] handle receiving multiple items --- patch-schema/netcafe.sql | 2 +- server/channelserver/handlers_cafe.go | 43 ++++++++++++++++----------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index e56633c46..c7aeb9460 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -6,7 +6,7 @@ ALTER TABLE IF EXISTS public.characters CREATE TABLE IF NOT EXISTS public.cafebonus ( id integer NOT NULL PRIMARY KEY, - seconds_req integer NOT NULL, + time_req integer NOT NULL, item_type integer NOT NULL, item_id integer NOT NULL, quantity integer NOT NULL diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 8e7deaf5e..cfac96cc7 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -5,6 +5,7 @@ import ( ps "erupe-ce/common/pascalstring" "erupe-ce/network/mhfpacket" "go.uber.org/zap" + "io" "time" ) @@ -85,7 +86,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { type CafeBonus struct { ID uint32 `db:"id"` - Seconds uint32 `db:"time_req"` + TimeReq uint32 `db:"time_req"` ItemType uint32 `db:"item_type"` ItemID uint32 `db:"item_id"` Quantity uint32 `db:"quantity"` @@ -98,7 +99,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { var count uint32 rows, err := s.server.db.Queryx(` - SELECT cb.id, seconds_req, item_type, item_id, quantity, + SELECT cb.id, time_req, item_type, item_id, quantity, ( SELECT count(*) FROM cafe_accepted ca @@ -116,7 +117,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { if err != nil { s.logger.Error("Error scanning cafebonus", zap.Error(err)) } - bf.WriteUint32(cafeBonus.Seconds) + bf.WriteUint32(cafeBonus.TimeReq) bf.WriteUint32(0) // Unk bf.WriteUint32(cafeBonus.ItemID) bf.WriteUint32(cafeBonus.Quantity) @@ -134,9 +135,10 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfReceiveCafeDurationBonus) bf := byteframe.NewByteFrame() - - row := s.server.db.QueryRowx(` - SELECT c.id, seconds_req, item_type, item_id, quantity + var count uint32 + bf.WriteUint32(0) + rows, err := s.server.db.Queryx(` + SELECT c.id, time_req, item_type, item_id, quantity FROM cafebonus c WHERE ( SELECT count(*) @@ -146,19 +148,26 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { SELECT ch.cafe_time + $2 FROM characters ch WHERE ch.id = $1 - ) >= seconds_req LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) - cafeBonus := &CafeBonus{} - err := row.StructScan(cafeBonus) + ) >= time_req`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) if err != nil { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 8)) - return + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + } else { + for rows.Next() { + cafeBonus := &CafeBonus{} + err = rows.StructScan(cafeBonus) + if err != nil { + continue + } + count++ + bf.WriteUint32(cafeBonus.ID) + bf.WriteUint32(cafeBonus.ItemType) + bf.WriteUint32(cafeBonus.ItemID) + bf.WriteUint32(cafeBonus.Quantity) + } + bf.Seek(0, io.SeekStart) + bf.WriteUint32(count) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } - bf.WriteUint32(1) - bf.WriteUint32(cafeBonus.ID) - bf.WriteUint32(cafeBonus.ItemType) - bf.WriteUint32(cafeBonus.ItemID) - bf.WriteUint32(cafeBonus.Quantity) - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) { From d8072ee06e70ff8abf405e055fee6c4fd1e8d9a2 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 18:22:56 +1000 Subject: [PATCH 07/36] default netcafe rewards and maintenance --- main.go | 4 +++- patch-schema/netcafe.sql | 12 +++++++++++- server/channelserver/handlers_cafe.go | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index d0b13d649..c3c0162e0 100644 --- a/main.go +++ b/main.go @@ -108,9 +108,11 @@ func main() { } logger.Info("Connected to database") - // Clear existing tokens + // Clear stale data _ = db.MustExec("DELETE FROM sign_sessions") _ = db.MustExec("DELETE FROM servers") + _ = db.MustExec("DELETE FROM cafe_accepted") + _ = db.MustExec("UPDATE characters SET cafe_time=0") // Clean the DB if the option is on. if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB { diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index c7aeb9460..077adc7bd 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -5,7 +5,7 @@ ALTER TABLE IF EXISTS public.characters CREATE TABLE IF NOT EXISTS public.cafebonus ( - id integer NOT NULL PRIMARY KEY, + id serial NOT NULL PRIMARY KEY, time_req integer NOT NULL, item_type integer NOT NULL, item_id integer NOT NULL, @@ -18,4 +18,14 @@ CREATE TABLE IF NOT EXISTS public.cafe_accepted character_id integer NOT NULL ); +INSERT INTO public.cafebonus (time_req, item_type, item_id, quantity) +VALUES + (1800, 17, 0, 250), + (3600, 17, 0, 500), + (7200, 17, 0, 1000), + (10800, 17, 0, 1500), + (18000, 17, 0, 1750), + (28800, 17, 0, 3000), + (43200, 17, 0, 4000); + END; \ No newline at end of file diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index cfac96cc7..a020d38a0 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -118,7 +118,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { s.logger.Error("Error scanning cafebonus", zap.Error(err)) } bf.WriteUint32(cafeBonus.TimeReq) - bf.WriteUint32(0) // Unk + bf.WriteUint32(cafeBonus.ItemType) bf.WriteUint32(cafeBonus.ItemID) bf.WriteUint32(cafeBonus.Quantity) bf.WriteBool(cafeBonus.Claimed) From 440b73f69658fdae79430fa3162d258ba01b66f1 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 21:23:02 +1000 Subject: [PATCH 08/36] implement netcafe points and boost time --- ...g_mhf_post_cafe_duration_bonus_received.go | 9 +-- patch-schema/netcafe.sql | 11 +++- server/channelserver/handlers_cafe.go | 66 +++++++++++++++++-- server/channelserver/handlers_event.go | 26 -------- 4 files changed, 74 insertions(+), 38 deletions(-) diff --git a/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go b/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go index be8947f2c..baa102f61 100644 --- a/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go +++ b/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go @@ -11,8 +11,7 @@ import ( // MsgMhfPostCafeDurationBonusReceived represents the MSG_MHF_POST_CAFE_DURATION_BONUS_RECEIVED type MsgMhfPostCafeDurationBonusReceived struct { AckHandle uint32 - Unk0 uint32 - CafeBonusID uint32 + CafeBonusID []uint32 } // Opcode returns the ID associated with this packet type. @@ -23,8 +22,10 @@ func (m *MsgMhfPostCafeDurationBonusReceived) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPostCafeDurationBonusReceived) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() - m.CafeBonusID = bf.ReadUint32() + ids := int(bf.ReadUint32()) + for i := 0; i < ids; i++ { + m.CafeBonusID = append(m.CafeBonusID, bf.ReadUint32()) + } return nil } diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index 077adc7bd..e284742ce 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -1,7 +1,16 @@ BEGIN; ALTER TABLE IF EXISTS public.characters - ADD COLUMN cafe_time integer DEFAULT 0; + ADD COLUMN IF NOT EXISTS cafe_time integer DEFAULT 0; + +ALTER TABLE IF EXISTS public.characters + DROP COLUMN IF EXISTS netcafe_points; + +ALTER TABLE IF EXISTS public.characters + ADD COLUMN IF NOT EXISTS netcafe_points int DEFAULT 0; + +ALTER TABLE IF EXISTS public.characters + ADD COLUMN IF NOT EXISTS boost_time timestamp without time zone; CREATE TABLE IF NOT EXISTS public.cafebonus ( diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index a020d38a0..041304ed7 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -29,7 +29,6 @@ func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) { s.logger.Fatal("Failed to get netcate points from db", zap.Error(err)) } resp := byteframe.NewByteFrame() - resp.WriteUint32(0) resp.WriteUint32(netcafePoints) doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) } @@ -58,7 +57,7 @@ func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) { if t.After(dailyTime) { // +5 netcafe points and setting next valid window - _, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points::int + 5 WHERE id=$2", midday, s.charID) + _, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points+5 WHERE id=$2", midday, s.charID) if err != nil { s.logger.Fatal("Failed to update daily_time and netcafe_points savedata in db", zap.Error(err)) } @@ -172,16 +171,69 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfPostCafeDurationBonusReceived) - - _, err := s.server.db.Exec("INSERT INTO public.cafe_accepted VALUES ($1, $2)", pkt.CafeBonusID, s.charID) - if err != nil { - s.logger.Error("Error updating accepted CafeBonus count", zap.Error(err)) + var cafeBonus CafeBonus + for _, cbID := range pkt.CafeBonusID { + err := s.server.db.QueryRow(` + SELECT cb.id, item_type, quantity FROM cafebonus cb WHERE cb.id=$1 + `, cbID).Scan(&cafeBonus.ID, &cafeBonus.ItemType, &cafeBonus.Quantity) + if err == nil { + if cafeBonus.ItemType == 17 { + s.server.db.Exec("UPDATE characters SET netcafe_points=netcafe_points+$1 WHERE id=$2", cafeBonus.Quantity, s.charID) + } + } + s.server.db.Exec("INSERT INTO public.cafe_accepted VALUES ($1, $2)", cbID, s.charID) } - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStartBoostTime) + bf := byteframe.NewByteFrame() + boostLimit := Time_Current_Adjusted().Add(100 * time.Minute) + s.server.db.Exec("UPDATE characters SET boost_time=$1 WHERE id=$2", boostLimit, s.charID) + bf.WriteUint32(uint32(boostLimit.Unix())) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} + +func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetBoostTime) + doAckBufSucceed(s, pkt.AckHandle, []byte{}) +} + +func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit) + bf := byteframe.NewByteFrame() + var boostLimit time.Time + err := s.server.db.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.charID).Scan(&boostLimit) + if err != nil { + bf.WriteUint32(0) + } else { + bf.WriteUint32(uint32(boostLimit.Unix())) + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } + +func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetBoostRight) + var boostLimit time.Time + err := s.server.db.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.charID).Scan(&boostLimit) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + return + } + if boostLimit.Unix() < Time_Current_Adjusted().Unix() { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02}) + } else { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + } +} + +func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPostBoostTimeQuestReturn) + doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) +} + +func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {} + +func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index 87ae246af..7ec7c30e7 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -303,32 +303,6 @@ func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } -func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetBoostTime) - - doAckBufSucceed(s, pkt.AckHandle, []byte{}) - updateRights(s) -} - -func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit) - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) -} - -func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetBoostRight) - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) -} - -func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfPostBoostTimeQuestReturn) - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) -} - -func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {} - func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) { From 0edfd255fdd1d10ad22b374a04d2cc36d44ecebc Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 21:46:05 +1000 Subject: [PATCH 09/36] correct backwards comparison --- server/channelserver/handlers_cafe.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 041304ed7..c7d826429 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -222,10 +222,10 @@ func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) return } - if boostLimit.Unix() < Time_Current_Adjusted().Unix() { - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02}) - } else { + if boostLimit.Unix() > Time_Current_Adjusted().Unix() { doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + } else { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02}) } } From 12ac12ff3f7375c5001c8760d27e0420747a7415 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 09:49:35 +1000 Subject: [PATCH 10/36] stub mercenary data --- server/channelserver/handlers_mercenary.go | 24 ++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index 37ce9e628..ca3dd2a71 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -121,10 +121,30 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfMercenaryHuntdata(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfMercenaryHuntdata) - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x0A)) + if pkt.Unk0 == 1 { + // Format: + // uint8 Hunts + // struct Hunt + // uint32 HuntID + // uint32 MonID + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) + } else { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0)) + } } -func handleMsgMhfEnumerateMercenaryLog(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateMercenaryLog(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateMercenaryLog) + bf := byteframe.NewByteFrame() + bf.WriteUint32(0) + // Format: + // struct Log + // uint32 Timestamp + // []byte Name (len 18) + // uint8 Unk + // uint8 Unk + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfCreateMercenary) From 6ab961fa90625addb4ad3b1539bb6dc0d9ced46c Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 17:38:25 +1000 Subject: [PATCH 11/36] initial stamp implementation --- .../mhfpacket/msg_mhf_check_weekly_stamp.go | 19 ++++---- .../msg_mhf_exchange_weekly_stamp.go | 27 +++++++++--- patch-schema/stamps.sql | 13 ++++++ server/channelserver/handlers.go | 44 ++++++++++++++----- server/channelserver/sys_timefix.go | 10 +++++ 5 files changed, 88 insertions(+), 25 deletions(-) create mode 100644 patch-schema/stamps.sql diff --git a/network/mhfpacket/msg_mhf_check_weekly_stamp.go b/network/mhfpacket/msg_mhf_check_weekly_stamp.go index 83f6e5ce8..f03b1d1e7 100644 --- a/network/mhfpacket/msg_mhf_check_weekly_stamp.go +++ b/network/mhfpacket/msg_mhf_check_weekly_stamp.go @@ -1,15 +1,16 @@ package mhfpacket import ( + "errors" + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfCheckWeeklyStamp represents the MSG_MHF_CHECK_WEEKLY_STAMP type MsgMhfCheckWeeklyStamp struct { AckHandle uint32 - Unk0 uint8 + StampType string Unk1 bool Unk2 uint16 // Hardcoded 0 in the binary } @@ -22,7 +23,13 @@ func (m *MsgMhfCheckWeeklyStamp) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfCheckWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() + stampType := bf.ReadUint8() + switch stampType { + case 1: + m.StampType = "hl" + case 2: + m.StampType = "ex" + } m.Unk1 = bf.ReadBool() m.Unk2 = bf.ReadUint16() return nil @@ -30,9 +37,5 @@ func (m *MsgMhfCheckWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.C // Build builds a binary packet from the current data. func (m *MsgMhfCheckWeeklyStamp) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - bf.WriteUint32(m.AckHandle) - bf.WriteUint8(m.Unk0) - bf.WriteBool(m.Unk1) - bf.WriteUint16(m.Unk2) - return nil + return errors.New("NOT IMPLEMENTED") } diff --git a/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go b/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go index 6cce19147..758925936 100644 --- a/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go +++ b/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go @@ -1,15 +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" ) // MsgMhfExchangeWeeklyStamp represents the MSG_MHF_EXCHANGE_WEEKLY_STAMP -type MsgMhfExchangeWeeklyStamp struct{} +type MsgMhfExchangeWeeklyStamp struct { + AckHandle uint32 + StampType string + Unk1 bool + Unk2 uint16 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfExchangeWeeklyStamp) Opcode() network.PacketID { @@ -18,7 +23,17 @@ func (m *MsgMhfExchangeWeeklyStamp) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfExchangeWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + stampType := bf.ReadUint8() + switch stampType { + case 1: + m.StampType = "hl" + case 2: + m.StampType = "ex" + } + m.Unk1 = bf.ReadBool() + m.Unk2 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/patch-schema/stamps.sql b/patch-schema/stamps.sql new file mode 100644 index 000000000..2b940fa8c --- /dev/null +++ b/patch-schema/stamps.sql @@ -0,0 +1,13 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS public.stamps ( + character_id integer PRIMARY KEY, + hl_total integer DEFAULT 0, + hl_redeemed integer DEFAULT 0, + hl_next timestamp without time zone, + ex_total integer DEFAULT 0, + ex_redeemed integer DEFAULT 0, + ex_next timestamp without time zone +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 26447a2fb..fa2bbf2f4 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -646,19 +646,41 @@ func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp) - - resp := byteframe.NewByteFrame() - resp.WriteUint16(0x000E) - resp.WriteUint16(0x0001) - resp.WriteUint16(0x0000) - resp.WriteUint16(0x0000) // 0x0000 stops the vaguely annoying log in pop up - resp.WriteUint32(0) - resp.WriteUint32(0x5dddcbb3) // Timestamp - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + weekCurrentStart := TimeWeekStart() + weekNextStart := TimeWeekNext() + var total, redeemed, updated uint16 + var nextClaim time.Time + err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_next FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&nextClaim) + if err != nil { + s.server.db.Exec("INSERT INTO stamps (character_id, hl_next, ex_next) VALUES ($1, $2, $2)", s.charID, weekNextStart) + nextClaim = weekNextStart + } + if nextClaim.Before(weekCurrentStart) { + s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1, %s_next=$1 WHERE character_id=$2", pkt.StampType, pkt.StampType, pkt.StampType), weekNextStart, s.charID) + updated = 1 + } + s.server.db.QueryRow(fmt.Sprintf("SELECT %s_total, %s_redeemed FROM stamps WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) + bf := byteframe.NewByteFrame() + bf.WriteUint16(total) + bf.WriteUint16(redeemed) + bf.WriteUint16(updated) + bf.WriteUint32(0) // Unk + bf.WriteUint32(uint32(weekCurrentStart.Unix())) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } -func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp) + var total, redeemed uint16 + s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) + bf := byteframe.NewByteFrame() + bf.WriteUint16(total) + bf.WriteUint16(redeemed) + bf.WriteUint16(0) + bf.WriteUint32(0) // Unk, but has possible values + bf.WriteUint32(uint32(TimeWeekStart().Unix())) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} func getGookData(s *Session, cid uint32) (uint16, []byte) { var data []byte diff --git a/server/channelserver/sys_timefix.go b/server/channelserver/sys_timefix.go index 839382053..4cde6a319 100644 --- a/server/channelserver/sys_timefix.go +++ b/server/channelserver/sys_timefix.go @@ -31,6 +31,16 @@ func Time_Current_Midnight() time.Time { return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location()) } +func TimeWeekStart() time.Time { + midnight := Time_Current_Midnight() + offset := (int(midnight.Weekday()) - 1) * -24 + return midnight.Add(time.Hour * time.Duration(offset)) +} + +func TimeWeekNext() time.Time { + return TimeWeekStart().Add(time.Hour * 24 * 7) +} + func Time_Current_Week_uint8() uint8 { baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust) From cecfb1348dd17ec52945a113924a27a5a57ac525 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 17:39:25 +1000 Subject: [PATCH 12/36] add missing import --- server/channelserver/handlers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index fa2bbf2f4..1e5d77e33 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -9,6 +9,7 @@ import ( "io" "net" "strings" + "time" "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" From d48a64e20828e1b1c5cbe94ee421502917a5d578 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 18:09:06 +1000 Subject: [PATCH 13/36] rename rights variable --- network/mhfpacket/msg_sys_update_right.go | 4 ++-- server/channelserver/handlers.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/network/mhfpacket/msg_sys_update_right.go b/network/mhfpacket/msg_sys_update_right.go index 3a7c95f4e..b343dd0c4 100644 --- a/network/mhfpacket/msg_sys_update_right.go +++ b/network/mhfpacket/msg_sys_update_right.go @@ -37,7 +37,7 @@ type ClientRight struct { // MsgSysUpdateRight represents the MSG_SYS_UPDATE_RIGHT type MsgSysUpdateRight struct { ClientRespAckHandle uint32 // If non-0, requests the client to send back a MSG_SYS_ACK packet with this value. - Unk1 uint32 + Bitfield uint32 Rights []ClientRight UnkSize uint16 // Count of some buf up to 0x800 bytes following it. } @@ -55,7 +55,7 @@ func (m *MsgSysUpdateRight) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client // Build builds a binary packet from the current data. func (m *MsgSysUpdateRight) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { bf.WriteUint32(m.ClientRespAckHandle) - bf.WriteUint32(m.Unk1) + bf.WriteUint32(m.Bitfield) bf.WriteUint16(uint16(len(m.Rights))) bf.WriteUint16(0) for _, v := range m.Rights { diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 1e5d77e33..5af8abb20 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -79,7 +79,7 @@ func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) { func updateRights(s *Session) { update := &mhfpacket.MsgSysUpdateRight{ ClientRespAckHandle: 0, - Unk1: s.rights, + Bitfield: s.rights, Rights: []mhfpacket.ClientRight{ { ID: 1, From d7cc7ab5e34943ba0fa8268d095149b9398abe44 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 18:52:53 +1000 Subject: [PATCH 14/36] fix invalid semaphore id for raviente support --- server/channelserver/handlers_register.go | 14 +++++++------- server/channelserver/handlers_semaphore.go | 12 +++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/server/channelserver/handlers_register.go b/server/channelserver/handlers_register.go index 3dd65cf6a..2c44cc6db 100644 --- a/server/channelserver/handlers_register.go +++ b/server/channelserver/handlers_register.go @@ -11,7 +11,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) s.server.raviente.Lock() switch pkt.SemaphoreID { - case 3: + case 4: resp := byteframe.NewByteFrame() size := 6 for i := 0; i < len(bf.Data())-1; i += size { @@ -49,7 +49,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) { } resp.WriteUint8(0) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - case 4: + case 5: resp := byteframe.NewByteFrame() size := 6 for i := 0; i < len(bf.Data())-1; i += size { @@ -74,7 +74,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) { } resp.WriteUint8(0) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - case 5: + case 6: resp := byteframe.NewByteFrame() size := 6 for i := 0; i < len(bf.Data())-1; i += size { @@ -242,15 +242,15 @@ func handleMsgSysLoadRegister(s *Session, p mhfpacket.MHFPacket) { func (s *Session) notifyRavi() { var temp mhfpacket.MHFPacket raviNotif := byteframe.NewByteFrame() - temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 3} - raviNotif.WriteUint16(uint16(temp.Opcode())) - temp.Build(raviNotif, s.clientContext) temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 4} raviNotif.WriteUint16(uint16(temp.Opcode())) temp.Build(raviNotif, s.clientContext) temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 5} raviNotif.WriteUint16(uint16(temp.Opcode())) temp.Build(raviNotif, s.clientContext) + temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 6} + raviNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(raviNotif, s.clientContext) raviNotif.WriteUint16(0x0010) // End it. sema := getRaviSemaphore(s) if sema != "" { @@ -262,7 +262,7 @@ func (s *Session) notifyRavi() { func getRaviSemaphore(s *Session) string { for _, semaphore := range s.server.semaphore { - if strings.HasPrefix(semaphore.id_semaphore, "hs_l0u3B5") && strings.HasSuffix(semaphore.id_semaphore, "3") { + if strings.HasPrefix(semaphore.id_semaphore, "hs_l0u3B5") && strings.HasSuffix(semaphore.id_semaphore, "4") { return semaphore.id_semaphore } } diff --git a/server/channelserver/handlers_semaphore.go b/server/channelserver/handlers_semaphore.go index 95af79314..cbf1a0a7f 100644 --- a/server/channelserver/handlers_semaphore.go +++ b/server/channelserver/handlers_semaphore.go @@ -45,12 +45,10 @@ func destructEmptySemaphores(s *Session) { } func releaseRaviSemaphore(s *Session, sema *Semaphore) { - if !strings.HasSuffix(sema.id_semaphore, "5") { - delete(sema.reservedClientSlots, s.charID) - delete(sema.clients, s) - } - if len(sema.reservedClientSlots) == 0 && len(sema.clients) == 0 { - s.logger.Debug("Raviente semaphore is empty, resetting") + delete(sema.reservedClientSlots, s.charID) + delete(sema.clients, s) + if strings.HasSuffix(sema.id_semaphore, "2") && len(sema.clients) == 0 { + s.logger.Debug("Main raviente semaphore is empty, resetting") resetRavi(s) } } @@ -91,7 +89,7 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { suffix, _ := strconv.ParseUint(pkt.SemaphoreID[len(pkt.SemaphoreID)-1:], 10, 32) s.server.semaphore[SemaphoreID] = &Semaphore{ id_semaphore: pkt.SemaphoreID, - id: uint32(suffix), + id: uint32(suffix + 1), clients: make(map[*Session]uint32), reservedClientSlots: make(map[uint32]interface{}), maxPlayers: 32, From 7a303d6e31d2a0f7d407b9641ea8f201d7a0d449 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 19:03:11 +1000 Subject: [PATCH 15/36] prevent use of reserved semaphore indexes --- server/channelserver/sys_channel_server.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index a23ec2834..ca9063baf 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -154,7 +154,7 @@ func NewServer(config *Config) *Server { stages: make(map[string]*Stage), userBinaryParts: make(map[userBinaryPartID][]byte), semaphore: make(map[string]*Semaphore), - semaphoreIndex: 5, + semaphoreIndex: 7, discordBot: config.DiscordBot, name: config.Name, enable: config.Enable, @@ -413,6 +413,9 @@ func (s *Server) NextSemaphoreID() uint32 { for { exists := false s.semaphoreIndex = s.semaphoreIndex + 1 + if s.semaphoreIndex == 0 { + s.semaphoreIndex = 7 // Skip reserved indexes + } for _, semaphore := range s.semaphore { if semaphore.id == s.semaphoreIndex { exists = true From 86cb254d1a5fe5205909783bef7f6157a94b07a5 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 15 Aug 2022 15:12:50 +1000 Subject: [PATCH 16/36] initial warehouse concept --- .../mhfpacket/msg_mhf_enumerate_warehouse.go | 20 ++-- .../mhfpacket/msg_mhf_operate_warehouse.go | 32 ++++-- network/mhfpacket/msg_mhf_update_warehouse.go | 37 +++++-- patch-schema/warehouse.sql | 49 ++++++++++ server/channelserver/handlers_house.go | 98 ++++++++++++++++++- 5 files changed, 215 insertions(+), 21 deletions(-) create mode 100644 patch-schema/warehouse.sql diff --git a/network/mhfpacket/msg_mhf_enumerate_warehouse.go b/network/mhfpacket/msg_mhf_enumerate_warehouse.go index f567e8bcb..7a5e76a7b 100644 --- a/network/mhfpacket/msg_mhf_enumerate_warehouse.go +++ b/network/mhfpacket/msg_mhf_enumerate_warehouse.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" ) // MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE -type MsgMhfEnumerateWarehouse struct{} +type MsgMhfEnumerateWarehouse struct { + AckHandle uint32 + BoxType uint8 + BoxIndex uint8 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID { @@ -18,7 +22,11 @@ func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.BoxType = bf.ReadUint8() + m.BoxIndex = bf.ReadUint8() + _ = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_operate_warehouse.go b/network/mhfpacket/msg_mhf_operate_warehouse.go index 9df4e3770..ba0ee7b78 100644 --- a/network/mhfpacket/msg_mhf_operate_warehouse.go +++ b/network/mhfpacket/msg_mhf_operate_warehouse.go @@ -1,15 +1,22 @@ package mhfpacket -import ( - "errors" +import ( + "errors" + "erupe-ce/common/stringsupport" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfOperateWarehouse represents the MSG_MHF_OPERATE_WAREHOUSE -type MsgMhfOperateWarehouse struct{} +type MsgMhfOperateWarehouse struct { + AckHandle uint32 + Operation uint8 + BoxType string + BoxIndex uint8 + Name string +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID { @@ -18,7 +25,20 @@ func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfOperateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Operation = bf.ReadUint8() + boxType := bf.ReadUint8() + switch boxType { + case 0: + m.BoxType = "item" + case 1: + m.BoxType = "equip" + } + m.BoxIndex = bf.ReadUint8() + _ = bf.ReadUint8() // lenName + _ = bf.ReadUint16() // Unk + m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_update_warehouse.go b/network/mhfpacket/msg_mhf_update_warehouse.go index ba0321910..884ed4119 100644 --- a/network/mhfpacket/msg_mhf_update_warehouse.go +++ b/network/mhfpacket/msg_mhf_update_warehouse.go @@ -1,15 +1,27 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) +type UpdatedStack struct { + ID uint32 + Index uint16 + ItemID uint16 + Quantity uint16 + Unk uint16 +} + // MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE -type MsgMhfUpdateWarehouse struct{} +type MsgMhfUpdateWarehouse struct { + AckHandle uint32 + BoxID uint16 + Items []UpdatedStack +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID { @@ -18,7 +30,20 @@ func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.BoxID = bf.ReadUint16() + changes := int(bf.ReadUint16()) + var stackUpdate UpdatedStack + for i := 0; i < changes; i++ { + stackUpdate.ID = bf.ReadUint32() + stackUpdate.Index = bf.ReadUint16() + stackUpdate.ItemID = bf.ReadUint16() + stackUpdate.Quantity = bf.ReadUint16() + stackUpdate.Unk = bf.ReadUint16() + m.Items = append(m.Items, stackUpdate) + } + _ = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/patch-schema/warehouse.sql b/patch-schema/warehouse.sql new file mode 100644 index 000000000..2f2a5adde --- /dev/null +++ b/patch-schema/warehouse.sql @@ -0,0 +1,49 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS public.warehouse ( + character_id integer PRIMARY KEY, + item0 bytea, + item1 bytea, + item2 bytea, + item3 bytea, + item4 bytea, + item5 bytea, + item6 bytea, + item7 bytea, + item8 bytea, + item9 bytea, + item10 bytea, + item0name text, + item1name text, + item2name text, + item3name text, + item4name text, + item5name text, + item6name text, + item7name text, + item8name text, + item9name text, + equip0 bytea, + equip1 bytea, + equip2 bytea, + equip3 bytea, + equip4 bytea, + equip5 bytea, + equip6 bytea, + equip7 bytea, + equip8 bytea, + equip9 bytea, + equip10 bytea, + equip0name text, + equip1name text, + equip2name text, + equip3name text, + equip4name text, + equip5name text, + equip6name text, + equip7name text, + equip8name text, + equip9name text +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 401347b76..b7bdd78bf 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -5,11 +5,37 @@ import ( ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" "erupe-ce/network/mhfpacket" + "fmt" "go.uber.org/zap" "io" "time" ) +const warehouseNamesQuery = ` +SELECT +COALESCE(item0name, ''), +COALESCE(item1name, ''), +COALESCE(item2name, ''), +COALESCE(item3name, ''), +COALESCE(item4name, ''), +COALESCE(item5name, ''), +COALESCE(item6name, ''), +COALESCE(item7name, ''), +COALESCE(item8name, ''), +COALESCE(item9name, ''), +COALESCE(equip0name, ''), +COALESCE(equip1name, ''), +COALESCE(equip2name, ''), +COALESCE(equip3name, ''), +COALESCE(equip4name, ''), +COALESCE(equip5name, ''), +COALESCE(equip6name, ''), +COALESCE(equip7name, ''), +COALESCE(equip8name, ''), +COALESCE(equip9name, '') +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) @@ -341,8 +367,74 @@ func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfOperateWarehouse) + bf := byteframe.NewByteFrame() + bf.WriteUint8(pkt.Operation) + switch pkt.Operation { + case 0: + var count uint8 + itemNames := make([]string, 10) + equipNames := make([]string, 10) + s.server.db.QueryRow(fmt.Sprintf("%s WHERE character_id=$1", warehouseNamesQuery), s.charID).Scan(&itemNames[0], + &itemNames[1], &itemNames[2], &itemNames[3], &itemNames[4], &itemNames[5], &itemNames[6], &itemNames[7], &itemNames[8], &itemNames[9], &equipNames[0], + &equipNames[1], &equipNames[2], &equipNames[3], &equipNames[4], &equipNames[5], &equipNames[6], &equipNames[7], &equipNames[8], &equipNames[9]) + bf.WriteUint32(0) + bf.WriteUint16(1000) // Usages + temp := byteframe.NewByteFrame() + for i, name := range itemNames { + if len(name) > 0 { + count++ + temp.WriteUint8(0) + temp.WriteUint8(uint8(i)) + ps.Uint8(temp, name, true) + } + } + for i, name := range equipNames { + if len(name) > 0 { + count++ + temp.WriteUint8(1) + temp.WriteUint8(uint8(i)) + ps.Uint8(temp, name, true) + } + } + bf.WriteUint8(count) + bf.WriteBytes(temp.Data()) + case 1: + bf.WriteUint8(0) + case 2: + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%dname=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), pkt.Name, s.charID) + case 3: + var t int + err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t) + if err != nil { + s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID) + } + bf.WriteUint32(0) + bf.WriteUint16(1000) // Usages + case 4: + bf.WriteUint32(0) + bf.WriteUint16(1000) // Usages + bf.WriteUint8(0) + } + // Opcodes + // 0 = Get box names + // 1 = Commit usage + // 2 = Rename + // 3 = Get usage limit + // 4 = Get gift box names (doesn't do anything?) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} -func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse) + bf := byteframe.NewByteFrame() + bf.WriteUint16(0) // numStacks + bf.WriteUint16(0) // Unk + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} -func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} From b7d41c1c7f0e829095095861148aa48134c476e0 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 15 Aug 2022 20:09:19 +1000 Subject: [PATCH 17/36] warehouse item updates --- .../mhfpacket/msg_mhf_enumerate_warehouse.go | 10 ++- network/mhfpacket/msg_mhf_update_warehouse.go | 24 ++++--- server/channelserver/handlers_house.go | 72 +++++++++++++++++-- 3 files changed, 91 insertions(+), 15 deletions(-) diff --git a/network/mhfpacket/msg_mhf_enumerate_warehouse.go b/network/mhfpacket/msg_mhf_enumerate_warehouse.go index 7a5e76a7b..3f1358045 100644 --- a/network/mhfpacket/msg_mhf_enumerate_warehouse.go +++ b/network/mhfpacket/msg_mhf_enumerate_warehouse.go @@ -11,7 +11,7 @@ import ( // MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE type MsgMhfEnumerateWarehouse struct { AckHandle uint32 - BoxType uint8 + BoxType string BoxIndex uint8 } @@ -23,7 +23,13 @@ func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.BoxType = bf.ReadUint8() + boxType := bf.ReadUint8() + switch boxType { + case 0: + m.BoxType = "item" + case 1: + m.BoxType = "equip" + } m.BoxIndex = bf.ReadUint8() _ = bf.ReadUint16() return nil diff --git a/network/mhfpacket/msg_mhf_update_warehouse.go b/network/mhfpacket/msg_mhf_update_warehouse.go index 884ed4119..b6777b8d6 100644 --- a/network/mhfpacket/msg_mhf_update_warehouse.go +++ b/network/mhfpacket/msg_mhf_update_warehouse.go @@ -2,25 +2,24 @@ package mhfpacket import ( "errors" - "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" ) -type UpdatedStack struct { +type WarehouseStack struct { ID uint32 Index uint16 ItemID uint16 Quantity uint16 - Unk uint16 } // MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE type MsgMhfUpdateWarehouse struct { AckHandle uint32 - BoxID uint16 - Items []UpdatedStack + BoxType string + BoxIndex uint8 + Updates []WarehouseStack } // Opcode returns the ID associated with this packet type. @@ -31,16 +30,23 @@ func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.BoxID = bf.ReadUint16() + boxType := bf.ReadUint8() + switch boxType { + case 0: + m.BoxType = "item" + case 1: + m.BoxType = "equip" + } + m.BoxIndex = bf.ReadUint8() changes := int(bf.ReadUint16()) - var stackUpdate UpdatedStack + var stackUpdate WarehouseStack for i := 0; i < changes; i++ { stackUpdate.ID = bf.ReadUint32() stackUpdate.Index = bf.ReadUint16() stackUpdate.ItemID = bf.ReadUint16() stackUpdate.Quantity = bf.ReadUint16() - stackUpdate.Unk = bf.ReadUint16() - m.Items = append(m.Items, stackUpdate) + _ = bf.ReadUint16() // Unk + m.Updates = append(m.Updates, stackUpdate) } _ = bf.ReadUint16() return nil diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index b7bdd78bf..1059b7a72 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -426,15 +426,79 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack { + var data []byte + s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data) + if len(data) > 0 { + box := byteframe.NewByteFrameFromBytes(data) + numStacks := box.ReadUint16() + stacks := make([]mhfpacket.WarehouseStack, numStacks) + for i := 0; i < int(numStacks); i++ { + stacks[i].ID = box.ReadUint32() + stacks[i].Index = box.ReadUint16() + stacks[i].ItemID = box.ReadUint16() + stacks[i].Quantity = box.ReadUint16() + box.ReadUint16() + } + return stacks + } else { + return make([]mhfpacket.WarehouseStack, 0) + } +} + +func boxToBytes(stacks []mhfpacket.WarehouseStack) []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(len(stacks))) + for _, stack := range stacks { + bf.WriteUint32(stack.ID) + bf.WriteUint16(stack.Index) + bf.WriteUint16(stack.ItemID) + bf.WriteUint16(stack.Quantity) + bf.WriteUint16(0) + } + bf.WriteUint16(0) + return bf.Data() +} + func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse) - bf := byteframe.NewByteFrame() - bf.WriteUint16(0) // numStacks - bf.WriteUint16(0) // Unk - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex) + if len(box) > 0 { + doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box)) + } else { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + } } func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse) + box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex) + // Update existing stacks + var newStacks []mhfpacket.WarehouseStack + for _, update := range pkt.Updates { + exists := false + for i, stack := range box { + if stack.ItemID == update.ItemID { + box[i].Quantity = update.Quantity + exists = true + break + } + } + if exists == false { + newStacks = append(newStacks, update) + } + } + // Append new stacks + for _, stack := range newStacks { + box = append(box, stack) + } + // Slice empty stacks + var cleanedBox []mhfpacket.WarehouseStack + for _, stack := range box { + if stack.Quantity > 0 { + cleanedBox = append(cleanedBox, stack) + } + } + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox), s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } From 81e40b988d38d80b53db3bfe9f38fc5e5e9c4de6 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 15 Aug 2022 21:53:00 +1000 Subject: [PATCH 18/36] warehouse equipment updates --- network/mhfpacket/msg_mhf_update_warehouse.go | 32 +++++--- server/channelserver/handlers_house.go | 74 +++++++++++++------ 2 files changed, 75 insertions(+), 31 deletions(-) diff --git a/network/mhfpacket/msg_mhf_update_warehouse.go b/network/mhfpacket/msg_mhf_update_warehouse.go index b6777b8d6..962906988 100644 --- a/network/mhfpacket/msg_mhf_update_warehouse.go +++ b/network/mhfpacket/msg_mhf_update_warehouse.go @@ -8,10 +8,12 @@ import ( ) type WarehouseStack struct { - ID uint32 - Index uint16 - ItemID uint16 - Quantity uint16 + ID uint32 + Index uint16 + EquipType uint16 + ItemID uint16 + Quantity uint16 + Data []byte } // MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE @@ -41,12 +43,22 @@ func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl changes := int(bf.ReadUint16()) var stackUpdate WarehouseStack for i := 0; i < changes; i++ { - stackUpdate.ID = bf.ReadUint32() - stackUpdate.Index = bf.ReadUint16() - stackUpdate.ItemID = bf.ReadUint16() - stackUpdate.Quantity = bf.ReadUint16() - _ = bf.ReadUint16() // Unk - m.Updates = append(m.Updates, stackUpdate) + switch boxType { + case 0: + stackUpdate.ID = bf.ReadUint32() + stackUpdate.Index = bf.ReadUint16() + stackUpdate.ItemID = bf.ReadUint16() + stackUpdate.Quantity = bf.ReadUint16() + _ = bf.ReadUint16() // Unk + m.Updates = append(m.Updates, stackUpdate) + case 1: + stackUpdate.ID = bf.ReadUint32() + stackUpdate.Index = bf.ReadUint16() + stackUpdate.EquipType = bf.ReadUint16() + stackUpdate.ItemID = bf.ReadUint16() + stackUpdate.Data = bf.ReadBytes(56) + m.Updates = append(m.Updates, stackUpdate) + } } _ = bf.ReadUint16() return nil diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 1059b7a72..4e6af6947 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -434,11 +434,19 @@ func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.War numStacks := box.ReadUint16() stacks := make([]mhfpacket.WarehouseStack, numStacks) for i := 0; i < int(numStacks); i++ { - stacks[i].ID = box.ReadUint32() - stacks[i].Index = box.ReadUint16() - stacks[i].ItemID = box.ReadUint16() - stacks[i].Quantity = box.ReadUint16() - box.ReadUint16() + if boxType == "item" { + stacks[i].ID = box.ReadUint32() + stacks[i].Index = box.ReadUint16() + stacks[i].ItemID = box.ReadUint16() + stacks[i].Quantity = box.ReadUint16() + box.ReadUint16() + } else { + stacks[i].ID = box.ReadUint32() + stacks[i].Index = box.ReadUint16() + stacks[i].EquipType = box.ReadUint16() + stacks[i].ItemID = box.ReadUint16() + stacks[i].Data = box.ReadBytes(56) + } } return stacks } else { @@ -446,15 +454,23 @@ func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.War } } -func boxToBytes(stacks []mhfpacket.WarehouseStack) []byte { +func boxToBytes(stacks []mhfpacket.WarehouseStack, boxType string) []byte { bf := byteframe.NewByteFrame() bf.WriteUint16(uint16(len(stacks))) - for _, stack := range stacks { - bf.WriteUint32(stack.ID) - bf.WriteUint16(stack.Index) - bf.WriteUint16(stack.ItemID) - bf.WriteUint16(stack.Quantity) - bf.WriteUint16(0) + for i, stack := range stacks { + if boxType == "item" { + bf.WriteUint32(stack.ID) + bf.WriteUint16(uint16(i + 1)) + bf.WriteUint16(stack.ItemID) + bf.WriteUint16(stack.Quantity) + bf.WriteUint16(0) + } else { + bf.WriteUint32(stack.ID) + bf.WriteUint16(uint16(i + 1)) + bf.WriteUint16(stack.EquipType) + bf.WriteUint16(stack.ItemID) + bf.WriteBytes(stack.Data) + } } bf.WriteUint16(0) return bf.Data() @@ -464,7 +480,7 @@ func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse) box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex) if len(box) > 0 { - doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box)) + doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box, pkt.BoxType)) } else { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -477,11 +493,21 @@ func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) { var newStacks []mhfpacket.WarehouseStack for _, update := range pkt.Updates { exists := false - for i, stack := range box { - if stack.ItemID == update.ItemID { - box[i].Quantity = update.Quantity - exists = true - break + if pkt.BoxType == "item" { + for i, stack := range box { + if stack.Index == update.Index { + exists = true + box[i].Quantity = update.Quantity + break + } + } + } else { + for i, stack := range box { + if stack.Index == update.Index { + exists = true + box[i].ItemID = update.ItemID + break + } } } if exists == false { @@ -495,10 +521,16 @@ func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) { // Slice empty stacks var cleanedBox []mhfpacket.WarehouseStack for _, stack := range box { - if stack.Quantity > 0 { - cleanedBox = append(cleanedBox, stack) + if pkt.BoxType == "item" { + if stack.Quantity > 0 { + cleanedBox = append(cleanedBox, stack) + } + } else { + if stack.ItemID != 0 { + cleanedBox = append(cleanedBox, stack) + } } } - s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox), s.charID) + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox, pkt.BoxType), s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } From bc75902da126b3b3e7172ee2f4daa1bd82fc1990 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 16 Aug 2022 01:39:53 +1000 Subject: [PATCH 19/36] prevent nil warehouse error --- server/channelserver/handlers_house.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 4e6af6947..b4b97e3fa 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -369,6 +369,11 @@ func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfOperateWarehouse) + var t int + err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t) + if err != nil { + s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID) + } bf := byteframe.NewByteFrame() bf.WriteUint8(pkt.Operation) switch pkt.Operation { @@ -380,7 +385,7 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { &itemNames[1], &itemNames[2], &itemNames[3], &itemNames[4], &itemNames[5], &itemNames[6], &itemNames[7], &itemNames[8], &itemNames[9], &equipNames[0], &equipNames[1], &equipNames[2], &equipNames[3], &equipNames[4], &equipNames[5], &equipNames[6], &equipNames[7], &equipNames[8], &equipNames[9]) bf.WriteUint32(0) - bf.WriteUint16(1000) // Usages + bf.WriteUint16(10000) // Usages temp := byteframe.NewByteFrame() for i, name := range itemNames { if len(name) > 0 { @@ -405,16 +410,11 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { case 2: s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%dname=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), pkt.Name, s.charID) case 3: - var t int - err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t) - if err != nil { - s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID) - } - bf.WriteUint32(0) - bf.WriteUint16(1000) // Usages + bf.WriteUint32(0) // Usage renewal time, >1 = disabled + bf.WriteUint16(10000) // Usages case 4: bf.WriteUint32(0) - bf.WriteUint16(1000) // Usages + bf.WriteUint16(10000) // Usages bf.WriteUint8(0) } // Opcodes From 5aa386a92c8d85e3ef40131e92c1e7449aca4df7 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 16 Aug 2022 13:06:16 +1000 Subject: [PATCH 20/36] add player reload command --- server/channelserver/handlers_cast_binary.go | 60 ++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index bf0f2f105..0d8a5495b 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -165,6 +165,66 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { fmt.Printf("Got chat message: %+v\n", chatMessage) + // Flush all objects and users and reload + if strings.HasPrefix(chatMessage.Message, "!reload") { + sendServerChatMessage(s, "Reloading players...") + var temp mhfpacket.MHFPacket + deleteNotif := byteframe.NewByteFrame() + for _, object := range s.stage.objects { + if object.ownerCharID == s.charID { + continue + } + temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.id} + deleteNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(deleteNotif, s.clientContext) + } + for _, session := range s.server.sessions { + if s == session { + continue + } + temp = &mhfpacket.MsgSysDeleteUser{CharID: session.charID} + deleteNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(deleteNotif, s.clientContext) + } + deleteNotif.WriteUint16(0x0010) + s.QueueSend(deleteNotif.Data()) + time.Sleep(500 * time.Millisecond) + reloadNotif := byteframe.NewByteFrame() + for _, session := range s.server.sessions { + if s == session { + continue + } + temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID} + reloadNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(reloadNotif, s.clientContext) + for i := 0; i < 3; i++ { + temp = &mhfpacket.MsgSysNotifyUserBinary{ + CharID: session.charID, + BinaryType: uint8(i + 1), + } + reloadNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(reloadNotif, s.clientContext) + } + } + for _, obj := range s.stage.objects { + if obj.ownerCharID == s.charID { + continue + } + temp = &mhfpacket.MsgSysDuplicateObject{ + ObjID: obj.id, + X: obj.x, + Y: obj.y, + Z: obj.z, + Unk0: 0, + OwnerCharID: obj.ownerCharID, + } + reloadNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(reloadNotif, s.clientContext) + } + reloadNotif.WriteUint16(0x0010) + s.QueueSend(reloadNotif.Data()) + } + // Set account rights if strings.HasPrefix(chatMessage.Message, "!rights") { var v uint32 From ed3687548c1130cdfe39ded0e02445664d7785b6 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 16 Aug 2022 21:03:30 +1000 Subject: [PATCH 21/36] prevent CSVs from adding existing values --- common/stringsupport/string_convert.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/stringsupport/string_convert.go b/common/stringsupport/string_convert.go index 84574375b..ab91311ac 100644 --- a/common/stringsupport/string_convert.go +++ b/common/stringsupport/string_convert.go @@ -120,7 +120,11 @@ func CSVAdd(csv string, v int) string { if len(csv) == 0 { return strconv.Itoa(v) } - return csv + "," + strconv.Itoa(v) + if CSVContains(csv, v) { + return csv + } else { + return csv + "," + strconv.Itoa(v) + } } func CSVRemove(csv string, v int) string { From 602cf31ed5cfc083f69c58b6d61f1c5ac71502fb Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 00:41:22 +1000 Subject: [PATCH 22/36] stub OperationInvGuild packets --- .../mhfpacket/msg_mhf_operation_inv_guild.go | 25 ++++++++++++++----- server/channelserver/handlers_guild.go | 5 +++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/network/mhfpacket/msg_mhf_operation_inv_guild.go b/network/mhfpacket/msg_mhf_operation_inv_guild.go index b07311c9d..bcd0b45b8 100644 --- a/network/mhfpacket/msg_mhf_operation_inv_guild.go +++ b/network/mhfpacket/msg_mhf_operation_inv_guild.go @@ -1,15 +1,22 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfOperationInvGuild represents the MSG_MHF_OPERATION_INV_GUILD -type MsgMhfOperationInvGuild struct{} +type MsgMhfOperationInvGuild struct { + AckHandle uint32 + Operation uint8 + ActiveHours uint8 + DaysActive uint8 + PlayStyle uint8 + GuildRequest uint8 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfOperationInvGuild) Opcode() network.PacketID { @@ -18,7 +25,13 @@ func (m *MsgMhfOperationInvGuild) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfOperationInvGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Operation = bf.ReadUint8() + m.ActiveHours = bf.ReadUint8() + m.DaysActive = bf.ReadUint8() + m.PlayStyle = bf.ReadUint8() + m.GuildRequest = bf.ReadUint8() + 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 2dd84f659..0bcfdfcbd 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1902,6 +1902,9 @@ func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) { stubEnumerateNoResults(s, pkt.AckHandle) } -func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfOperationInvGuild) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) +} func handleMsgMhfUpdateGuildcard(s *Session, p mhfpacket.MHFPacket) {} From 89683e6063b02c472a9b40e4ba2561be4d8fcd1a Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 04:55:42 +1000 Subject: [PATCH 23/36] further TransitMessage decoding --- server/channelserver/handlers.go | 60 ++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 5af8abb20..28164b103 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -423,6 +423,9 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { } for session := range stage.clients { count++ + hrp := uint16(1) + gr := uint16(0) + s.server.db.QueryRow("SELECT hrp, gr FROM characters WHERE id=$1", session.charID).Scan(&hrp, &gr) sessionStage := stringsupport.UTF8ToSJIS(session.stageID) sessionName := stringsupport.UTF8ToSJIS(session.Name) resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) @@ -435,8 +438,8 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { resp.WriteBytes(make([]byte, 48)) resp.WriteNullTerminatedBytes(sessionStage) resp.WriteNullTerminatedBytes(sessionName) - resp.WriteUint16(999) // HR - resp.WriteUint16(999) // GR + resp.WriteUint16(hrp) + resp.WriteUint16(gr) resp.WriteBytes([]byte{0x06, 0x10, 0x00}) // Unk } } @@ -445,12 +448,23 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { } case 4: // Find Party bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) - bf.ReadUint8() + setting := bf.ReadUint8() maxResults := bf.ReadUint16() - bf.ReadUint8() - bf.ReadUint8() + bf.Seek(2, 1) partyType := bf.ReadUint16() - _ = bf.DataFromCurrent() // Restrictions + 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() + } + } var stagePrefix string switch partyType { case 0: // Public Bar @@ -470,31 +484,41 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { break } if strings.HasPrefix(stage.id, stagePrefix) { + sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}]) + sb3.Seek(4, 0) + stageRankRestriction := sb3.ReadUint16() + stageTarget := sb3.ReadUint16() + if rankRestriction != 0xFFFF && stageRankRestriction < rankRestriction { + continue + } count++ sessionStage := stringsupport.UTF8ToSJIS(stage.id) resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) resp.WriteUint16(c.Port) - - // TODO: This is half right, could be trimmed - resp.WriteUint16(0) - resp.WriteUint16(uint16(len(stage.clients))) + resp.WriteUint16(0) // Static? + resp.WriteUint16(0) // Unk resp.WriteUint16(uint16(len(stage.clients))) resp.WriteUint16(stage.maxPlayers) - resp.WriteUint16(0) - resp.WriteUint16(uint16(len(stage.clients))) - // - - resp.WriteUint16(uint16(len(sessionStage) + 1)) - resp.WriteUint8(1) + resp.WriteUint16(0) // Num clients entered from stage + resp.WriteUint16(stage.maxPlayers) + resp.WriteUint8(1) // Static? + resp.WriteUint8(uint8(len(sessionStage) + 1)) + resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 0}]))) resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}]))) - resp.WriteBytes(make([]byte, 16)) + resp.WriteUint16(stageRankRestriction) + resp.WriteUint16(stageTarget) + resp.WriteBytes(make([]byte, 12)) resp.WriteNullTerminatedBytes(sessionStage) - resp.WriteBytes([]byte{0x00}) + resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 0}]) resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}]) } } } } + if (pkt.SearchType == 1 || pkt.SearchType == 3) && count == 0 { + doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) + return + } resp.Seek(0, io.SeekStart) resp.WriteUint16(count) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) From 3c099acec405a316179320827779fe7a0191bf52 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 05:07:37 +1000 Subject: [PATCH 24/36] update launcher links --- www/erupe/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/erupe/index.html b/www/erupe/index.html index 57aa1c568..0b130c684 100644 --- a/www/erupe/index.html +++ b/www/erupe/index.html @@ -118,11 +118,11 @@
-
+
From 97c57762f03bd53d43ff6cad591c5745909f2752 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 17:04:13 +1000 Subject: [PATCH 25/36] add query to reset savemercenary --- patch-schema/mercenary.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/patch-schema/mercenary.sql b/patch-schema/mercenary.sql index f67e4fe4e..9000db9f3 100644 --- a/patch-schema/mercenary.sql +++ b/patch-schema/mercenary.sql @@ -2,4 +2,6 @@ BEGIN; CREATE SEQUENCE IF NOT EXISTS public.rasta_id_seq; +UPDATE characters SET savemercenary=NULL; + END; \ No newline at end of file From 14d3b3743516dfd230e4efb9cca1ae76aef7e306 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 23:29:16 +1000 Subject: [PATCH 26/36] discord bot cleanup --- config.json | 5 +- main.go | 3 +- server/channelserver/handlers_discord.go | 346 +++------------------ server/channelserver/sys_channel_server.go | 21 +- server/channelserver/sys_stage.go | 29 -- server/discordbot/discord_bot.go | 38 +-- 6 files changed, 54 insertions(+), 388 deletions(-) diff --git a/config.json b/config.json index 29af1fb8d..67087d28c 100644 --- a/config.json +++ b/config.json @@ -28,10 +28,7 @@ "discord": { "enabled": false, "bottoken": "", - "realtimeChannelID": "", - "serverId": "", - "devRoles": [], - "devMode": false + "realtimeChannelID": "" }, "database": { "host": "localhost", diff --git a/main.go b/main.go index c3c0162e0..2bc598a06 100644 --- a/main.go +++ b/main.go @@ -65,7 +65,7 @@ func main() { var discordBot *discordbot.DiscordBot = nil if erupeConfig.Discord.Enabled { - bot, err := discordbot.NewDiscordBot(discordbot.DiscordBotOptions{ + bot, err := discordbot.NewDiscordBot(discordbot.Options{ Logger: logger, Config: erupeConfig, }) @@ -82,6 +82,7 @@ func main() { } discordBot = bot + logger.Info("Discord bot is enabled") } else { logger.Info("Discord bot is disabled") } diff --git a/server/channelserver/handlers_discord.go b/server/channelserver/handlers_discord.go index 38619e079..f9290908b 100644 --- a/server/channelserver/handlers_discord.go +++ b/server/channelserver/handlers_discord.go @@ -2,87 +2,45 @@ package channelserver import ( "fmt" + "github.com/bwmarrin/discordgo" "sort" "strings" - - "github.com/bwmarrin/discordgo" + "unicode" ) -type Character struct { - ID uint32 `db:"id"` - IsFemale bool `db:"is_female"` - IsNewCharacter bool `db:"is_new_character"` - Name string `db:"name"` - UnkDescString string `db:"unk_desc_string"` - HRP uint16 `db:"hrp"` - GR uint16 `db:"gr"` - WeaponType uint16 `db:"weapon_type"` - LastLogin uint32 `db:"last_login"` +type Player struct { + CharName string + QuestID int } -var weapons = []string{ - "<:gs:970861408227049477>", - "<:hbg:970861408281563206>", - "<:hm:970861408239628308>", - "<:lc:970861408298352660>", - "<:sns:970861408319315988>", - "<:lbg:970861408327725137>", - "<:ds:970861408277368883>", - "<:ls:970861408319311872>", - "<:hh:970861408222863400>", - "<:gl:970861408327720980>", - "<:bw:970861408294174780>", - "<:tf:970861408424177744>", - "<:sw:970861408340283472>", - "<:ms:970861408411594842>", -} +func getPlayerSlice(s *Server) []Player { + var p []Player + var questIndex int -func (s *Server) getCharacterForUser(uid int) (*Character, error) { - character := Character{} - err := s.db.Get(&character, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE id = $1", uid) - if err != nil { - return nil, err + for _, channel := range s.Channels { + for _, stage := range channel.stages { + if len(stage.clients) == 0 { + continue + } + questID := 0 + if stage.isQuest() { + questIndex++ + questID = questIndex + } + for client := range stage.clients { + p = append(p, Player{ + CharName: client.Name, + QuestID: questID, + }) + } + } } - - return &character, nil + return p } -func CountChars(s *Server) string { - count := 0 - for _, stage := range s.stages { - count += len(stage.clients) - } - - message := fmt.Sprintf("Server [%s]: %d players;", s.name, count) - - return message -} - -type ListPlayer struct { - CharName string - InQuest bool - WeaponEmoji string - QuestEmoji string - StageName string -} - -func (p *ListPlayer) toString(length int) string { - status := "" - if p.InQuest { - status = "(in quest " + p.QuestEmoji + ")" - } else { - status = p.StageName - } - - missingSpace := length - len(p.CharName) - whitespaces := strings.Repeat(" ", missingSpace+5) - - return fmt.Sprintf("%s %s %s %s", p.WeaponEmoji, p.CharName, whitespaces, status) -} - -func getPlayerList(s *Server) ([]ListPlayer, int) { - list := []ListPlayer{} +func getCharacterList(s *Server) string { questEmojis := []string{ + ":person_in_lotus_position:", ":white_circle:", ":red_circle:", ":blue_circle:", @@ -91,255 +49,41 @@ func getPlayerList(s *Server) ([]ListPlayer, int) { ":purple_circle:", ":yellow_circle:", ":orange_circle:", + ":black_circle:", } - bigNameLen := 0 + playerSlice := getPlayerSlice(s) - for _, stage := range s.stages { - if len(stage.clients) == 0 { - continue - } - - questEmoji := ":black_circle:" - - if len(questEmojis) > 0 { - questEmoji = questEmojis[len(questEmojis)-1] - questEmojis = questEmojis[:len(questEmojis)-1] - } - - isQuest := stage.isQuest() - for client := range stage.clients { - char, err := s.getCharacterForUser(int(client.charID)) - if err == nil { - if len(char.Name) > bigNameLen { - bigNameLen = len(char.Name) - } - - list = append(list, ListPlayer{ - CharName: char.Name, - InQuest: isQuest, - QuestEmoji: questEmoji, - WeaponEmoji: weapons[char.WeaponType], - StageName: stage.GetName(), - }) - - } - } - } - - return list, bigNameLen -} - -func PlayerList(s *Server) string { - list := "" - count := 0 - listPlayers, bigNameLen := getPlayerList(s) - - sort.SliceStable(listPlayers, func(i, j int) bool { - return listPlayers[i].CharName < listPlayers[j].CharName + sort.SliceStable(playerSlice, func(i, j int) bool { + return playerSlice[i].QuestID < playerSlice[j].QuestID }) - for _, lp := range listPlayers { - list += lp.toString(bigNameLen) + "\n" - count++ + message := fmt.Sprintf("===== Online: %d =====\n", len(playerSlice)) + for _, player := range playerSlice { + message += fmt.Sprintf("%s %s", questEmojis[player.QuestID], player.CharName) } - message := fmt.Sprintf("<:SnS:822963937360347148> Frontier Hunters Online: [%s ] <:switcha:822963906401533992> \n============== Total %d =============\n", s.name, count) - message += list - return message } -func debug(s *Server) string { - list := "" - - for _, stage := range s.stages { - if !stage.isQuest() && len(stage.objects) == 0 { - continue - } - - list += fmt.Sprintf(" -> Stage: %s StageId: %s\n", stage.GetName(), stage.id) - isQuest := "false" - hasDeparted := "false" - - if stage.isQuest() { - isQuest = "true" - } - - list += fmt.Sprintf(" '-> isQuest: %s\n", isQuest) - - if stage.isQuest() { - if len(stage.clients) > 0 { - hasDeparted = "true" - } - - list += fmt.Sprintf(" '-> isDeparted: %s\n", hasDeparted) - list += fmt.Sprintf(" '-> reserveSlots (%d/%d)\n", len(stage.reservedClientSlots), stage.maxPlayers) - - for charid, _ := range stage.reservedClientSlots { - char, err := s.getCharacterForUser(int(charid)) - if err == nil { - list += fmt.Sprintf(" '-> %s\n", char.Name) - } - } - } - - list += " '-> objects: \n" - for _, obj := range stage.objects { - objInfo := fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z) - list += fmt.Sprintf(" '-> ObjectId: %d - %s\n", obj.id, objInfo) - } - } - - message := fmt.Sprintf("Objects in Server: [%s ]\n", s.name) - message += list - - return message -} - -func questlist(s *Server) string { - list := "" - - for _, stage := range s.stages { - if !stage.isQuest() { - continue - } - - hasDeparted := "" - if len(stage.clients) > 0 { - hasDeparted = " - departed" - } - list += fmt.Sprintf(" '-> StageId: %s (%d/%d) %s - %s\n", stage.id, len(stage.reservedClientSlots), stage.maxPlayers, hasDeparted, stage.createdAt) - - for charid, _ := range stage.reservedClientSlots { - char, err := s.getCharacterForUser(int(charid)) - if err == nil { - list += fmt.Sprintf(" '-> %s\n", char.Name) - } - } - } - - message := fmt.Sprintf("Quests in Server: [%s ]\n", s.name) - message += list - - return message -} - -func removeStageById(s *Server, stageId string) string { - if s.stages[stageId] != nil { - delete(s.stages, stageId) - return "Stage deleted!" - } - - return "Stage not found!" -} - -func cleanStr(str string) string { - return strings.ToLower(strings.Trim(str, " ")) -} - -func getCharInfo(server *Server, charName string) string { - var s *Stage - var c *Session - - for _, stage := range server.stages { - for client := range stage.clients { - - if client.Name == "" { - continue - } - - if cleanStr(client.Name) == cleanStr(charName) { - s = stage - c = client - } - - } - } - - if s == nil { - return "Character not found" - } - - objInfo := "" - - obj := server.FindObjectByChar(c.charID) - // server.logger.Info("Found object: %+v", zap.Object("obj", obj)) - - if obj != nil { - objInfo = fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z) - } - - return fmt.Sprintf("Character: %s\nStage: %s\nStageId: %s\n%s", c.Name, s.GetName(), s.id, objInfo) -} - -func (s *Server) isDiscordAdmin(ds *discordgo.Session, m *discordgo.MessageCreate) bool { - for _, role := range m.Member.Roles { - for _, id := range s.erupeConfig.Discord.DevRoles { - if id == role { - return true - } - } - } - - return false -} - // onDiscordMessage handles receiving messages from discord and forwarding them ingame. func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) { // Ignore messages from our bot, or ones that are not in the correct channel. - if m.Author.ID == ds.State.User.ID || !s.enable { + if m.Author.Bot || m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID { return } - // Ignore other channels in devMode - if s.erupeConfig.Discord.DevMode && m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID { - return - } - - args := strings.Split(m.Content, " ") - commandName := args[0] - - // Move to slash commadns - if commandName == "!players" { - ds.ChannelMessageSend(m.ChannelID, PlayerList(s)) - return - } - - if commandName == "-char" { - if len(args) < 2 { - ds.ChannelMessageSend(m.ChannelID, "Usage: !char ") - return + paddedName := strings.TrimSpace(strings.Map(func(r rune) rune { + if r > unicode.MaxASCII { + return -1 } + return r + }, m.Author.Username)) - charName := strings.Join(args[1:], " ") - ds.ChannelMessageSend(m.ChannelID, getCharInfo(s, charName)) - return + for i := 0; i < 10-len(m.Author.Username); i++ { + paddedName += " " } - if commandName == "!debug" && s.isDiscordAdmin(ds, m) { - ds.ChannelMessageSend(m.ChannelID, debug(s)) - return - } - - if commandName == "!questlist" && s.isDiscordAdmin(ds, m) { - ds.ChannelMessageSend(m.ChannelID, questlist(s)) - return - } - - if commandName == "!remove-stage" && s.isDiscordAdmin(ds, m) { - if len(args) < 2 { - ds.ChannelMessageSend(m.ChannelID, "Usage: !remove-stage ") - return - } - - stageId := strings.Join(args[1:], " ") - ds.ChannelMessageSend(m.ChannelID, removeStageById(s, stageId)) - return - } - - if m.ChannelID == s.erupeConfig.Discord.RealtimeChannelID { - message := fmt.Sprintf("[DISCORD] %s: %s", m.Author.Username, m.Content) - s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message)) - } + message := fmt.Sprintf("[D] %s > %s", paddedName, m.Content) + s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message)) } diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index ca9063baf..8702d056a 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -17,21 +17,6 @@ import ( type StageIdType = string -const ( - // GlobalStage is the stage that is used for all users. - MezeportaStageId StageIdType = "sl1Ns200p0a0u0" - GuildHallLv1StageId StageIdType = "sl1Ns202p0a0u0" - GuildHallLv2StageId StageIdType = "sl1Ns203p0a0u0" - GuildHallLv3StageId StageIdType = "sl1Ns204p0a0u0" - PugiFarmStageId StageIdType = "sl1Ns205p0a0u0" - RastaBarStageId StageIdType = "sl1Ns211p0a0u0" - PalloneCaravanStageId StageIdType = "sl1Ns260p0a0u0" - GookFarmStageId StageIdType = "sl1Ns265p0a0u0" - DivaFountainStageId StageIdType = "sl2Ns379p0a0u0" - DivaHallStageId StageIdType = "sl1Ns445p0a0u0" - MezFesStageId StageIdType = "sl1Ns462p0a0u0" -) - // Config struct allows configuring the server. type Config struct { ID uint16 @@ -80,8 +65,7 @@ type Server struct { // Discord chat integration discordBot *discordbot.DiscordBot - name string - enable bool + name string raviente *Raviente } @@ -157,7 +141,6 @@ func NewServer(config *Config) *Server { semaphoreIndex: 7, discordBot: config.DiscordBot, name: config.Name, - enable: config.Enable, raviente: NewRaviente(), } @@ -367,7 +350,7 @@ func (s *Server) BroadcastRaviente(ip uint32, port uint16, stage []byte, _type u func (s *Server) DiscordChannelSend(charName string, content string) { if s.erupeConfig.Discord.Enabled && s.discordBot != nil { - message := fmt.Sprintf("**%s** : %s", charName, content) + message := fmt.Sprintf("**%s**: %s", charName, content) s.discordBot.RealtimeChannelSend(message) } } diff --git a/server/channelserver/sys_stage.go b/server/channelserver/sys_stage.go index 9a968bf3f..827b2d6be 100644 --- a/server/channelserver/sys_stage.go +++ b/server/channelserver/sys_stage.go @@ -98,35 +98,6 @@ func (s *Stage) isQuest() bool { return len(s.reservedClientSlots) > 0 } -func (s *Stage) GetName() string { - switch s.id { - case MezeportaStageId: - return "Mezeporta" - case GuildHallLv1StageId: - return "Guild Hall Lv1" - case GuildHallLv2StageId: - return "Guild Hall Lv2" - case GuildHallLv3StageId: - return "Guild Hall Lv3" - case PugiFarmStageId: - return "Pugi Farm" - case RastaBarStageId: - return "Rasta Bar" - case PalloneCaravanStageId: - return "Pallone Caravan" - case GookFarmStageId: - return "Gook Farm" - case DivaFountainStageId: - return "Diva Fountain" - case DivaHallStageId: - return "Diva Hall" - case MezFesStageId: - return "Mez Fes" - default: - return "" - } -} - func (s *Stage) NextObjectID() uint32 { s.objectIndex = s.objectIndex + 1 // Objects beyond 127 do not duplicate correctly diff --git a/server/discordbot/discord_bot.go b/server/discordbot/discord_bot.go index df817d50a..fc5c41ce8 100644 --- a/server/discordbot/discord_bot.go +++ b/server/discordbot/discord_bot.go @@ -1,11 +1,10 @@ package discordbot import ( - "regexp" - "erupe-ce/config" "github.com/bwmarrin/discordgo" "go.uber.org/zap" + "regexp" ) type DiscordBot struct { @@ -16,12 +15,12 @@ type DiscordBot struct { RealtimeChannel *discordgo.Channel } -type DiscordBotOptions struct { +type Options struct { Config *config.Config Logger *zap.Logger } -func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error) { +func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) { session, err := discordgo.New("Bot " + options.Config.Discord.BotToken) if err != nil { @@ -29,13 +28,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error return nil, err } - mainGuild, err := session.Guild(options.Config.Discord.ServerID) - - if err != nil { - options.Logger.Fatal("Discord failed to get main guild", zap.Error(err)) - return nil, err - } - realtimeChannel, err := session.Channel(options.Config.Discord.RealtimeChannelID) if err != nil { @@ -47,7 +39,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error config: options.Config, logger: options.Logger, Session: session, - MainGuild: mainGuild, RealtimeChannel: realtimeChannel, } @@ -60,21 +51,10 @@ func (bot *DiscordBot) Start() (err error) { return } -func (bot *DiscordBot) FindRoleByID(id string) *discordgo.Role { - for _, role := range bot.MainGuild.Roles { - if role.ID == id { - return role - } - } - - return nil -} - // Replace all mentions to real name from the message. func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { userRegex := regexp.MustCompile(`<@!?(\d{17,19})>`) emojiRegex := regexp.MustCompile(`(?:)?`) - roleRegex := regexp.MustCompile(`<@&(\d{17,19})>`) result := ReplaceTextAll(message, userRegex, func(userId string) string { user, err := bot.Session.User(userId) @@ -90,17 +70,7 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { return ":" + emojiName + ":" }) - result = ReplaceTextAll(result, roleRegex, func(roleId string) string { - role := bot.FindRoleByID(roleId) - - if role != nil { - return "@!" + role.Name - } - - return "@!unknown" - }) - - return string(result) + return result } func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) { From e0c50bb5d76e7e6200ba6ed564879bea71ece819 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 23:38:09 +1000 Subject: [PATCH 27/36] correct padding on chat broadcast --- server/channelserver/handlers_discord.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_discord.go b/server/channelserver/handlers_discord.go index f9290908b..1f3398d18 100644 --- a/server/channelserver/handlers_discord.go +++ b/server/channelserver/handlers_discord.go @@ -80,7 +80,7 @@ func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCre return r }, m.Author.Username)) - for i := 0; i < 10-len(m.Author.Username); i++ { + for i := 0; i < 8-len(m.Author.Username); i++ { paddedName += " " } From a79c09d205ad0df39c3368c78311e5b1a3a3eeb6 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 18 Aug 2022 00:11:54 +1000 Subject: [PATCH 28/36] restrict local chat, enable world chat --- server/channelserver/handlers_cast_binary.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 0d8a5495b..35538e0fb 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -240,7 +240,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { } // Discord integration - if chatMessage.Type == binpacket.ChatTypeLocal || chatMessage.Type == binpacket.ChatTypeParty { + if (pkt.BroadcastType == BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == BroadcastTypeWorld { s.server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message) } From 56ae30745ded1a6cae935f654bb9c06536654340 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 18 Aug 2022 00:23:48 +1000 Subject: [PATCH 29/36] remove unused static stages --- server/channelserver/sys_channel_server.go | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 8702d056a..635706d1a 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -15,8 +15,6 @@ import ( "go.uber.org/zap" ) -type StageIdType = string - // Config struct allows configuring the server. type Config struct { ID uint16 @@ -147,33 +145,15 @@ func NewServer(config *Config) *Server { // Mezeporta s.stages["sl1Ns200p0a0u0"] = NewStage("sl1Ns200p0a0u0") - // Guild Hall LV1 - s.stages["sl1Ns202p0a0u0"] = NewStage("sl1Ns202p0a0u0") - - // Guild Hall LV2 - s.stages["sl1Ns203p0a0u0"] = NewStage("sl1Ns203p0a0u0") - - // Guild Hall LV3 - s.stages["sl1Ns204p0a0u0"] = NewStage("sl1Ns204p0a0u0") - - // Pugi Farm - s.stages["sl1Ns205p0a0u0"] = NewStage("sl1Ns205p0a0u0") - // Rasta bar stage s.stages["sl1Ns211p0a0u0"] = NewStage("sl1Ns211p0a0u0") // Pallone Carvan s.stages["sl1Ns260p0a0u0"] = NewStage("sl1Ns260p0a0u0") - // Gook Farm - s.stages["sl1Ns265p0a0u0"] = NewStage("sl1Ns265p0a0u0") - // Diva fountain / prayer fountain. s.stages["sl2Ns379p0a0u0"] = NewStage("sl2Ns379p0a0u0") - // Diva Hall - s.stages["sl1Ns445p0a0u0"] = NewStage("sl1Ns445p0a0u0") - // MezFes s.stages["sl1Ns462p0a0u0"] = NewStage("sl1Ns462p0a0u0") From 1ba3d5556669679461ef03a4f9980ba336b65dcb Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 03:53:28 +1000 Subject: [PATCH 30/36] road leaderboard initial implementation --- .../msg_mhf_enumerate_rengoku_ranking.go | 18 +- patch-schema/road-leaderboard.sql | 11 + server/channelserver/handlers_rengoku.go | 203 +++++++++++++++++- 3 files changed, 216 insertions(+), 16 deletions(-) create mode 100644 patch-schema/road-leaderboard.sql diff --git a/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go b/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go index d35bc7227..21817237d 100644 --- a/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go +++ b/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go @@ -1,19 +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" ) // MsgMhfEnumerateRengokuRanking represents the MSG_MHF_ENUMERATE_RENGOKU_RANKING type MsgMhfEnumerateRengokuRanking struct { - AckHandle uint32 - Unk0 uint32 - Unk1 uint16 // Hardcoded 0 in the binary - Unk2 uint16 // Hardcoded 00 01 in the binary + AckHandle uint32 + Leaderboard uint32 + Unk1 uint16 // Hardcoded 0 in the binary + Unk2 uint16 // Hardcoded 00 01 in the binary } // Opcode returns the ID associated with this packet type. @@ -24,7 +24,7 @@ func (m *MsgMhfEnumerateRengokuRanking) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateRengokuRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() + m.Leaderboard = bf.ReadUint32() m.Unk1 = bf.ReadUint16() m.Unk2 = bf.ReadUint16() return nil diff --git a/patch-schema/road-leaderboard.sql b/patch-schema/road-leaderboard.sql new file mode 100644 index 000000000..8dfea2875 --- /dev/null +++ b/patch-schema/road-leaderboard.sql @@ -0,0 +1,11 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS rengoku_score ( + character_id integer PRIMARY KEY, + max_stages_mp integer, + max_points_mp integer, + max_stages_sp integer, + max_points_sp integer +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_rengoku.go b/server/channelserver/handlers_rengoku.go index d7b60686b..6fdce7c22 100644 --- a/server/channelserver/handlers_rengoku.go +++ b/server/channelserver/handlers_rengoku.go @@ -1,6 +1,8 @@ package channelserver import ( + ps "erupe-ce/common/pascalstring" + "fmt" "io/ioutil" "path/filepath" @@ -17,7 +19,19 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) { if err != nil { s.logger.Fatal("Failed to update rengokudata savedata in db", zap.Error(err)) } - + bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) + bf.Seek(71, 0) + maxStageMp := bf.ReadUint32() + maxScoreMp := bf.ReadUint32() + bf.Seek(4, 1) + maxStageSp := bf.ReadUint32() + maxScoreSp := bf.ReadUint32() + var t int + err = s.server.db.QueryRow("SELECT character_id FROM rengoku_score WHERE character_id=$1", s.charID).Scan(&t) + if err != nil { + s.server.db.Exec("INSERT INTO rengoku_score (character_id) VALUES ($1)", s.charID) + } + s.server.db.Exec("UPDATE rengoku_score SET max_stages_mp=$1, max_points_mp=$2, max_stages_sp=$3, max_points_sp=$4 WHERE character_id=$5", maxStageMp, maxScoreMp, maxStageSp, maxScoreSp, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } @@ -81,16 +95,191 @@ func handleMsgMhfGetRengokuBinary(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, data) } +const rengokuScoreQuery = ` +SELECT max_stages_mp, max_points_mp, max_stages_sp, max_points_sp, c.name, gc.guild_id +FROM rengoku_score rs +LEFT JOIN characters c ON c.id = rs.character_id +LEFT JOIN guild_characters gc ON gc.character_id = rs.character_id +` + +type RengokuScore struct { + Name string `db:"name"` + GuildID int `db:"guild_id"` + MaxStagesMP uint32 `db:"max_stages_mp"` + MaxPointsMP uint32 `db:"max_points_mp"` + MaxStagesSP uint32 `db:"max_stages_sp"` + MaxPointsSP uint32 `db:"max_points_sp"` +} + func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateRengokuRanking) - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + + guild, _ := GetGuildInfoByCharacterId(s, s.charID) + isApplicant, _ := guild.HasApplicationForCharID(s, s.charID) + if isApplicant { + guild = nil + } + + var score RengokuScore + i := uint32(1) + bf := byteframe.NewByteFrame() + scoreData := byteframe.NewByteFrame() + switch pkt.Leaderboard { + case 0: // Max stage overall MP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_mp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 1: // Max RdP overall MP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_mp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 2: // Max stage guild MP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_mp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + case 3: // Max RdP guild MP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_mp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + case 4: // Max stage overall SP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_sp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 5: // Max RdP overall SP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_sp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 6: // Max stage guild SP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_sp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + case 7: // Max RdP guild SP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + } + bf.WriteUint8(uint8(i) - 1) + bf.WriteBytes(scoreData.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGetRengokuRankingRank(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetRengokuRankingRank) - - resp := byteframe.NewByteFrame() - resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + // What is this for? + bf := byteframe.NewByteFrame() + bf.WriteUint32(0) // Max stage overall MP rank + bf.WriteUint32(0) // Max RdP overall MP rank + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } From c4d128ec182c7bd64447f84ebc6cb3f578447da5 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 14:00:59 +1000 Subject: [PATCH 31/36] correctly handle yearly sub exchanges --- network/mhfpacket/msg_mhf_exchange_weekly_stamp.go | 4 ++-- server/channelserver/handlers.go | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go b/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go index 758925936..918a870ac 100644 --- a/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go +++ b/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go @@ -12,7 +12,7 @@ import ( type MsgMhfExchangeWeeklyStamp struct { AckHandle uint32 StampType string - Unk1 bool + Unk1 uint8 Unk2 uint16 } @@ -31,7 +31,7 @@ func (m *MsgMhfExchangeWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientct case 2: m.StampType = "ex" } - m.Unk1 = bf.ReadBool() + m.Unk1 = bf.ReadUint8() m.Unk2 = bf.ReadUint16() return nil } diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 28164b103..b5d6d0d38 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -697,7 +697,11 @@ func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp) var total, redeemed uint16 - s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) + if pkt.Unk1 == 0xA { // Yearly Sub Ex + s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed) + } else { + s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) + } bf := byteframe.NewByteFrame() bf.WriteUint16(total) bf.WriteUint16(redeemed) From 8a3daa1cfac02e4a316b8ef0bb9bf40a417b32e1 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 14:42:00 +1000 Subject: [PATCH 32/36] add exchanged tkts to warehouse gift box --- server/channelserver/handlers.go | 8 ++++++++ server/channelserver/handlers_house.go | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index b5d6d0d38..53e59d25f 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -697,11 +697,19 @@ func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp) var total, redeemed uint16 + var tktStack mhfpacket.WarehouseStack if pkt.Unk1 == 0xA { // Yearly Sub Ex s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed) + tktStack = mhfpacket.WarehouseStack{ItemID: 0x08A2, Quantity: 1} } else { s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) + if pkt.StampType == "hl" { + tktStack = mhfpacket.WarehouseStack{ItemID: 0x065E, Quantity: 5} + } else { + tktStack = mhfpacket.WarehouseStack{ItemID: 0x065F, Quantity: 5} + } } + addWarehouseGift(s, "item", tktStack) bf := byteframe.NewByteFrame() bf.WriteUint16(total) bf.WriteUint16(redeemed) diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index b4b97e3fa..b65c5fbeb 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -426,6 +426,26 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +func addWarehouseGift(s *Session, boxType string, giftStack mhfpacket.WarehouseStack) { + giftBox := getWarehouseBox(s, boxType, 10) + if boxType == "item" { + exists := false + for i, stack := range giftBox { + if stack.ItemID == giftStack.ItemID { + exists = true + giftBox[i].Quantity += giftStack.Quantity + break + } + } + if exists == false { + giftBox = append(giftBox, giftStack) + } + } else { + giftBox = append(giftBox, giftStack) + } + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s10=$1 WHERE character_id=$2", boxType), boxToBytes(giftBox, boxType), s.charID) +} + func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack { var data []byte s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data) From a56309014b629d4c1b721ce54f8abf0b017b87aa Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 16:39:07 +1000 Subject: [PATCH 33/36] persistent BackStage --- server/channelserver/handlers_stage.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_stage.go b/server/channelserver/handlers_stage.go index 4bf19d114..906480be7 100644 --- a/server/channelserver/handlers_stage.go +++ b/server/channelserver/handlers_stage.go @@ -135,7 +135,6 @@ func removeSessionFromStage(s *Session) { // Remove client from old stage. s.stage.Lock() delete(s.stage.clients, s) - delete(s.stage.reservedClientSlots, s.charID) // Delete old stage objects owned by the client. s.logger.Info("Sending notification to old stage clients") @@ -157,6 +156,7 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { if s.stageID == "" { s.stageMoveStack.Set(pkt.StageID) } else { + s.stage.reservedClientSlots[s.charID] = false s.stageMoveStack.Push(s.stageID) s.stageMoveStack.Lock() } @@ -175,11 +175,18 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) { // Transfer back to the saved stage ID before the previous move or enter. s.stageMoveStack.Unlock() backStage, err := s.stageMoveStack.Pop() - if err != nil { panic(err) } + if _, exists := s.stage.reservedClientSlots[s.charID]; exists { + delete(s.stage.reservedClientSlots, s.charID) + } + + if _, exists := s.server.stages[backStage].reservedClientSlots[s.charID]; exists { + delete(s.server.stages[backStage].reservedClientSlots, s.charID) + } + doStageTransfer(s, pkt.AckHandle, backStage) } From 2a1ce2b79dbf45f75f816a62223ea8efd55efb10 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 17:06:15 +1000 Subject: [PATCH 34/36] match houses by wildcard name --- server/channelserver/handlers_house.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index b65c5fbeb..280f27238 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -92,7 +92,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) { } case 3: house := HouseData{} - row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name=$1", pkt.Name) + 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) From f87c1e480f3596c21c02a0700e925b1364df7478 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 17:27:40 +1000 Subject: [PATCH 35/36] add missing static stages --- server/channelserver/sys_channel_server.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 635706d1a..6b2d46f3c 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -151,6 +151,12 @@ func NewServer(config *Config) *Server { // Pallone Carvan s.stages["sl1Ns260p0a0u0"] = NewStage("sl1Ns260p0a0u0") + // Pallone Guest House 1st Floor + s.stages["sl1Ns262p0a0u0"] = NewStage("sl1Ns262p0a0u0") + + // Pallone Guest House 2nd Floor + s.stages["sl1Ns263p0a0u0"] = NewStage("sl1Ns263p0a0u0") + // Diva fountain / prayer fountain. s.stages["sl2Ns379p0a0u0"] = NewStage("sl2Ns379p0a0u0") From 6e7259a0688ace07fc2f7186e58fb51b09d78a2d Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 19:46:55 +1000 Subject: [PATCH 36/36] dump more save data types to backup --- server/channelserver/handlers_data.go | 6 +++--- server/channelserver/handlers_mercenary.go | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index d9cdcca17..8ed55c54a 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -56,7 +56,7 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { s.logger.Fatal("Failed to update savedata in db", zap.Error(err)) } s.logger.Info("Wrote recompressed savedata back to DB.") - dumpSaveData(s, pkt.RawDataPayload, "") + dumpSaveData(s, pkt.RawDataPayload, "savedata") _, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID) if err != nil { @@ -275,8 +275,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("%s_", s.Name)) - path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%s_", s.Name), fmt.Sprintf("%d_%s_%s%s.bin", s.charID, s.Name, Time_Current().Format("2006-01-02_15.04.05"), suffix)) + 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)) if _, err := os.Stat(dir); os.IsNotExist(err) { os.Mkdir(dir, os.ModeDir) diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index ca3dd2a71..31bc26249 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -39,7 +39,7 @@ func handleMsgMhfLoadPartner(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSavePartner(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSavePartner) - dumpSaveData(s, pkt.RawDataPayload, "_partner") + dumpSaveData(s, pkt.RawDataPayload, "partner") _, err := s.server.db.Exec("UPDATE characters SET partner=$1 WHERE id=$2", pkt.RawDataPayload, s.charID) if err != nil { @@ -75,7 +75,7 @@ func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi) - dumpSaveData(s, pkt.RawDataPayload, "_hunternavi") + dumpSaveData(s, pkt.RawDataPayload, "hunternavi") if pkt.IsDataDiff { var data []byte @@ -228,6 +228,7 @@ func handleMsgMhfLoadOtomoAirou(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveOtomoAirou) + dumpSaveData(s, pkt.RawDataPayload, "otomoairou") decomp, err := nullcomp.Decompress(pkt.RawDataPayload[1:]) if err != nil { s.logger.Error("Failed to decompress airou", zap.Error(err))