package gameserver import ( "encoding/json" "os" "sort" "sync" "gac-hkrpg-go/internal/common" goproto "gac-hkrpg-go/protocol/proto" ) // MissionUnlocks mirrors mission_unlocks.json (loaded once). type MissionUnlocks struct { FuncUnlockMissions []uint32 `json:"funcUnlockMissions"` AllMainMissions []uint32 `json:"allMainMissions"` TeleportIDs []uint32 `json:"teleportIds"` EntranceIDs []uint32 `json:"entranceIds"` RaidIDs []uint32 `json:"raidIds"` ChallengeRaidIDs []uint32 `json:"challengeRaidIds"` NpcIDs []uint32 `json:"npcIds"` SubMissionIDs []uint32 `json:"subMissionIds"` QuestIDs []uint32 `json:"questIds"` } var ( missionUnlocksOnce sync.Once missionUnlocks MissionUnlocks ) func getMissionUnlocks() *MissionUnlocks { missionUnlocksOnce.Do(func() { raw, err := os.ReadFile("mission_unlocks.json") if err != nil { Log("mission_unlocks.json not found: " + err.Error()) return } if err := json.Unmarshal(raw, &missionUnlocks); err != nil { Log("Failed to parse mission_unlocks.json: " + err.Error()) missionUnlocks = MissionUnlocks{} return } Log("Loaded mission_unlocks.json successfully") }) return &missionUnlocks } func init() { handle("GetMissionStatus", func(s *Session, req *goproto.GetMissionStatusCsReq, rsp *goproto.GetMissionStatusScRsp) error { rsp.Retcode = 0 unlocks := getMissionUnlocks() // main missions already finished: res.json + funcUnlockMissions + client-requested. // Without these the client Lua locks the HUD, teleports and Endgame. seen := map[uint32]bool{} var finished []uint32 add := func(ids []uint32) { for _, id := range ids { if id != 0 && !seen[id] { seen[id] = true finished = append(finished, id) } } } if res, err := common.GameRes(); err == nil { for _, m := range res.LevelOutputConfigs { for _, sc := range m { for _, grp := range sc.Scenes { add(grp.FinishedMainMissions) } } } } add(unlocks.FuncUnlockMissions) add(req.MainMissionIdList) sort.Slice(finished, func(i, j int) bool { return finished[i] < finished[j] }) rsp.FinishedMainMissionIdList = finished // beta client re-asks when this field is missing (tag 13) rsp.CurversionFinishedMainMissionIdList = finished for _, id := range req.SubMissionIdList { rsp.SubMissionStatusList = append(rsp.SubMissionStatusList, &goproto.Mission{ Id: id, Progress: 1, Status: goproto.MissionStatus_MISSION_FINISH, }) } SceneDebugLog("GET_MISSION_STATUS main_req=%d sub_req=%d finished_sent=%d", len(req.MainMissionIdList), len(req.SubMissionIdList), len(rsp.FinishedMainMissionIdList)) return nil }) handle("GetMissionData", func(s *Session, req *goproto.GetMissionDataCsReq, rsp *goproto.GetMissionDataScRsp) error { rsp.Retcode = 0 unlocks := getMissionUnlocks() seen := map[uint32]bool{} var finished []uint32 for _, id := range unlocks.FuncUnlockMissions { if id != 0 && !seen[id] { seen[id] = true finished = append(finished, id) } } sort.Slice(finished, func(i, j int) bool { return finished[i] < finished[j] }) for _, id := range finished { rsp.MainMissionList = append(rsp.MainMissionList, &goproto.MainMission{ Id: id, Status: goproto.MissionStatus_MISSION_FINISH, }) } rsp.FinishedMainMissionIdList = finished SceneDebugLog("GET_MISSION_DATA finished_sent=%d", len(rsp.FinishedMainMissionIdList)) return nil }) handle("GetQuestData", func(s *Session, req *goproto.GetQuestDataCsReq, rsp *goproto.GetQuestDataScRsp) error { rsp.Retcode = 0 unlocks := getMissionUnlocks() for _, id := range unlocks.QuestIDs { rsp.QuestList = append(rsp.QuestList, &goproto.Quest{ Id: id, Status: goproto.QuestStatus_QuestStatus_QuestFinish, Progress: 1, }) } SceneDebugLog("GET_QUEST_DATA quests_sent=%d", len(rsp.QuestList)) return nil }) // client Lua (_GetActiveGroupInfoByMissionCustomValue) reads this response; // dropping it blocks input. handle("GetMainMissionCustomValue", func(s *Session, req *goproto.GetMainMissionCustomValueCsReq, rsp *goproto.GetMainMissionCustomValueScRsp) error { rsp.Retcode = 0 for _, id := range req.SubMissionIdList { rsp.MainMissionList = append(rsp.MainMissionList, &goproto.MainMission{ Id: id, Status: goproto.MissionStatus_MISSION_FINISH, }) } SceneDebugLog("GET_MAIN_MISSION_CUSTOM_VALUE ids=%d", len(req.SubMissionIdList)) return nil }) handle("UpdateTrackMainMission", func(s *Session, req *goproto.UpdateTrackMainMissionCsReq, rsp *goproto.UpdateTrackMainMissionScRsp) error { rsp.Retcode = 0 rsp.TrackMissionId = req.TrackMissionId return nil }) handle("GetNpcMessageGroup", func(s *Session, req *goproto.GetNpcMessageGroupCsReq, rsp *goproto.GetNpcMessageGroupScRsp) error { rsp.Retcode = 0 return nil }) }