From a6025be8b7a38b7a255513279ee14b3acc47b01d Mon Sep 17 00:00:00 2001 From: Houmgaor Date: Wed, 18 Mar 2026 23:19:25 +0100 Subject: [PATCH] fix(festa): filter trials and rewards for Forward.5 compatibility (#156) Skip trials referencing monsters added after em106 (Odibatorasu, the last F5 monster) and filter out item 7011 which does not exist before G1, preventing client crashes on Forward.4/5 servers. Also logs the pre-existing bookshelf save data pointer fix (already applied during the savedata refactor) in the CHANGELOG. --- CHANGELOG.md | 2 ++ server/channelserver/handlers_festa.go | 28 +++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98ecb7502..1ebddae10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed guild nil panics: added nil guards for alliance guild lookups ([#171](https://github.com/Mezeporta/Erupe/issues/171)) - Fixed `rasta_id=0` overwriting NULL in mercenary save, preventing game state saving ([#163](https://github.com/Mezeporta/Erupe/issues/163)) - Fixed false race condition in `PacketDuringLogout` test +- Fixed bookshelf save data pointers for non-ZZ client versions (G1–G10, F4–F5, S6.0) +- Fixed Forward.5 festa crashes: skip trials referencing monsters added after em106 (Odibatorasu) and filter out item 7011 which does not exist before G1 ([#156](https://github.com/Mezeporta/Erupe/pull/156)) ### Changed diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index 14cd52391..6b6be370b 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -212,6 +212,21 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { if err != nil { s.logger.Error("Failed to query festa trials", zap.Error(err)) } + // em106 (Odibatorasu) is the last monster added in Forward.5; skip trials + // referencing monsters or items that don't exist in that version. + if s.server.erupeConfig.RealClientMode <= cfg.F5 { + filtered := trials[:0] + for _, t := range trials { + if (t.Objective == 1 || t.Objective == 2 || t.Objective == 3) && t.GoalID > 106 { + continue + } + if t.Objective == 4 && t.GoalID > 6430 { + continue + } + filtered = append(filtered, t) + } + trials = filtered + } bf.WriteUint16(uint16(len(trials))) for _, trial := range trials { bf.WriteUint32(trial.ID) @@ -227,7 +242,6 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { } // The Winner and Loser Armor IDs are missing - // Item 7011 may not exist in older versions, remove to prevent crashes // Fields: {Unk0, Unk1, ItemType, Quantity, ItemID, MinHR, MinSR, MinGR} rewards := []FestaReward{ {1, 0, 7, 350, 1520, 0, 0, 0}, @@ -257,6 +271,18 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { //{5, 0, 1, 0, 0, 0, 0, 0}, } + // Item 7011 does not exist before G1 — filter it to prevent client crashes. + if s.server.erupeConfig.RealClientMode <= cfg.F5 { + filtered := rewards[:0] + for _, r := range rewards { + if r.ItemType == 7 && r.ItemID == 7011 { + continue + } + filtered = append(filtered, r) + } + rewards = filtered + } + bf.WriteUint16(uint16(len(rewards))) for _, reward := range rewards { bf.WriteUint8(reward.Unk0)