using System; using System.Collections.Generic; using Game.Entities; using Framework.Database; using Framework.Constants; using Game.Arenas; using Game.Networking.Packets; namespace Game.Cache { public class CharacterCache : Singleton { Dictionary _characterCacheStore = new(); Dictionary _characterCacheByNameStore = new(); CharacterCache() { } public void LoadCharacterCacheStorage() { _characterCacheStore.Clear(); uint oldMSTime = Time.GetMSTime(); SQLResult result = DB.Characters.Query("SELECT guid, name, account, race, gender, class, level, deleteDate FROM characters"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "No character name data loaded, empty query"); return; } do { AddCharacterCacheEntry(ObjectGuid.Create(HighGuid.Player, result.Read(0)), result.Read(2), result.Read(1), result.Read(4), result.Read(3), result.Read(5), result.Read(6), result.Read(7) != 0); } while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, $"Loaded character infos for {_characterCacheStore.Count} characters in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); } public void AddCharacterCacheEntry(ObjectGuid guid, uint accountId, string name, byte gender, byte race, byte playerClass, byte level, bool isDeleted) { var data = new CharacterCacheEntry(); data.Guid = guid; data.Name = name; data.AccountId = accountId; data.RaceId = (Race)race; data.Sex = (Gender)gender; data.ClassId = (Class)playerClass; data.Level = level; data.GuildId = 0; // Will be set in guild loading or guild setting for (byte i = 0; i < SharedConst.MaxArenaSlot; ++i) data.ArenaTeamId[i] = 0; // Will be set in arena teams loading data.IsDeleted = isDeleted; // Fill Name to Guid Store if (!isDeleted) _characterCacheByNameStore[name] = data; _characterCacheStore[guid] = data; } public void DeleteCharacterCacheEntry(ObjectGuid guid, string name) { _characterCacheStore.Remove(guid); _characterCacheByNameStore.Remove(name); } public void UpdateCharacterData(ObjectGuid guid, string name, byte? gender = null, byte? race = null) { var characterCacheEntry = _characterCacheStore.LookupByKey(guid); if (characterCacheEntry == null) return; string oldName = characterCacheEntry.Name; characterCacheEntry.Name = name; if (gender.HasValue) characterCacheEntry.Sex = (Gender)gender.Value; if (race.HasValue) characterCacheEntry.RaceId = (Race)race.Value; InvalidatePlayer invalidatePlayer = new(); invalidatePlayer.Guid = guid; Global.WorldMgr.SendGlobalMessage(invalidatePlayer); // Correct name -> pointer storage _characterCacheByNameStore.Remove(oldName); _characterCacheByNameStore[name] = characterCacheEntry; } public void UpdateCharacterGender(ObjectGuid guid, byte gender) { if (!_characterCacheStore.ContainsKey(guid)) return; _characterCacheStore[guid].Sex = (Gender)gender; } public void UpdateCharacterLevel(ObjectGuid guid, byte level) { if (!_characterCacheStore.ContainsKey(guid)) return; _characterCacheStore[guid].Level = level; } public void UpdateCharacterAccountId(ObjectGuid guid, uint accountId) { if (!_characterCacheStore.ContainsKey(guid)) return; _characterCacheStore[guid].AccountId = accountId; } public void UpdateCharacterGuildId(ObjectGuid guid, ulong guildId) { if (!_characterCacheStore.ContainsKey(guid)) return; _characterCacheStore[guid].GuildId = guildId; } public void UpdateCharacterArenaTeamId(ObjectGuid guid, byte slot, uint arenaTeamId) { if (!_characterCacheStore.ContainsKey(guid)) return; _characterCacheStore[guid].ArenaTeamId[slot] = arenaTeamId; } public void UpdateCharacterInfoDeleted(ObjectGuid guid, bool deleted, string name) { if (!_characterCacheStore.TryGetValue(guid, out var cacheEntry)) return; if (deleted) _characterCacheByNameStore.Remove(cacheEntry.Name); else _characterCacheByNameStore[name] = cacheEntry; _characterCacheStore[guid].Name = name; _characterCacheStore[guid].IsDeleted = deleted; } public bool HasCharacterCacheEntry(ObjectGuid guid) { return _characterCacheStore.ContainsKey(guid); } public CharacterCacheEntry GetCharacterCacheByGuid(ObjectGuid guid) { return _characterCacheStore.LookupByKey(guid); } public CharacterCacheEntry GetCharacterCacheByName(string name) { return _characterCacheByNameStore.LookupByKey(name); } public ObjectGuid GetCharacterGuidByName(string name) { var characterCacheEntry = _characterCacheByNameStore.LookupByKey(name); if (characterCacheEntry != null) return characterCacheEntry.Guid; return ObjectGuid.Empty; } public bool GetCharacterNameByGuid(ObjectGuid guid, out string name) { name = "Unknown"; var characterCacheEntry = _characterCacheStore.LookupByKey(guid); if (characterCacheEntry == null) return false; name = characterCacheEntry.Name; return true; } public Team GetCharacterTeamByGuid(ObjectGuid guid) { var characterCacheEntry = _characterCacheStore.LookupByKey(guid); if (characterCacheEntry == null) return Team.Other; return Player.TeamForRace(characterCacheEntry.RaceId); } public uint GetCharacterAccountIdByGuid(ObjectGuid guid) { var characterCacheEntry = _characterCacheStore.LookupByKey(guid); if (characterCacheEntry == null) return 0; return characterCacheEntry.AccountId; } public uint GetCharacterAccountIdByName(string name) { var characterCacheEntry = _characterCacheByNameStore.LookupByKey(name); if (characterCacheEntry != null) return characterCacheEntry.AccountId; return 0; } public byte GetCharacterLevelByGuid(ObjectGuid guid) { var characterCacheEntry = _characterCacheStore.LookupByKey(guid); if (characterCacheEntry == null) return 0; return characterCacheEntry.Level; } public ulong GetCharacterGuildIdByGuid(ObjectGuid guid) { var characterCacheEntry = _characterCacheStore.LookupByKey(guid); if (characterCacheEntry == null) return 0; return characterCacheEntry.GuildId; } public uint GetCharacterArenaTeamIdByGuid(ObjectGuid guid, byte type) { var characterCacheEntry = _characterCacheStore.LookupByKey(guid); if (characterCacheEntry == null) return 0; return characterCacheEntry.ArenaTeamId[ArenaTeam.GetSlotByType(type)]; } public bool GetCharacterNameAndClassByGUID(ObjectGuid guid, out string name, out byte _class) { name = "Unknown"; _class = 0; var characterCacheEntry = _characterCacheStore.LookupByKey(guid); if (characterCacheEntry == null) return false; name = characterCacheEntry.Name; _class = (byte)characterCacheEntry.ClassId; return true; } } public class CharacterCacheEntry { public ObjectGuid Guid; public string Name; public uint AccountId; public Class ClassId; public Race RaceId; public Gender Sex; public byte Level; public ulong GuildId; public uint[] ArenaTeamId = new uint[SharedConst.MaxArenaSlot]; public bool IsDeleted; } }