From 38d11fdabf5df8c8ca6d4e1aef805aff05b5c5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?akiragatsu=20=E7=9A=93=E6=9C=88=E7=8B=A9=E9=AD=82?= <353374337@qq.com> Date: Wed, 10 Dec 2025 17:56:55 +0800 Subject: [PATCH 1/5] Resolve the issue of G1 and GG, gacha cat not being usable. Already compatible Mainly, there are differences in the MHFG low version network protocol when pkt.ShopType=1 or 2 in handleMgMhfEnumerationShop. Conducted code judgment and differentiation processing. If you need to configure the optional material list among the three options, Configure directly in gacha_detries The same Entry Type can be merged and displayed in GG In addition, the prizes are also directly configured in the gacha-entries table, MHFG1~GG does not use the gacha_items table throughout the entire process, which meets the lottery function of MHFG with a more single function In addition, the MHFG function itself is relatively simple Example of lottery configuration for G1~GG: eg: gachaname:test entry: itemgroup: group1:(choose one of the two) ITEM_1_ID:7 COUNT:1 ITEM_1_ID:8 COUNT:2 group2:ITEM_1_ID:9 COUNT:3 reward: reward1: ITEM_ID:1 COUNT:4 weight:10% reward1: ITEM_ID:2 COUNT:5 weight:90% table:gacha_shop |id|min_gr|min_hr|name|url_bannel|url_feature|url_thmubnail|wide|recommendded|gacha_type|hidden| |1|0|0|test|null|null|null|f|f|3|t| table:gacha_entries |id|gacha_id|entry_type|item_type|item_number|item_quantity|weight|rarity|rolls|frontier_points|daily_limit|name| |1|1|0|7|7|1|0|0|0|0|0|null| |4|1|0|7|8|2|0|0|0|0|0|null| |5|1|1|7|9|3|0|0|0|0|0|null| |8|1|100|7|1|4|1000|0|0|0|0|null| |9|1|100|7|2|5|9000|0|0|0|0|null| --- server/channelserver/handlers_shop_gacha.go | 78 ++++++++++++++++++--- 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/server/channelserver/handlers_shop_gacha.go b/server/channelserver/handlers_shop_gacha.go index 3058fb632..acba56636 100644 --- a/server/channelserver/handlers_shop_gacha.go +++ b/server/channelserver/handlers_shop_gacha.go @@ -122,7 +122,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { switch pkt.ShopType { case 1: // Running gachas // Fundamentally, gacha works completely differently, just hide it for now. - if _config.ErupeConfig.RealClientMode <= _config.G7 { + if _config.ErupeConfig.RealClientMode < _config.G1 { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } @@ -144,15 +144,20 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(uint16(len(gachas))) bf.WriteUint16(uint16(len(gachas))) for _, g := range gachas { - bf.WriteUint32(g.ID) - bf.WriteUint32(0) // Unknown rank restrictions - bf.WriteUint32(0) - bf.WriteUint32(0) - bf.WriteUint32(0) - bf.WriteUint32(g.MinGR) - bf.WriteUint32(g.MinHR) - bf.WriteUint32(0) // only 0 in known packet + if _config.ErupeConfig.RealClientMode >= _config.GG { //GG之前这段数据都没有 G1就没有如下数据 G1只有ID和名字 Before GG, there was no data for G1, so there was no data for G1 except for ID and name + //TODO 但是G2 和G3 的差异还需要测试,G1和GG的数据已经清晰 But the difference between G2 and G3 still needs to be tested, and the data for G1 and GG are already clear + bf.WriteUint32(0) // Unknown rank restrictions + bf.WriteUint32(0) + bf.WriteUint32(0) + bf.WriteUint32(0) + bf.WriteUint32(g.MinGR) + bf.WriteUint32(g.MinHR) + bf.WriteUint32(0) // only 0 in known packet + } ps.Uint8(bf, g.Name, true) + if _config.ErupeConfig.RealClientMode <= _config.GG { //小于等于GG的版本,每一条发送到名字就结束了 For versions less than or equal to GG, each message sent to the name ends + continue + } ps.Uint8(bf, g.URLBanner, false) ps.Uint8(bf, g.URLFeature, false) if _config.ErupeConfig.RealClientMode >= _config.G10 { @@ -195,6 +200,61 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(uint16(len(entries))) for _, ge := range entries { var items []GachaItem + if _config.ErupeConfig.RealClientMode <= _config.GG { + /* + 如果需要在三个选项中配置可选素材列表里, + 直接在gacha_entries中配置 + 同样的EntryType即可在GG中合并显示 + 此外奖品也直接在gacha_entries表中配置, + MHFG1~GG全程不使用gacha_items表,已满足MHFG功能更单一的抽奖功能 + 此外,本身MHFG功能就比较单一 + G1 ~ GG的抽奖配置 示例: + + If you need to configure the optional material list among the three options, + Configure directly in gacha_detries + The same Entry Type can be merged and displayed in GG + In addition, the prizes are also directly configured in the gacha-entries table, + MHFG1~GG does not use the gacha_items table throughout the entire process, which meets the lottery function of MHFG with a more single function + In addition, the MHFG function itself is relatively simple + Example of lottery configuration for G1~GG: + eg: + gachaname:test + entry: + itemgroup: + group1:(choose one of the two) ITEM_1_ID:7 COUNT:1 ITEM_1_ID:8 COUNT:2 + group2:ITEM_1_ID:9 COUNT:3 + reward: + reward1: ITEM_ID:1 COUNT:4 weight:10% + reward1: ITEM_ID:2 COUNT:5 weight:90% + + table:gacha_shop + |id|min_gr|min_hr|name|url_bannel|url_feature|url_thmubnail|wide|recommendded|gacha_type|hidden| + |1|0|0|test|null|null|null|f|f|3|t| + + table:gacha_entries + |id|gacha_id|entry_type|item_type|item_number|item_quantity|weight|rarity|rolls|frontier_points|daily_limit|name| + |1|1|0|7|7|1|0|0|0|0|0|null| + |4|1|0|7|8|2|0|0|0|0|0|null| + |5|1|1|7|9|3|0|0|0|0|0|null| + |8|1|100|7|1|4|1000|0|0|0|0|null| + |9|1|100|7|2|5|9000|0|0|0|0|null| + + */ + bf.WriteUint8(ge.EntryType) //0 + bf.WriteUint32(ge.ID) //1 + bf.WriteUint8(ge.ItemType) //5 + bf.WriteUint32(ge.ItemNumber) //6 + bf.WriteUint16(ge.ItemQuantity) //10 + var weightPr uint16 + if gachaType >= 4 { // If box //12 概率 + weightPr = 1 + } else { + weightPr = uint16(ge.Weight / divisor) + } + bf.WriteUint16(weightPr) //12 概率 + bf.WriteUint8(0) //14 结束符(maybe) + continue + } bf.WriteUint8(ge.EntryType) bf.WriteUint32(ge.ID) bf.WriteUint8(ge.ItemType) From ee88fd7218e6d67f7651fba48405e7552743a351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?akiragatsu=20=E7=9A=93=E6=9C=88=E7=8B=A9=E9=AD=82?= <353374337@qq.com> Date: Wed, 10 Dec 2025 18:09:32 +0800 Subject: [PATCH 2/5] fixed --- server/channelserver/handlers_shop_gacha.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/channelserver/handlers_shop_gacha.go b/server/channelserver/handlers_shop_gacha.go index acba56636..018218caa 100644 --- a/server/channelserver/handlers_shop_gacha.go +++ b/server/channelserver/handlers_shop_gacha.go @@ -144,6 +144,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(uint16(len(gachas))) bf.WriteUint16(uint16(len(gachas))) for _, g := range gachas { + bf.WriteUint32(g.ID) if _config.ErupeConfig.RealClientMode >= _config.GG { //GG之前这段数据都没有 G1就没有如下数据 G1只有ID和名字 Before GG, there was no data for G1, so there was no data for G1 except for ID and name //TODO 但是G2 和G3 的差异还需要测试,G1和GG的数据已经清晰 But the difference between G2 and G3 still needs to be tested, and the data for G1 and GG are already clear bf.WriteUint32(0) // Unknown rank restrictions From a23a9ca5c62c7ae37ee2e8c016dd9c978b5a7576 Mon Sep 17 00:00:00 2001 From: sin365 <353374337@qq.com> Date: Thu, 26 Feb 2026 17:31:44 +0800 Subject: [PATCH 3/5] standardized code comments --- server/channelserver/handlers_shop_gacha.go | 74 +++++++-------------- 1 file changed, 24 insertions(+), 50 deletions(-) diff --git a/server/channelserver/handlers_shop_gacha.go b/server/channelserver/handlers_shop_gacha.go index 018218caa..725e2867c 100644 --- a/server/channelserver/handlers_shop_gacha.go +++ b/server/channelserver/handlers_shop_gacha.go @@ -145,8 +145,9 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(uint16(len(gachas))) for _, g := range gachas { bf.WriteUint32(g.ID) - if _config.ErupeConfig.RealClientMode >= _config.GG { //GG之前这段数据都没有 G1就没有如下数据 G1只有ID和名字 Before GG, there was no data for G1, so there was no data for G1 except for ID and name - //TODO 但是G2 和G3 的差异还需要测试,G1和GG的数据已经清晰 But the difference between G2 and G3 still needs to be tested, and the data for G1 and GG are already clear + if _config.ErupeConfig.RealClientMode >= _config.GG { + //Before GG, there was no data for G1, so there was no data for G1 except for ID and name + //But the difference between G2 and G3 still needs to be tested, and the data for G1 and GG are already clear bf.WriteUint32(0) // Unknown rank restrictions bf.WriteUint32(0) bf.WriteUint32(0) @@ -156,7 +157,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(0) // only 0 in known packet } ps.Uint8(bf, g.Name, true) - if _config.ErupeConfig.RealClientMode <= _config.GG { //小于等于GG的版本,每一条发送到名字就结束了 For versions less than or equal to GG, each message sent to the name ends + if _config.ErupeConfig.RealClientMode <= _config.GG { //For versions less than or equal to GG, each message sent to the name ends continue } ps.Uint8(bf, g.URLBanner, false) @@ -202,58 +203,31 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { for _, ge := range entries { var items []GachaItem if _config.ErupeConfig.RealClientMode <= _config.GG { - /* - 如果需要在三个选项中配置可选素材列表里, - 直接在gacha_entries中配置 - 同样的EntryType即可在GG中合并显示 - 此外奖品也直接在gacha_entries表中配置, - MHFG1~GG全程不使用gacha_items表,已满足MHFG功能更单一的抽奖功能 - 此外,本身MHFG功能就比较单一 - G1 ~ GG的抽奖配置 示例: - - If you need to configure the optional material list among the three options, - Configure directly in gacha_detries - The same Entry Type can be merged and displayed in GG - In addition, the prizes are also directly configured in the gacha-entries table, - MHFG1~GG does not use the gacha_items table throughout the entire process, which meets the lottery function of MHFG with a more single function - In addition, the MHFG function itself is relatively simple - Example of lottery configuration for G1~GG: - eg: - gachaname:test - entry: - itemgroup: - group1:(choose one of the two) ITEM_1_ID:7 COUNT:1 ITEM_1_ID:8 COUNT:2 - group2:ITEM_1_ID:9 COUNT:3 - reward: - reward1: ITEM_ID:1 COUNT:4 weight:10% - reward1: ITEM_ID:2 COUNT:5 weight:90% - - table:gacha_shop - |id|min_gr|min_hr|name|url_bannel|url_feature|url_thmubnail|wide|recommendded|gacha_type|hidden| - |1|0|0|test|null|null|null|f|f|3|t| - - table:gacha_entries - |id|gacha_id|entry_type|item_type|item_number|item_quantity|weight|rarity|rolls|frontier_points|daily_limit|name| - |1|1|0|7|7|1|0|0|0|0|0|null| - |4|1|0|7|8|2|0|0|0|0|0|null| - |5|1|1|7|9|3|0|0|0|0|0|null| - |8|1|100|7|1|4|1000|0|0|0|0|null| - |9|1|100|7|2|5|9000|0|0|0|0|null| - - */ - bf.WriteUint8(ge.EntryType) //0 - bf.WriteUint32(ge.ID) //1 - bf.WriteUint8(ge.ItemType) //5 - bf.WriteUint32(ge.ItemNumber) //6 - bf.WriteUint16(ge.ItemQuantity) //10 + // If you need to configure the optional material list among the three options,Configure directly in gacha_detries,The same Entry Type can be merged and displayed in GG,In addition, the prizes are also directly configured in the gacha-entries table, + // MHFG1~GG does not use the gacha_items table throughout the entire process, which meets the lottery function of MHFG with a more single function + // In addition, the MHFG function itself is relatively simple,Example of lottery configuration for G1~GG: + // eg: gachaname:test + // entry: itemgroup: group1:(choose one of the two) ITEM_1_ID:7 COUNT:1 ITEM_1_ID:8 COUNT:2group2:ITEM_1_ID:9 COUNT:3 ; reward:reward1: ITEM_ID:1 COUNT:4 weight:10% reward1: ITEM_ID:2 COUNT:5 weight:90% + // table:gacha_shop |1|0|0|test|null|null|null|f|f|3|f| + // table:gacha_entries + // |1|1|0|7|7|1|0|0|0|0|0|null| + // |4|1|0|7|8|2|0|0|0|0|0|null| + // |5|1|1|7|9|3|0|0|0|0|0|null| + // |8|1|100|7|1|4|1000|0|0|0|0|null| + // |9|1|100|7|2|5|9000|0|0|0|0|null| + bf.WriteUint8(ge.EntryType) + bf.WriteUint32(ge.ID) + bf.WriteUint8(ge.ItemType) + bf.WriteUint32(ge.ItemNumber) + bf.WriteUint16(ge.ItemQuantity) var weightPr uint16 - if gachaType >= 4 { // If box //12 概率 + if gachaType >= 4 { // If box weightPr = 1 } else { weightPr = uint16(ge.Weight / divisor) } - bf.WriteUint16(weightPr) //12 概率 - bf.WriteUint8(0) //14 结束符(maybe) + bf.WriteUint16(weightPr) + bf.WriteUint8(0) continue } bf.WriteUint8(ge.EntryType) From cca84415e4d9fbc9a2256693b0440ff0b956095a Mon Sep 17 00:00:00 2001 From: sin365 <353374337@qq.com> Date: Thu, 26 Feb 2026 18:00:58 +0800 Subject: [PATCH 4/5] follow the latest version of the repository and modify gacha for handlers_sthop.go --- server/channelserver/handlers_shop.go | 53 ++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/server/channelserver/handlers_shop.go b/server/channelserver/handlers_shop.go index c958e2287..0f5048d01 100644 --- a/server/channelserver/handlers_shop.go +++ b/server/channelserver/handlers_shop.go @@ -79,7 +79,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { switch pkt.ShopType { case 1: // Running gachas // Fundamentally, gacha works completely differently, just hide it for now. - if s.server.erupeConfig.RealClientMode <= cfg.G7 { + if s.server.erupeConfig.RealClientMode <= cfg.G1 { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } @@ -93,15 +93,22 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(uint16(len(gachas))) bf.WriteUint16(uint16(len(gachas))) for _, g := range gachas { - bf.WriteUint32(g.ID) - bf.WriteUint32(0) // Unknown rank restrictions - bf.WriteUint32(0) - bf.WriteUint32(0) - bf.WriteUint32(0) - bf.WriteUint32(g.MinGR) - bf.WriteUint32(g.MinHR) - bf.WriteUint32(0) // only 0 in known packet + if s.server.erupeConfig.RealClientMode >= cfg.GG{ + //Before GG, there was no data for G1, so there was no data for G1 except for ID and name + //But the difference between G2 and G3 still needs to be tested, and the data for G1 and GG are already clear + bf.WriteUint32(g.ID) + bf.WriteUint32(0) // Unknown rank restrictions + bf.WriteUint32(0) + bf.WriteUint32(0) + bf.WriteUint32(0) + bf.WriteUint32(g.MinGR) + bf.WriteUint32(g.MinHR) + bf.WriteUint32(0) // only 0 in known packet + } ps.Uint8(bf, g.Name, true) + if s.server.erupeConfig.RealClientMode <= cfg.GG{ { //For versions less than or equal to GG, each message sent to the name ends + continue + } ps.Uint8(bf, g.URLBanner, false) ps.Uint8(bf, g.URLFeature, false) if s.server.erupeConfig.RealClientMode >= cfg.G10 { @@ -138,6 +145,34 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(uint16(len(entries))) for _, ge := range entries { var items []GachaItem + if s.server.erupeConfig.RealClientMode <= cfg.GG { + // If you need to configure the optional material list among the three options,Configure directly in gacha_detries,The same Entry Type can be merged and displayed in GG,In addition, the prizes are also directly configured in the gacha-entries table, + // MHFG1~GG does not use the gacha_items table throughout the entire process, which meets the lottery function of MHFG with a more single function + // In addition, the MHFG function itself is relatively simple,Example of lottery configuration for G1~GG: + // eg: gachaname:test + // entry: itemgroup: group1:(choose one of the two) ITEM_1_ID:7 COUNT:1 ITEM_1_ID:8 COUNT:2group2:ITEM_1_ID:9 COUNT:3 ; reward:reward1: ITEM_ID:1 COUNT:4 weight:10% reward1: ITEM_ID:2 COUNT:5 weight:90% + // table:gacha_shop |1|0|0|test|null|null|null|f|f|3|f| + // table:gacha_entries + // |1|1|0|7|7|1|0|0|0|0|0|null| + // |4|1|0|7|8|2|0|0|0|0|0|null| + // |5|1|1|7|9|3|0|0|0|0|0|null| + // |8|1|100|7|1|4|1000|0|0|0|0|null| + // |9|1|100|7|2|5|9000|0|0|0|0|null| + bf.WriteUint8(ge.EntryType) + bf.WriteUint32(ge.ID) + bf.WriteUint8(ge.ItemType) + bf.WriteUint32(ge.ItemNumber) + bf.WriteUint16(ge.ItemQuantity) + var weightPr uint16 + if gachaType >= 4 { // If box + weightPr = 1 + } else { + weightPr = uint16(ge.Weight / divisor) + } + bf.WriteUint16(weightPr) + bf.WriteUint8(0) + continue + } bf.WriteUint8(ge.EntryType) bf.WriteUint32(ge.ID) bf.WriteUint8(ge.ItemType) From cf3fc3fed3c26725a65fcc96f958b39f1d7354c9 Mon Sep 17 00:00:00 2001 From: sin365 <353374337@qq.com> Date: Thu, 26 Feb 2026 18:09:51 +0800 Subject: [PATCH 5/5] gacha shop min version range:G1 --- server/channelserver/handlers_shop.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_shop.go b/server/channelserver/handlers_shop.go index 0f5048d01..b679f2a64 100644 --- a/server/channelserver/handlers_shop.go +++ b/server/channelserver/handlers_shop.go @@ -79,7 +79,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { switch pkt.ShopType { case 1: // Running gachas // Fundamentally, gacha works completely differently, just hide it for now. - if s.server.erupeConfig.RealClientMode <= cfg.G1 { + if s.server.erupeConfig.RealClientMode < cfg.G1 { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return }