public AccountInfo(SQLResult result) { Id = result.Read <uint>(0); Login = result.Read <string>(1); IsLockedToIP = result.Read <bool>(2); LockCountry = result.Read <string>(3); LastIP = result.Read <string>(4); LoginTicketExpiry = result.Read <uint>(5); IsBanned = result.Read <ulong>(6) != 0; IsPermanenetlyBanned = result.Read <ulong>(7) != 0; PasswordVerifier = result.Read <string>(9); Salt = result.Read <string>(10); GameAccounts = new Dictionary <uint, GameAccountInfo>(); const int GameAccountFieldsOffset = 8; do { var account = new GameAccountInfo(result.GetFields(), GameAccountFieldsOffset); GameAccounts[result.Read <uint>(GameAccountFieldsOffset)] = account; } while (result.NextRow()); }
void HandleAuthSessionCallback(AuthSession authSession, SQLResult result) { // Stop if the account is not found if (result.IsEmpty()) { Log.outError(LogFilter.Network, "HandleAuthSession: Sent Auth Response (unknown account)."); CloseSocket(); return; } AccountInfo account = new AccountInfo(result.GetFields()); // For hook purposes, we get Remoteaddress at this point. string address = GetRemoteIpAddress().ToString(); byte[] clientSeed = ClientTypeSeed_Win; if (account.game.OS == "Wn64") { clientSeed = ClientTypeSeed_Wn64; } else if (account.game.OS == "Mc64") { clientSeed = ClientTypeSeed_Mc64; } Sha256 digestKeyHash = new Sha256(); digestKeyHash.Process(account.game.SessionKey, account.game.SessionKey.Length); digestKeyHash.Finish(clientSeed, clientSeed.Length); HmacSha256 hmac = new HmacSha256(digestKeyHash.Digest); hmac.Process(authSession.LocalChallenge, authSession.LocalChallenge.Count); hmac.Process(_serverChallenge, 16); hmac.Finish(AuthCheckSeed, 16); // Check that Key and account name are the same on client and server if (!hmac.Digest.Compare(authSession.Digest)) { Log.outError(LogFilter.Network, "WorldSocket.HandleAuthSession: Authentication failed for account: {0} ('{1}') address: {2}", account.game.Id, authSession.RealmJoinTicket, address); CloseSocket(); return; } Sha256 keyData = new Sha256(); keyData.Finish(account.game.SessionKey, account.game.SessionKey.Length); HmacSha256 sessionKeyHmac = new HmacSha256(keyData.Digest); sessionKeyHmac.Process(_serverChallenge, 16); sessionKeyHmac.Process(authSession.LocalChallenge, authSession.LocalChallenge.Count); sessionKeyHmac.Finish(SessionKeySeed, 16); _sessionKey = new byte[40]; var sessionKeyGenerator = new SessionKeyGenerator(sessionKeyHmac.Digest, 32); sessionKeyGenerator.Generate(_sessionKey, 40); // As we don't know if attempted login process by ip works, we update last_attempt_ip right away PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_LAST_ATTEMPT_IP); stmt.AddValue(0, address); stmt.AddValue(1, authSession.RealmJoinTicket); DB.Login.Execute(stmt); // This also allows to check for possible "hack" attempts on account stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_ACCOUNT_INFO_CONTINUED_SESSION); stmt.AddValue(0, _sessionKey.ToHexString()); stmt.AddValue(1, account.game.Id); DB.Login.Execute(stmt); // First reject the connection if packet contains invalid data or realm state doesn't allow logging in if (Global.WorldMgr.IsClosed()) { SendAuthResponseError(BattlenetRpcErrorCode.Denied); Log.outError(LogFilter.Network, "WorldSocket.HandleAuthSession: World closed, denying client ({0}).", GetRemoteIpAddress()); CloseSocket(); return; } if (authSession.RealmID != Global.WorldMgr.GetRealm().Id.Realm) { SendAuthResponseError(BattlenetRpcErrorCode.Denied); Log.outError(LogFilter.Network, "WorldSocket.HandleAuthSession: Client {0} requested connecting with realm id {1} but this realm has id {2} set in config.", GetRemoteIpAddress().ToString(), authSession.RealmID, Global.WorldMgr.GetRealm().Id.Realm); CloseSocket(); return; } // Must be done before WorldSession is created bool wardenActive = WorldConfig.GetBoolValue(WorldCfg.WardenEnabled); if (wardenActive && account.game.OS != "Win" && account.game.OS != "Wn64" && account.game.OS != "Mc64") { SendAuthResponseError(BattlenetRpcErrorCode.Denied); Log.outError(LogFilter.Network, "WorldSocket.HandleAuthSession: Client {0} attempted to log in using invalid client OS ({1}).", address, account.game.OS); CloseSocket(); return; } //Re-check ip locking (same check as in auth). if (account.battleNet.IsLockedToIP) // if ip is locked { if (account.battleNet.LastIP != address) { SendAuthResponseError(BattlenetRpcErrorCode.RiskAccountLocked); Log.outDebug(LogFilter.Network, "HandleAuthSession: Sent Auth Response (Account IP differs)."); CloseSocket(); return; } } else if (!account.battleNet.LockCountry.IsEmpty() && account.battleNet.LockCountry != "00" && !_ipCountry.IsEmpty()) { if (account.battleNet.LockCountry != _ipCountry) { SendAuthResponseError(BattlenetRpcErrorCode.RiskAccountLocked); Log.outDebug(LogFilter.Network, "WorldSocket.HandleAuthSession: Sent Auth Response (Account country differs. Original country: {0}, new country: {1}).", account.battleNet.LockCountry, _ipCountry); CloseSocket(); return; } } long mutetime = account.game.MuteTime; //! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now. if (mutetime < 0) { mutetime = Time.UnixTime + mutetime; stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_MUTE_TIME_LOGIN); stmt.AddValue(0, mutetime); stmt.AddValue(1, account.game.Id); DB.Login.Execute(stmt); } if (account.IsBanned()) // if account banned { SendAuthResponseError(BattlenetRpcErrorCode.GameAccountBanned); Log.outError(LogFilter.Network, "WorldSocket:HandleAuthSession: Sent Auth Response (Account banned)."); CloseSocket(); return; } // Check locked state for server AccountTypes allowedAccountType = Global.WorldMgr.GetPlayerSecurityLimit(); if (allowedAccountType > AccountTypes.Player && account.game.Security < allowedAccountType) { SendAuthResponseError(BattlenetRpcErrorCode.ServerIsPrivate); Log.outInfo(LogFilter.Network, "WorldSocket:HandleAuthSession: User tries to login but his security level is not enough"); CloseSocket(); return; } Log.outDebug(LogFilter.Network, "WorldSocket:HandleAuthSession: Client '{0}' authenticated successfully from {1}.", authSession.RealmJoinTicket, address); // Update the last_ip in the database stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_LAST_IP); stmt.AddValue(0, address); stmt.AddValue(1, authSession.RealmJoinTicket); DB.Login.Execute(stmt); _worldSession = new WorldSession(account.game.Id, authSession.RealmJoinTicket, account.battleNet.Id, this, account.game.Security, (Expansion)account.game.Expansion, mutetime, account.game.OS, account.battleNet.Locale, account.game.Recruiter, account.game.IsRectuiter); // Initialize Warden system only if it is enabled by config //if (wardenActive) //_worldSession.InitWarden(_sessionKey); _queryProcessor.AddQuery(_worldSession.LoadPermissionsAsync().WithCallback(LoadSessionPermissionsCallback)); }
public void LoadGroups() { { uint oldMSTime = Time.GetMSTime(); // Delete all groups whose leader does not exist DB.Characters.DirectExecute("DELETE FROM groups WHERE leaderGuid NOT IN (SELECT guid FROM characters)"); // Delete all groups with less than 2 members DB.Characters.DirectExecute("DELETE FROM groups WHERE guid NOT IN (SELECT guid FROM group_member GROUP BY guid HAVING COUNT(guid) > 1)"); // 0 1 2 3 4 5 6 7 8 9 SQLResult result = DB.Characters.Query("SELECT g.leaderGuid, g.lootMethod, g.looterGuid, g.lootThreshold, g.icon1, g.icon2, g.icon3, g.icon4, g.icon5, g.icon6" + // 10 11 12 13 14 15 16 17 18 19 ", g.icon7, g.icon8, g.groupType, g.difficulty, g.raiddifficulty, g.legacyRaidDifficulty, g.masterLooterGuid, g.guid, lfg.dungeon, lfg.state FROM groups g LEFT JOIN lfg_data lfg ON lfg.guid = g.guid ORDER BY g.guid ASC"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 group definitions. DB table `groups` is empty!"); return; } uint count = 0; do { Group group = new Group(); group.LoadGroupFromDB(result.GetFields()); AddGroup(group); // Get the ID used for storing the group in the database and register it in the pool. uint storageId = group.GetDbStoreId(); RegisterGroupDbStoreId(storageId, group); // Increase the next available storage ID if (storageId == NextGroupDbStoreId) { NextGroupDbStoreId++; } ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} group definitions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } Log.outInfo(LogFilter.ServerLoading, "Loading Group members..."); { uint oldMSTime = Time.GetMSTime(); // Delete all rows from group_member or group_instance with no group DB.Characters.DirectExecute("DELETE FROM group_member WHERE guid NOT IN (SELECT guid FROM groups)"); DB.Characters.DirectExecute("DELETE FROM group_instance WHERE guid NOT IN (SELECT guid FROM groups)"); // Delete all members that does not exist DB.Characters.DirectExecute("DELETE FROM group_member WHERE memberGuid NOT IN (SELECT guid FROM characters)"); // 0 1 2 3 4 SQLResult result = DB.Characters.Query("SELECT guid, memberGuid, memberFlags, subgroup, roles FROM group_member ORDER BY guid"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 group members. DB table `group_member` is empty!"); return; } uint count = 0; do { Group group = GetGroupByDbStoreId(result.Read <uint>(0)); if (group) { group.LoadMemberFromDB(result.Read <uint>(1), result.Read <byte>(2), result.Read <byte>(3), (LfgRoles)result.Read <byte>(4)); } else { Log.outError(LogFilter.Server, "GroupMgr:LoadGroups: Consistency failed, can't find group (storage id: {0})", result.Read <uint>(0)); } ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} group members in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } Log.outInfo(LogFilter.ServerLoading, "Loading Group instance saves..."); { uint oldMSTime = Time.GetMSTime(); // 0 1 2 3 4 5 6 7 SQLResult result = DB.Characters.Query("SELECT gi.guid, i.map, gi.instance, gi.permanent, i.difficulty, i.resettime, i.entranceId, COUNT(g.guid) " + "FROM group_instance gi INNER JOIN instance i ON gi.instance = i.id " + "LEFT JOIN character_instance ci LEFT JOIN groups g ON g.leaderGuid = ci.guid ON ci.instance = gi.instance AND ci.permanent = 1 GROUP BY gi.instance ORDER BY gi.guid"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 group-instance saves. DB table `group_instance` is empty!"); return; } uint count = 0; do { Group group = GetGroupByDbStoreId(result.Read <uint>(0)); // group will never be NULL (we have run consistency sql's before loading) MapRecord mapEntry = CliDB.MapStorage.LookupByKey(result.Read <ushort>(1)); if (mapEntry == null || !mapEntry.IsDungeon()) { Log.outError(LogFilter.Sql, "Incorrect entry in group_instance table : no dungeon map {0}", result.Read <ushort>(1)); continue; } uint diff = result.Read <byte>(4); DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(diff); if (difficultyEntry == null || difficultyEntry.InstanceType != mapEntry.InstanceType) { continue; } InstanceSave save = Global.InstanceSaveMgr.AddInstanceSave(mapEntry.Id, result.Read <uint>(2), (Difficulty)diff, result.Read <uint>(5), result.Read <uint>(6), result.Read <ulong>(7) != 0, true); group.BindToInstance(save, result.Read <bool>(3), true); ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} group-instance saves in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } }
public void LoadGuilds() { Log.outInfo(LogFilter.ServerLoading, "Loading Guilds Definitions..."); { uint oldMSTime = Time.GetMSTime(); // 0 1 2 3 4 5 6 SQLResult result = DB.Characters.Query("SELECT g.guildid, g.name, g.leaderguid, g.EmblemStyle, g.EmblemColor, g.BorderStyle, g.BorderColor, " + // 7 8 9 10 11 12 "g.BackgroundColor, g.info, g.motd, g.createdate, g.BankMoney, COUNT(gbt.guildid) " + "FROM guild g LEFT JOIN guild_bank_tab gbt ON g.guildid = gbt.guildid GROUP BY g.guildid ORDER BY g.guildid ASC"); if (result.IsEmpty()) { Log.outError(LogFilter.Guild, "Loaded 0 guild definitions. DB table `guild` is empty."); return; } uint count = 0; do { Guild guild = new Guild(); if (!guild.LoadFromDB(result.GetFields())) { continue; } AddGuild(guild); count++; } while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} guild definitions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } Log.outInfo(LogFilter.ServerLoading, "Loading guild ranks..."); { uint oldMSTime = Time.GetMSTime(); // Delete orphaned guild rank entries before loading the valid ones DB.Characters.DirectExecute("DELETE gr FROM guild_rank gr LEFT JOIN guild g ON gr.guildId = g.guildId WHERE g.guildId IS NULL"); // 0 1 2 3 4 SQLResult result = DB.Characters.Query("SELECT guildid, rid, rname, rights, BankMoneyPerDay FROM guild_rank ORDER BY guildid ASC, rid ASC"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 guild ranks. DB table `guild_rank` is empty."); } else { uint count = 0; do { uint guildId = result.Read <uint>(0); Guild guild = GetGuildById(guildId); if (guild) { guild.LoadRankFromDB(result.GetFields()); } ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} guild ranks in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } } // 3. Load all guild members Log.outInfo(LogFilter.ServerLoading, "Loading guild members..."); { uint oldMSTime = Time.GetMSTime(); // Delete orphaned guild member entries before loading the valid ones DB.Characters.DirectExecute("DELETE gm FROM guild_member gm LEFT JOIN guild g ON gm.guildId = g.guildId WHERE g.guildId IS NULL"); DB.Characters.DirectExecute("DELETE gm FROM guild_member_withdraw gm LEFT JOIN guild_member g ON gm.guid = g.guid WHERE g.guid IS NULL"); // 0 1 2 3 4 5 6 7 8 9 10 SQLResult result = DB.Characters.Query("SELECT gm.guildid, gm.guid, rank, pnote, offnote, w.tab0, w.tab1, w.tab2, w.tab3, w.tab4, w.tab5, " + // 11 12 13 14 15 16 17 18 19 20 "w.tab6, w.tab7, w.money, c.name, c.level, c.class, c.gender, c.zone, c.account, c.logout_time " + "FROM guild_member gm LEFT JOIN guild_member_withdraw w ON gm.guid = w.guid " + "LEFT JOIN characters c ON c.guid = gm.guid ORDER BY gm.guildid ASC"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 guild members. DB table `guild_member` is empty."); } else { uint count = 0; do { uint guildId = result.Read <uint>(0); Guild guild = GetGuildById(guildId); if (guild) { guild.LoadMemberFromDB(result.GetFields()); } ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} guild members in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } } // 4. Load all guild bank tab rights Log.outInfo(LogFilter.ServerLoading, "Loading bank tab rights..."); { uint oldMSTime = Time.GetMSTime(); // Delete orphaned guild bank right entries before loading the valid ones DB.Characters.DirectExecute("DELETE gbr FROM guild_bank_right gbr LEFT JOIN guild g ON gbr.guildId = g.guildId WHERE g.guildId IS NULL"); // 0 1 2 3 4 SQLResult result = DB.Characters.Query("SELECT guildid, TabId, rid, gbright, SlotPerDay FROM guild_bank_right ORDER BY guildid ASC, TabId ASC"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 guild bank tab rights. DB table `guild_bank_right` is empty."); } else { uint count = 0; do { uint guildId = result.Read <uint>(0); Guild guild = GetGuildById(guildId); if (guild) { guild.LoadBankRightFromDB(result.GetFields()); } ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} bank tab rights in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } } // 5. Load all event logs Log.outInfo(LogFilter.ServerLoading, "Loading guild event logs..."); { uint oldMSTime = Time.GetMSTime(); DB.Characters.DirectExecute("DELETE FROM guild_eventlog WHERE LogGuid > {0}", GuildConst.EventLogMaxRecords); // 0 1 2 3 4 5 6 SQLResult result = DB.Characters.Query("SELECT guildid, LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp FROM guild_eventlog ORDER BY TimeStamp DESC, LogGuid DESC"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 guild event logs. DB table `guild_eventlog` is empty."); } else { uint count = 0; do { uint guildId = result.Read <uint>(0); Guild guild = GetGuildById(guildId); if (guild) { guild.LoadEventLogFromDB(result.GetFields()); } ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} guild event logs in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } } // 6. Load all bank event logs Log.outInfo(LogFilter.ServerLoading, "Loading guild bank event logs..."); { uint oldMSTime = Time.GetMSTime(); // Remove log entries that exceed the number of allowed entries per guild DB.Characters.DirectExecute("DELETE FROM guild_bank_eventlog WHERE LogGuid > {0}", GuildConst.BankLogMaxRecords); // 0 1 2 3 4 5 6 7 8 SQLResult result = DB.Characters.Query("SELECT guildid, TabId, LogGuid, EventType, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp FROM guild_bank_eventlog ORDER BY TimeStamp DESC, LogGuid DESC"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 guild bank event logs. DB table `guild_bank_eventlog` is empty."); } else { uint count = 0; do { uint guildId = result.Read <uint>(0); Guild guild = GetGuildById(guildId); if (guild) { guild.LoadBankEventLogFromDB(result.GetFields()); } ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} guild bank event logs in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } } // 7. Load all news event logs Log.outInfo(LogFilter.ServerLoading, "Loading Guild News..."); { uint oldMSTime = Time.GetMSTime(); DB.Characters.DirectExecute("DELETE FROM guild_newslog WHERE LogGuid > {0}", GuildConst.NewsLogMaxRecords); // 0 1 2 3 4 5 6 SQLResult result = DB.Characters.Query("SELECT guildid, LogGuid, EventType, PlayerGuid, Flags, Value, Timestamp FROM guild_newslog ORDER BY TimeStamp DESC, LogGuid DESC"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 guild event logs. DB table `guild_newslog` is empty."); } else { uint count = 0; do { uint guildId = result.Read <uint>(0); Guild guild = GetGuildById(guildId); if (guild) { guild.LoadGuildNewsLogFromDB(result.GetFields()); } ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} guild new logs in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } } // 8. Load all guild bank tabs Log.outInfo(LogFilter.ServerLoading, "Loading guild bank tabs..."); { uint oldMSTime = Time.GetMSTime(); // Delete orphaned guild bank tab entries before loading the valid ones DB.Characters.DirectExecute("DELETE gbt FROM guild_bank_tab gbt LEFT JOIN guild g ON gbt.guildId = g.guildId WHERE g.guildId IS NULL"); // 0 1 2 3 4 SQLResult result = DB.Characters.Query("SELECT guildid, TabId, TabName, TabIcon, TabText FROM guild_bank_tab ORDER BY guildid ASC, TabId ASC"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 guild bank tabs. DB table `guild_bank_tab` is empty."); } else { uint count = 0; do { uint guildId = result.Read <uint>(0); Guild guild = GetGuildById(guildId); if (guild) { guild.LoadBankTabFromDB(result.GetFields()); } ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} guild bank tabs in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } } // 9. Fill all guild bank tabs Log.outInfo(LogFilter.ServerLoading, "Filling bank tabs with items..."); { uint oldMSTime = Time.GetMSTime(); // Delete orphan guild bank items DB.Characters.DirectExecute("DELETE gbi FROM guild_bank_item gbi LEFT JOIN guild g ON gbi.guildId = g.guildId WHERE g.guildId IS NULL"); SQLResult result = DB.Characters.Query(DB.Characters.GetPreparedStatement(CharStatements.SEL_GUILD_BANK_ITEMS)); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 guild bank tab items. DB table `guild_bank_item` or `item_instance` is empty."); } else { uint count = 0; do { ulong guildId = result.Read <ulong>(45); Guild guild = GetGuildById(guildId); if (guild) { guild.LoadBankItemFromDB(result.GetFields()); } ++count; }while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} guild bank tab items in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } } // 10. Load guild achievements Log.outInfo(LogFilter.ServerLoading, "Loading guild achievements..."); { uint oldMSTime = Time.GetMSTime(); foreach (var pair in GuildStore) { PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_GUILD_ACHIEVEMENT); stmt.AddValue(0, pair.Key); SQLResult achievementResult = DB.Characters.Query(stmt); stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_GUILD_ACHIEVEMENT_CRITERIA); stmt.AddValue(0, pair.Key); SQLResult criteriaResult = DB.Characters.Query(stmt); pair.Value.GetAchievementMgr().LoadFromDB(achievementResult, criteriaResult); } Log.outInfo(LogFilter.ServerLoading, "Loaded guild achievements and criterias in {0} ms", Time.GetMSTimeDiffToNow(oldMSTime)); } // 11. Validate loaded guild data Log.outInfo(LogFilter.Server, "Validating data of loaded guilds..."); { uint oldMSTime = Time.GetMSTime(); foreach (var guild in GuildStore.ToList()) { if (!guild.Value.Validate()) { GuildStore.Remove(guild.Key); } } Log.outInfo(LogFilter.ServerLoading, "Validated data of loaded guilds in {0} ms", Time.GetMSTimeDiffToNow(oldMSTime)); } }