package gameserver import ( "fmt" "os" "reflect" "gac-hkrpg-go/internal/common" goproto "gac-hkrpg-go/protocol/proto" ) func syncPersistedExRanks(s *Session) error { // Publish changes after battle settlement; do not replace actor progression // while the client's combat scene is using its captured BattleInfo. if _, active := CURRENT_BATTLE.Load(s.Token); active { return nil } player := s.JSON() if player == nil { return nil } p, err := common.ReadPersistent() if os.IsNotExist(err) { return nil } if err != nil { return err } player.RefreshTechniqueSettings(p) changed := player.RefreshExRanks(p) if s.pendingExRankSync == nil { s.pendingExRankSync = make(map[uint32]bool) } for _, id := range changed { s.pendingExRankSync[id] = true } if len(s.pendingExRankSync) == 0 { return nil } changed = changed[:0] for id := range s.pendingExRankSync { changed = append(changed, id) } sortU32Slice(changed) notify := &goproto.PlayerSyncScNotify{} buildSync(player, notify, changed, nil, nil) notify.MaterialList, err = eidolonMaterials(player) if err != nil { return err } if err := s.Send(notify); err != nil { return err } Log(fmt.Sprintf("[EX-RANK SYNC] updated avatars=%v", changed)) s.pendingExRankSync = nil return nil } // A rank-only edit does not require deleting/recreating inventory. Preserve the // existing full-import path only when equipment, relics or avatar ownership change. func reloadPlayerData(s *Session, next *common.FreesrData) error { previous := s.JSON() materials, err := bagMaterials(next) if err != nil { return err } var changedMaterials []*goproto.Material if previous != nil { oldMaterials, err := bagMaterials(previous) if err != nil { return err } oldCounts := make(map[uint32]uint32, len(oldMaterials)) for _, item := range oldMaterials { oldCounts[item.Tid] = item.Num } for _, item := range materials { if old, exists := oldCounts[item.Tid]; !exists || old != item.Num { changedMaterials = append(changedMaterials, item) } } } full := previous == nil if !full { full = !reflect.DeepEqual(previous.Lightcones, next.Lightcones) || !reflect.DeepEqual(previous.Relics, next.Relics) || len(previous.Avatars) != len(next.Avatars) } var changed []uint32 if !full { for id, avatar := range next.Avatars { old, exists := previous.Avatars[id] if !exists { full = true break } if !reflect.DeepEqual(old, avatar) { changed = append(changed, id) } } } s.SetJSON(next) if full { return s.SyncPlayer() } if len(changed) == 0 && len(changedMaterials) == 0 { return nil } sortU32Slice(changed) notify := &goproto.PlayerSyncScNotify{} buildSync(next, notify, changed, nil, nil) notify.MaterialList = changedMaterials return s.Send(notify) }