37d62e3a32
Port From (https://github.com/TrinityCore/TrinityCore/commit/cace41449771bbe692e1a45c4d38b15a12f6496e)
2754 lines
114 KiB
C#
2754 lines
114 KiB
C#
/*
|
|
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using Framework.Collections;
|
|
using Framework.Configuration;
|
|
using Framework.Constants;
|
|
using Framework.Database;
|
|
using Framework.Realm;
|
|
using Game.BattlePets;
|
|
using Game.Chat;
|
|
using Game.Collision;
|
|
using Game.DataStorage;
|
|
using Game.Entities;
|
|
using Game.Maps;
|
|
using Game.Networking;
|
|
using Game.Networking.Packets;
|
|
using Game.Spells;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Game
|
|
{
|
|
public class WorldManager : Singleton<WorldManager>
|
|
{
|
|
WorldManager()
|
|
{
|
|
foreach (WorldTimers timer in Enum.GetValues(typeof(WorldTimers)))
|
|
m_timers[timer] = new IntervalTimer();
|
|
|
|
m_allowedSecurityLevel = AccountTypes.Player;
|
|
|
|
_realm = new Realm();
|
|
|
|
_worldUpdateTime = new WorldUpdateTime();
|
|
_warnShutdownTime = GameTime.GetGameTime();
|
|
}
|
|
|
|
public Player FindPlayerInZone(uint zone)
|
|
{
|
|
foreach (var session in m_sessions)
|
|
{
|
|
Player player = session.Value.GetPlayer();
|
|
if (player == null)
|
|
continue;
|
|
|
|
if (player.IsInWorld && player.GetZoneId() == zone)
|
|
{
|
|
// Used by the weather system. We return the player to broadcast the change weather message to him and all players in the zone.
|
|
return player;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public bool IsClosed()
|
|
{
|
|
return m_isClosed;
|
|
}
|
|
|
|
public void SetClosed(bool val)
|
|
{
|
|
m_isClosed = val;
|
|
Global.ScriptMgr.OnOpenStateChange(!val);
|
|
}
|
|
|
|
public void LoadDBAllowedSecurityLevel()
|
|
{
|
|
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_REALMLIST_SECURITY_LEVEL);
|
|
stmt.AddValue(0, (int)_realm.Id.Index);
|
|
SQLResult result = DB.Login.Query(stmt);
|
|
|
|
if (!result.IsEmpty())
|
|
SetPlayerSecurityLimit((AccountTypes)result.Read<byte>(0));
|
|
}
|
|
|
|
public void SetPlayerSecurityLimit(AccountTypes _sec)
|
|
{
|
|
AccountTypes sec = _sec < AccountTypes.Console ? _sec : AccountTypes.Player;
|
|
bool update = sec > m_allowedSecurityLevel;
|
|
m_allowedSecurityLevel = sec;
|
|
if (update)
|
|
KickAllLess(m_allowedSecurityLevel);
|
|
}
|
|
|
|
public void SetMotd(string motd)
|
|
{
|
|
Global.ScriptMgr.OnMotdChange(motd);
|
|
|
|
m_motd.Clear();
|
|
m_motd.AddRange(motd.Split('@'));
|
|
}
|
|
|
|
public List<string> GetMotd()
|
|
{
|
|
return m_motd;
|
|
}
|
|
|
|
public void TriggerGuidWarning()
|
|
{
|
|
// Lock this only to prevent multiple maps triggering at the same time
|
|
lock (_guidAlertLock)
|
|
{
|
|
long gameTime = GameTime.GetGameTime();
|
|
long today = (gameTime / Time.Day) * Time.Day;
|
|
|
|
// Check if our window to restart today has passed. 5 mins until quiet time
|
|
while (gameTime >= Time.GetLocalHourTimestamp(today, WorldConfig.GetUIntValue(WorldCfg.RespawnRestartQuietTime)) - 1810)
|
|
today += Time.Day;
|
|
|
|
// Schedule restart for 30 minutes before quiet time, or as long as we have
|
|
_warnShutdownTime = Time.GetLocalHourTimestamp(today, WorldConfig.GetUIntValue(WorldCfg.RespawnRestartQuietTime)) - 1800;
|
|
|
|
_guidWarn = true;
|
|
SendGuidWarning();
|
|
}
|
|
}
|
|
|
|
public void TriggerGuidAlert()
|
|
{
|
|
// Lock this only to prevent multiple maps triggering at the same time
|
|
lock (_guidAlertLock)
|
|
{
|
|
DoGuidAlertRestart();
|
|
_guidAlert = true;
|
|
_guidWarn = false;
|
|
}
|
|
}
|
|
|
|
void DoGuidWarningRestart()
|
|
{
|
|
if (m_ShutdownTimer != 0)
|
|
return;
|
|
|
|
ShutdownServ(1800, ShutdownMask.Restart, ShutdownExitCode.Restart);
|
|
_warnShutdownTime += Time.Hour;
|
|
}
|
|
|
|
void DoGuidAlertRestart()
|
|
{
|
|
if (m_ShutdownTimer != 0)
|
|
return;
|
|
|
|
ShutdownServ(300, ShutdownMask.Restart, ShutdownExitCode.Restart, _alertRestartReason);
|
|
}
|
|
|
|
void SendGuidWarning()
|
|
{
|
|
if (m_ShutdownTimer == 0 && _guidWarn && WorldConfig.GetIntValue(WorldCfg.RespawnGuidWarningFrequency) > 0)
|
|
SendServerMessage(ServerMessageType.String, _guidWarningMsg);
|
|
_warnDiff = 0;
|
|
}
|
|
|
|
public WorldSession FindSession(uint id)
|
|
{
|
|
return m_sessions.LookupByKey(id);
|
|
}
|
|
|
|
bool RemoveSession(uint id)
|
|
{
|
|
// Find the session, kick the user, but we can't delete session at this moment to prevent iterator invalidation
|
|
var session = m_sessions.LookupByKey(id);
|
|
|
|
if (session != null)
|
|
{
|
|
if (session.PlayerLoading())
|
|
return false;
|
|
|
|
session.KickPlayer("World::RemoveSession");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void AddSession(WorldSession s)
|
|
{
|
|
addSessQueue.Enqueue(s);
|
|
}
|
|
|
|
public void AddInstanceSocket(WorldSocket sock, ulong connectToKey)
|
|
{
|
|
_linkSocketQueue.Enqueue(Tuple.Create(sock, connectToKey));
|
|
}
|
|
|
|
void AddSession_(WorldSession s)
|
|
{
|
|
Cypher.Assert(s != null);
|
|
|
|
//NOTE - Still there is race condition in WorldSession* being used in the Sockets
|
|
|
|
// kick already loaded player with same account (if any) and remove session
|
|
// if player is in loading and want to load again, return
|
|
if (!RemoveSession(s.GetAccountId()))
|
|
{
|
|
s.KickPlayer("World::AddSession_ Couldn't remove the other session while on loading screen");
|
|
return;
|
|
}
|
|
|
|
// decrease session counts only at not reconnection case
|
|
bool decrease_session = true;
|
|
|
|
// if session already exist, prepare to it deleting at next world update
|
|
// NOTE - KickPlayer() should be called on "old" in RemoveSession()
|
|
{
|
|
var old = m_sessions.LookupByKey(s.GetAccountId());
|
|
if (old != null)
|
|
{
|
|
// prevent decrease sessions count if session queued
|
|
if (RemoveQueuedPlayer(old))
|
|
decrease_session = false;
|
|
|
|
m_sessionsByBnetGuid.Remove(old.GetBattlenetAccountGUID(), old);
|
|
}
|
|
}
|
|
|
|
m_sessions[s.GetAccountId()] = s;
|
|
m_sessionsByBnetGuid.Add(s.GetBattlenetAccountGUID(), s);
|
|
|
|
int Sessions = GetActiveAndQueuedSessionCount();
|
|
uint pLimit = GetPlayerAmountLimit();
|
|
int QueueSize = GetQueuedSessionCount(); //number of players in the queue
|
|
|
|
//so we don't count the user trying to
|
|
//login as a session and queue the socket that we are using
|
|
if (decrease_session)
|
|
--Sessions;
|
|
|
|
if (pLimit > 0 && Sessions >= pLimit && !s.HasPermission(RBACPermissions.SkipQueue) && !HasRecentlyDisconnected(s))
|
|
{
|
|
AddQueuedPlayer(s);
|
|
UpdateMaxSessionCounters();
|
|
Log.outInfo(LogFilter.Server, "PlayerQueue: Account id {0} is in Queue Position ({1}).", s.GetAccountId(), ++QueueSize);
|
|
return;
|
|
}
|
|
|
|
s.InitializeSession();
|
|
|
|
UpdateMaxSessionCounters();
|
|
|
|
// Updates the population
|
|
if (pLimit > 0)
|
|
{
|
|
float popu = GetActiveSessionCount(); // updated number of users on the server
|
|
popu /= pLimit;
|
|
popu *= 2;
|
|
Log.outInfo(LogFilter.Server, "Server Population ({0}).", popu);
|
|
}
|
|
}
|
|
|
|
void ProcessLinkInstanceSocket(Tuple<WorldSocket, ulong> linkInfo)
|
|
{
|
|
if (!linkInfo.Item1.IsOpen())
|
|
return;
|
|
|
|
ConnectToKey key = new();
|
|
key.Raw = linkInfo.Item2;
|
|
|
|
WorldSession session = FindSession(key.AccountId);
|
|
if (!session || session.GetConnectToInstanceKey() != linkInfo.Item2)
|
|
{
|
|
linkInfo.Item1.SendAuthResponseError(BattlenetRpcErrorCode.TimedOut);
|
|
linkInfo.Item1.CloseSocket();
|
|
return;
|
|
}
|
|
|
|
linkInfo.Item1.SetWorldSession(session);
|
|
session.AddInstanceConnection(linkInfo.Item1);
|
|
session.HandleContinuePlayerLogin();
|
|
}
|
|
|
|
bool HasRecentlyDisconnected(WorldSession session)
|
|
{
|
|
if (session == null)
|
|
return false;
|
|
uint tolerance = 0;
|
|
if (tolerance != 0)
|
|
{
|
|
foreach (var disconnect in m_disconnects)
|
|
{
|
|
if ((disconnect.Value - GameTime.GetGameTime()) < tolerance)
|
|
{
|
|
if (disconnect.Key == session.GetAccountId())
|
|
return true;
|
|
}
|
|
else
|
|
m_disconnects.Remove(disconnect.Key);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint GetQueuePos(WorldSession sess)
|
|
{
|
|
uint position = 1;
|
|
|
|
foreach (var iter in m_QueuedPlayer)
|
|
{
|
|
if (iter != sess)
|
|
++position;
|
|
else
|
|
return position;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void AddQueuedPlayer(WorldSession sess)
|
|
{
|
|
sess.SetInQueue(true);
|
|
m_QueuedPlayer.Add(sess);
|
|
|
|
// The 1st SMSG_AUTH_RESPONSE needs to contain other info too.
|
|
sess.SendAuthResponse(BattlenetRpcErrorCode.Ok, true, GetQueuePos(sess));
|
|
}
|
|
|
|
bool RemoveQueuedPlayer(WorldSession sess)
|
|
{
|
|
// sessions count including queued to remove (if removed_session set)
|
|
int sessions = GetActiveSessionCount();
|
|
|
|
uint position = 1;
|
|
|
|
// search to remove and count skipped positions
|
|
bool found = false;
|
|
|
|
foreach (var iter in m_QueuedPlayer)
|
|
{
|
|
if (iter != sess)
|
|
++position;
|
|
else
|
|
{
|
|
sess.SetInQueue(false);
|
|
sess.ResetTimeOutTime(false);
|
|
m_QueuedPlayer.Remove(iter);
|
|
found = true; // removing queued session
|
|
break;
|
|
}
|
|
}
|
|
|
|
// iter point to next socked after removed or end()
|
|
// position store position of removed socket and then new position next socket after removed
|
|
|
|
// if session not queued then we need decrease sessions count
|
|
if (!found && sessions != 0)
|
|
--sessions;
|
|
|
|
// accept first in queue
|
|
if ((m_playerLimit == 0 || sessions < m_playerLimit) && !m_QueuedPlayer.Empty())
|
|
{
|
|
WorldSession pop_sess = m_QueuedPlayer.First();
|
|
pop_sess.InitializeSession();
|
|
|
|
m_QueuedPlayer.RemoveAt(0);
|
|
|
|
// update iter to point first queued socket or end() if queue is empty now
|
|
position = 1;
|
|
}
|
|
|
|
// update position from iter to end()
|
|
// iter point to first not updated socket, position store new position
|
|
foreach (var iter in m_QueuedPlayer)
|
|
{
|
|
iter.SendAuthWaitQueue(++position);
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
public void SetInitialWorldSettings()
|
|
{
|
|
LoadRealmInfo();
|
|
|
|
Log.SetRealmId(_realm.Id.Index);
|
|
|
|
LoadConfigSettings();
|
|
|
|
// Initialize Allowed Security Level
|
|
LoadDBAllowedSecurityLevel();
|
|
|
|
Global.ObjectMgr.SetHighestGuids();
|
|
|
|
if (!Global.MapMgr.ExistMapAndVMap(0, -6240.32f, 331.033f) || !Global.MapMgr.ExistMapAndVMap(0, -8949.95f, -132.493f)
|
|
|| !Global.MapMgr.ExistMapAndVMap(1, -618.518f, -4251.67f) || !Global.MapMgr.ExistMapAndVMap(0, 1676.35f, 1677.45f)
|
|
|| !Global.MapMgr.ExistMapAndVMap(1, 10311.3f, 832.463f) || !Global.MapMgr.ExistMapAndVMap(1, -2917.58f, -257.98f)
|
|
|| (WorldConfig.GetIntValue(WorldCfg.Expansion) != 0 && (!Global.MapMgr.ExistMapAndVMap(530, 10349.6f, -6357.29f) || !Global.MapMgr.ExistMapAndVMap(530, -3961.64f, -13931.2f))))
|
|
{
|
|
Log.outError(LogFilter.ServerLoading, "Unable to load critical files - server shutting down !!!");
|
|
Environment.Exit(1);
|
|
}
|
|
|
|
// Initialize pool manager
|
|
Global.PoolMgr.Initialize();
|
|
|
|
// Initialize game event manager
|
|
Global.GameEventMgr.Initialize();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Cypher Strings...");
|
|
Global.ObjectMgr.LoadCypherStrings();
|
|
|
|
// not send custom type REALM_FFA_PVP to realm list
|
|
RealmType server_type = IsFFAPvPRealm() ? RealmType.PVP : (RealmType)WorldConfig.GetIntValue(WorldCfg.GameType);
|
|
uint realm_zone = WorldConfig.GetUIntValue(WorldCfg.RealmZone);
|
|
|
|
DB.Login.Execute("UPDATE realmlist SET icon = {0}, timezone = {1} WHERE id = '{2}'", (byte)server_type, realm_zone, _realm.Id.Index); // One-time query
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Initialize DataStorage...");
|
|
// Load DB2s
|
|
m_availableDbcLocaleMask = CliDB.LoadStores(_dataPath, m_defaultDbcLocale);
|
|
if (m_availableDbcLocaleMask == null || !m_availableDbcLocaleMask[(int)m_defaultDbcLocale])
|
|
{
|
|
Log.outFatal(LogFilter.ServerLoading, $"Unable to load db2 files for {m_defaultDbcLocale} locale specified in DBC.Locale config!");
|
|
Environment.Exit(1);
|
|
}
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading hotfix blobs...");
|
|
Global.DB2Mgr.LoadHotfixBlob(m_availableDbcLocaleMask);
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading hotfix info...");
|
|
Global.DB2Mgr.LoadHotfixData();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading hotfix optional data...");
|
|
Global.DB2Mgr.LoadHotfixOptionalData(m_availableDbcLocaleMask);
|
|
|
|
//- Load M2 fly by cameras
|
|
M2Storage.LoadM2Cameras(_dataPath);
|
|
|
|
//- Load GameTables
|
|
CliDB.LoadGameTables(_dataPath);
|
|
|
|
//Load weighted graph on taxi nodes path
|
|
Global.TaxiPathGraph.Initialize();
|
|
|
|
MultiMap<uint, uint> mapData = new();
|
|
foreach (MapRecord mapEntry in CliDB.MapStorage.Values)
|
|
{
|
|
if (mapEntry.ParentMapID != -1)
|
|
{
|
|
Cypher.Assert(mapEntry.CosmeticParentMapID == -1 || mapEntry.ParentMapID == mapEntry.CosmeticParentMapID, $"Inconsistent parent map data for map {mapEntry.Id} (ParentMapID = {mapEntry.ParentMapID}, CosmeticParentMapID = {mapEntry.CosmeticParentMapID})");
|
|
mapData.Add((uint)mapEntry.ParentMapID, mapEntry.Id);
|
|
}
|
|
else if (mapEntry.CosmeticParentMapID != -1)
|
|
mapData.Add((uint)mapEntry.CosmeticParentMapID, mapEntry.Id);
|
|
}
|
|
|
|
Global.MapMgr.InitializeParentMapData(mapData);
|
|
|
|
Global.VMapMgr.Initialize(mapData);
|
|
Global.MMapMgr.Initialize(mapData);
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading SpellInfo Storage...");
|
|
Global.SpellMgr.LoadSpellInfoStore();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading serverside spells...");
|
|
Global.SpellMgr.LoadSpellInfoServerside();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading SpellInfo corrections...");
|
|
Global.SpellMgr.LoadSpellInfoCorrections();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading SkillLineAbility Data...");
|
|
Global.SpellMgr.LoadSkillLineAbilityMap();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading SpellInfo custom attributes...");
|
|
Global.SpellMgr.LoadSpellInfoCustomAttributes();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading SpellInfo diminishing infos...");
|
|
Global.SpellMgr.LoadSpellInfoDiminishing();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading SpellInfo immunity infos...");
|
|
Global.SpellMgr.LoadSpellInfoImmunities();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading PetFamilySpellsStore Data...");
|
|
Global.SpellMgr.LoadPetFamilySpellsStore();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Totem models...");
|
|
Global.SpellMgr.LoadSpellTotemModel();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading languages...");
|
|
Global.LanguageMgr.LoadLanguages();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading languages words...");
|
|
Global.LanguageMgr.LoadLanguagesWords();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GameObject models...");
|
|
GameObjectModel.LoadGameObjectModelList();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Instance Template...");
|
|
Global.ObjectMgr.LoadInstanceTemplate();
|
|
|
|
// Must be called before `respawn` data
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading instances...");
|
|
Global.InstanceSaveMgr.LoadInstances();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Localization strings...");
|
|
uint oldMSTime = Time.GetMSTime();
|
|
Global.ObjectMgr.LoadCreatureLocales();
|
|
Global.ObjectMgr.LoadGameObjectLocales();
|
|
Global.ObjectMgr.LoadQuestTemplateLocale();
|
|
Global.ObjectMgr.LoadQuestOfferRewardLocale();
|
|
Global.ObjectMgr.LoadQuestRequestItemsLocale();
|
|
Global.ObjectMgr.LoadQuestObjectivesLocale();
|
|
Global.ObjectMgr.LoadPageTextLocales();
|
|
Global.ObjectMgr.LoadGossipMenuItemsLocales();
|
|
Global.ObjectMgr.LoadPointOfInterestLocales();
|
|
Log.outInfo(LogFilter.ServerLoading, "Localization strings loaded in {0} ms", Time.GetMSTimeDiffToNow(oldMSTime));
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Account Roles and Permissions...");
|
|
Global.AccountMgr.LoadRBAC();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Page Texts...");
|
|
Global.ObjectMgr.LoadPageTexts();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GameObject Template...");
|
|
Global.ObjectMgr.LoadGameObjectTemplate();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Game Object template addons...");
|
|
Global.ObjectMgr.LoadGameObjectTemplateAddons();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Transport Templates...");
|
|
Global.TransportMgr.LoadTransportTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Transport animations and rotations...");
|
|
Global.TransportMgr.LoadTransportAnimationAndRotation();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Transport spawns...");
|
|
Global.TransportMgr.LoadTransportSpawns();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Rank Data...");
|
|
Global.SpellMgr.LoadSpellRanks();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Required Data...");
|
|
Global.SpellMgr.LoadSpellRequired();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Group types...");
|
|
Global.SpellMgr.LoadSpellGroups();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Learn Skills...");
|
|
Global.SpellMgr.LoadSpellLearnSkills();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading SpellInfo SpellSpecific and AuraState...");
|
|
Global.SpellMgr.LoadSpellInfoSpellSpecificAndAuraState();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Learn Spells...");
|
|
Global.SpellMgr.LoadSpellLearnSpells();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Proc conditions and data...");
|
|
Global.SpellMgr.LoadSpellProcs();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Aggro Spells Definitions...");
|
|
Global.SpellMgr.LoadSpellThreats();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Group Stack Rules...");
|
|
Global.SpellMgr.LoadSpellGroupStackRules();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading NPC Texts...");
|
|
Global.ObjectMgr.LoadNPCText();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Enchant Spells Proc datas...");
|
|
Global.SpellMgr.LoadSpellEnchantProcData();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Random item bonus list definitions...");
|
|
ItemEnchantmentManager.LoadItemRandomBonusListTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Disables"); // must be before loading quests and items
|
|
Global.DisableMgr.LoadDisables();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Items..."); // must be after LoadRandomEnchantmentsTable and LoadPageTexts
|
|
Global.ObjectMgr.LoadItemTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Item set names..."); // must be after LoadItemPrototypes
|
|
Global.ObjectMgr.LoadItemTemplateAddon();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Item Scripts..."); // must be after LoadItemPrototypes
|
|
Global.ObjectMgr.LoadItemScriptNames();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Model Based Info Data...");
|
|
Global.ObjectMgr.LoadCreatureModelInfo();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature templates...");
|
|
Global.ObjectMgr.LoadCreatureTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Equipment templates...");
|
|
Global.ObjectMgr.LoadEquipmentTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template addons...");
|
|
Global.ObjectMgr.LoadCreatureTemplateAddons();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template scaling...");
|
|
Global.ObjectMgr.LoadCreatureScalingData();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Reputation Reward Rates...");
|
|
Global.ObjectMgr.LoadReputationRewardRate();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Reputation OnKill Data...");
|
|
Global.ObjectMgr.LoadReputationOnKill();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Reputation Spillover Data...");
|
|
Global.ObjectMgr.LoadReputationSpilloverTemplate();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Points Of Interest Data...");
|
|
Global.ObjectMgr.LoadPointsOfInterest();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Base Stats...");
|
|
Global.ObjectMgr.LoadCreatureClassLevelStats();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spawn Group Templates...");
|
|
Global.ObjectMgr.LoadSpawnGroupTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading instance spawn groups...");
|
|
Global.ObjectMgr.LoadInstanceSpawnGroups();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Data...");
|
|
Global.ObjectMgr.LoadCreatures();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Temporary Summon Data...");
|
|
Global.ObjectMgr.LoadTempSummons(); // must be after LoadCreatureTemplates() and LoadGameObjectTemplates()
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading pet levelup spells...");
|
|
Global.SpellMgr.LoadPetLevelupSpellMap();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading pet default spells additional to levelup spells...");
|
|
Global.SpellMgr.LoadPetDefaultSpells();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Addon Data...");
|
|
Global.ObjectMgr.LoadCreatureAddons();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Movement Overrides...");
|
|
Global.ObjectMgr.LoadCreatureMovementOverrides(); // must be after LoadCreatures()
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GameObjects...");
|
|
Global.ObjectMgr.LoadGameObjects();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spawn Group Data...");
|
|
Global.ObjectMgr.LoadSpawnGroups();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GameObject Addon Data...");
|
|
Global.ObjectMgr.LoadGameObjectAddons(); // must be after LoadGameObjects()
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GameObject faction and flags overrides...");
|
|
Global.ObjectMgr.LoadGameObjectOverrides(); // must be after LoadGameObjects()
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GameObject Quest Items...");
|
|
Global.ObjectMgr.LoadGameObjectQuestItems();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Quest Items...");
|
|
Global.ObjectMgr.LoadCreatureQuestItems();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Linked Respawn...");
|
|
Global.ObjectMgr.LoadLinkedRespawn(); // must be after LoadCreatures(), LoadGameObjects()
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Weather Data...");
|
|
Global.WeatherMgr.LoadWeatherData();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Quests...");
|
|
Global.ObjectMgr.LoadQuests();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Checking Quest Disables");
|
|
Global.DisableMgr.CheckQuestDisables(); // must be after loading quests
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Quest POI");
|
|
Global.ObjectMgr.LoadQuestPOI();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Quests Starters and Enders...");
|
|
Global.ObjectMgr.LoadQuestStartersAndEnders(); // must be after quest load
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Quest Greetings...");
|
|
Global.ObjectMgr.LoadQuestGreetings();
|
|
Global.ObjectMgr.LoadQuestGreetingLocales();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Objects Pooling Data...");
|
|
Global.PoolMgr.LoadFromDB();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Quest Pooling Data...");
|
|
Global.QuestPoolMgr.LoadFromDB(); // must be after quest templates
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Game Event Data..."); // must be after loading pools fully
|
|
Global.GameEventMgr.LoadFromDB();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading NPCSpellClick Data..."); // must be after LoadQuests
|
|
Global.ObjectMgr.LoadNPCSpellClickSpells();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Vehicle Templates...");
|
|
Global.ObjectMgr.LoadVehicleTemplate(); // must be after LoadCreatureTemplates()
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Vehicle Template Accessories...");
|
|
Global.ObjectMgr.LoadVehicleTemplateAccessories(); // must be after LoadCreatureTemplates() and LoadNPCSpellClickSpells()
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Vehicle Accessories...");
|
|
Global.ObjectMgr.LoadVehicleAccessories(); // must be after LoadCreatureTemplates() and LoadNPCSpellClickSpells()
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Vehicle Seat Addon Data...");
|
|
Global.ObjectMgr.LoadVehicleSeatAddon(); // must be after loading DBC
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading SpellArea Data..."); // must be after quest load
|
|
Global.SpellMgr.LoadSpellAreas();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading World locations...");
|
|
Global.ObjectMgr.LoadWorldSafeLocs(); // must be before LoadAreaTriggerTeleports and LoadGraveyardZones
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading AreaTrigger definitions...");
|
|
Global.ObjectMgr.LoadAreaTriggerTeleports();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Access Requirements...");
|
|
Global.ObjectMgr.LoadAccessRequirements(); // must be after item template load
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Quest Area Triggers...");
|
|
Global.ObjectMgr.LoadQuestAreaTriggers(); // must be after LoadQuests
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Tavern Area Triggers...");
|
|
Global.ObjectMgr.LoadTavernAreaTriggers();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading AreaTrigger script names...");
|
|
Global.ObjectMgr.LoadAreaTriggerScripts();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading LFG entrance positions..."); // Must be after areatriggers
|
|
Global.LFGMgr.LoadLFGDungeons();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Dungeon boss data...");
|
|
Global.ObjectMgr.LoadInstanceEncounters();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading LFG rewards...");
|
|
Global.LFGMgr.LoadRewards();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Graveyard-zone links...");
|
|
Global.ObjectMgr.LoadGraveyardZones();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading spell pet auras...");
|
|
Global.SpellMgr.LoadSpellPetAuras();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Spell target coordinates...");
|
|
Global.SpellMgr.LoadSpellTargetPositions();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading linked spells...");
|
|
Global.SpellMgr.LoadSpellLinked();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Scenes Templates..."); // must be before LoadPlayerInfo
|
|
Global.ObjectMgr.LoadSceneTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Player Create Data...");
|
|
Global.ObjectMgr.LoadPlayerInfo();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Exploration BaseXP Data...");
|
|
Global.ObjectMgr.LoadExplorationBaseXP();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Pet Name Parts...");
|
|
Global.ObjectMgr.LoadPetNames();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading AreaTrigger Templates...");
|
|
Global.AreaTriggerDataStorage.LoadAreaTriggerTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading AreaTrigger Spawns...");
|
|
Global.AreaTriggerDataStorage.LoadAreaTriggerSpawns();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Conversation Templates...");
|
|
Global.ConversationDataStorage.LoadConversationTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Player Choices...");
|
|
Global.ObjectMgr.LoadPlayerChoices();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Player Choices Locales...");
|
|
Global.ObjectMgr.LoadPlayerChoicesLocale();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Jump Charge Params...");
|
|
Global.ObjectMgr.LoadJumpChargeParams();
|
|
|
|
CharacterDatabaseCleaner.CleanDatabase();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading the max pet number...");
|
|
Global.ObjectMgr.LoadPetNumber();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading pet level stats...");
|
|
Global.ObjectMgr.LoadPetLevelInfo();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Player level dependent mail rewards...");
|
|
Global.ObjectMgr.LoadMailLevelRewards();
|
|
|
|
// Loot tables
|
|
Loots.LootManager.LoadLootTables();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Skill Discovery Table...");
|
|
SkillDiscovery.LoadSkillDiscoveryTable();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Skill Extra Item Table...");
|
|
SkillExtraItems.LoadSkillExtraItemTable();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Skill Perfection Data Table...");
|
|
SkillPerfectItems.LoadSkillPerfectItemTable();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Skill Fishing base level requirements...");
|
|
Global.ObjectMgr.LoadFishingBaseSkillLevel();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading skill tier info...");
|
|
Global.ObjectMgr.LoadSkillTiers();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Criteria Modifier trees...");
|
|
Global.CriteriaMgr.LoadCriteriaModifiersTree();
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Criteria Lists...");
|
|
Global.CriteriaMgr.LoadCriteriaList();
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Criteria Data...");
|
|
Global.CriteriaMgr.LoadCriteriaData();
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Achievements...");
|
|
Global.AchievementMgr.LoadAchievementReferenceList();
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Achievements Scripts...");
|
|
Global.AchievementMgr.LoadAchievementScripts();
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Achievement Rewards...");
|
|
Global.AchievementMgr.LoadRewards();
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Achievement Reward Locales...");
|
|
Global.AchievementMgr.LoadRewardLocales();
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Completed Achievements...");
|
|
Global.AchievementMgr.LoadCompletedAchievements();
|
|
|
|
// Load before guilds and arena teams
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading character cache store...");
|
|
Global.CharacterCacheStorage.LoadCharacterCacheStorage();
|
|
|
|
// Load dynamic data tables from the database
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Auctions...");
|
|
Global.AuctionHouseMgr.LoadAuctions();
|
|
|
|
if (WorldConfig.GetBoolValue(WorldCfg.BlackmarketEnabled))
|
|
{
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Black Market Templates...");
|
|
Global.BlackMarketMgr.LoadTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Black Market Auctions...");
|
|
Global.BlackMarketMgr.LoadAuctions();
|
|
}
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Guild rewards...");
|
|
Global.GuildMgr.LoadGuildRewards();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Guilds...");
|
|
Global.GuildMgr.LoadGuilds();
|
|
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading ArenaTeams...");
|
|
Global.ArenaTeamMgr.LoadArenaTeams();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Groups...");
|
|
Global.GroupMgr.LoadGroups();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading ReservedNames...");
|
|
Global.ObjectMgr.LoadReservedPlayersNames();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GameObjects for quests...");
|
|
Global.ObjectMgr.LoadGameObjectForQuests();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading BattleMasters...");
|
|
Global.BattlegroundMgr.LoadBattleMastersEntry();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GameTeleports...");
|
|
Global.ObjectMgr.LoadGameTele();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Trainers...");
|
|
Global.ObjectMgr.LoadTrainers(); // must be after load CreatureTemplate
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Gossip menu...");
|
|
Global.ObjectMgr.LoadGossipMenu();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Gossip menu options...");
|
|
Global.ObjectMgr.LoadGossipMenuItems();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature trainers...");
|
|
Global.ObjectMgr.LoadCreatureTrainers(); // must be after LoadGossipMenuItems
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Vendors...");
|
|
Global.ObjectMgr.LoadVendors(); // must be after load CreatureTemplate and ItemTemplate
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Waypoints...");
|
|
Global.WaypointMgr.Load();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading SmartAI Waypoints...");
|
|
Global.SmartAIMgr.LoadWaypointFromDB();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Formations...");
|
|
FormationMgr.LoadCreatureFormations();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading World States..."); // must be loaded before Battleground, outdoor PvP and conditions
|
|
LoadWorldStates();
|
|
|
|
Global.ObjectMgr.LoadPhases();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Conditions...");
|
|
Global.ConditionMgr.LoadConditions();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading faction change achievement pairs...");
|
|
Global.ObjectMgr.LoadFactionChangeAchievements();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading faction change spell pairs...");
|
|
Global.ObjectMgr.LoadFactionChangeSpells();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading faction change item pairs...");
|
|
Global.ObjectMgr.LoadFactionChangeItems();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading faction change quest pairs...");
|
|
Global.ObjectMgr.LoadFactionChangeQuests();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading faction change reputation pairs...");
|
|
Global.ObjectMgr.LoadFactionChangeReputations();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading faction change title pairs...");
|
|
Global.ObjectMgr.LoadFactionChangeTitles();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading mount definitions...");
|
|
CollectionMgr.LoadMountDefinitions();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GM bugs...");
|
|
Global.SupportMgr.LoadBugTickets();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GM complaints...");
|
|
Global.SupportMgr.LoadComplaintTickets();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading GM suggestions...");
|
|
Global.SupportMgr.LoadSuggestionTickets();
|
|
|
|
//Log.outInfo(LogFilter.ServerLoading, "Loading GM surveys...");
|
|
//Global.SupportMgr.LoadSurveys();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading garrison info...");
|
|
Global.GarrisonMgr.Initialize();
|
|
|
|
// Handle outdated emails (delete/return)
|
|
Log.outInfo(LogFilter.ServerLoading, "Returning old mails...");
|
|
Global.ObjectMgr.ReturnOrDeleteOldMails(false);
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Autobroadcasts...");
|
|
LoadAutobroadcasts();
|
|
|
|
// Load and initialize scripts
|
|
Global.ObjectMgr.LoadSpellScripts(); // must be after load Creature/Gameobject(Template/Data)
|
|
Global.ObjectMgr.LoadEventScripts(); // must be after load Creature/Gameobject(Template/Data)
|
|
Global.ObjectMgr.LoadWaypointScripts();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading spell script names...");
|
|
Global.ObjectMgr.LoadSpellScriptNames();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Texts...");
|
|
Global.CreatureTextMgr.LoadCreatureTexts();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Creature Text Locales...");
|
|
Global.CreatureTextMgr.LoadCreatureTextLocales();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Initializing Scripts...");
|
|
Global.ScriptMgr.Initialize();
|
|
Global.ScriptMgr.OnConfigLoad(false); // must be done after the ScriptMgr has been properly initialized
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Validating spell scripts...");
|
|
Global.ObjectMgr.ValidateSpellScripts();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading SmartAI scripts...");
|
|
Global.SmartAIMgr.LoadFromDB();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Calendar data...");
|
|
Global.CalendarMgr.LoadFromDB();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Petitions...");
|
|
Global.PetitionMgr.LoadPetitions();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Signatures...");
|
|
Global.PetitionMgr.LoadSignatures();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Item loot...");
|
|
Global.LootItemStorage.LoadStorageFromDB();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Initialize query data...");
|
|
Global.ObjectMgr.InitializeQueriesData(QueryDataGroup.All);
|
|
|
|
// Initialize game time and timers
|
|
Log.outInfo(LogFilter.ServerLoading, "Initialize game time and timers");
|
|
GameTime.UpdateGameTimers();
|
|
|
|
DB.Login.Execute("INSERT INTO uptime (realmid, starttime, uptime, revision) VALUES({0}, {1}, 0, '{2}')", _realm.Id.Index, GameTime.GetStartTime(), ""); // One-time query
|
|
|
|
m_timers[WorldTimers.Auctions].SetInterval(Time.Minute * Time.InMilliseconds);
|
|
m_timers[WorldTimers.AuctionsPending].SetInterval(250);
|
|
|
|
//Update "uptime" table based on configuration entry in minutes.
|
|
m_timers[WorldTimers.UpTime].SetInterval(10 * Time.Minute * Time.InMilliseconds);
|
|
//erase corpses every 20 minutes
|
|
m_timers[WorldTimers.Corpses].SetInterval(20 * Time.Minute * Time.InMilliseconds);
|
|
m_timers[WorldTimers.CleanDB].SetInterval(WorldConfig.GetIntValue(WorldCfg.LogdbClearinterval) * Time.Minute * Time.InMilliseconds);
|
|
m_timers[WorldTimers.AutoBroadcast].SetInterval(WorldConfig.GetIntValue(WorldCfg.AutoBroadcastInterval));
|
|
// check for chars to delete every day
|
|
m_timers[WorldTimers.DeleteChars].SetInterval(Time.Day * Time.InMilliseconds);
|
|
// for AhBot
|
|
m_timers[WorldTimers.AhBot].SetInterval(WorldConfig.GetIntValue(WorldCfg.AhbotUpdateInterval) * Time.InMilliseconds); // every 20 sec
|
|
m_timers[WorldTimers.GuildSave].SetInterval(WorldConfig.GetIntValue(WorldCfg.GuildSaveInterval) * Time.Minute * Time.InMilliseconds);
|
|
|
|
m_timers[WorldTimers.Blackmarket].SetInterval(10 * Time.InMilliseconds);
|
|
|
|
blackmarket_timer = 0;
|
|
|
|
m_timers[WorldTimers.WhoList].SetInterval(5 * Time.InMilliseconds); // update who list cache every 5 seconds
|
|
|
|
m_timers[WorldTimers.ChannelSave].SetInterval(WorldConfig.GetIntValue(WorldCfg.PreserveCustomChannelInterval) * Time.Minute * Time.InMilliseconds);
|
|
|
|
//to set mailtimer to return mails every day between 4 and 5 am
|
|
//mailtimer is increased when updating auctions
|
|
//one second is 1000 -(tested on win system)
|
|
// @todo Get rid of magic numbers
|
|
var localTime = Time.UnixTimeToDateTime(GameTime.GetGameTime()).ToLocalTime();
|
|
int CleanOldMailsTime = WorldConfig.GetIntValue(WorldCfg.CleanOldMailTime);
|
|
mail_timer = ((((localTime.Hour + (24 - CleanOldMailsTime)) % 24) * Time.Hour * Time.InMilliseconds) / m_timers[WorldTimers.Auctions].GetInterval());
|
|
//1440
|
|
mail_timer_expires = ((Time.Day * Time.InMilliseconds) / (m_timers[(int)WorldTimers.Auctions].GetInterval()));
|
|
Log.outInfo(LogFilter.ServerLoading, "Mail timer set to: {0}, mail return is called every {1} minutes", mail_timer, mail_timer_expires);
|
|
|
|
//- Initialize MapManager
|
|
Log.outInfo(LogFilter.ServerLoading, "Starting Map System");
|
|
Global.MapMgr.Initialize();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Starting Game Event system...");
|
|
uint nextGameEvent = Global.GameEventMgr.StartSystem();
|
|
m_timers[WorldTimers.Events].SetInterval(nextGameEvent); //depend on next event
|
|
|
|
// Delete all characters which have been deleted X days before
|
|
Player.DeleteOldCharacters();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Initializing chat channels...");
|
|
ChannelManager.LoadFromDB();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Initializing Opcodes...");
|
|
PacketManager.Initialize();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Starting Arena Season...");
|
|
Global.GameEventMgr.StartArenaSeason();
|
|
|
|
Global.SupportMgr.Initialize();
|
|
|
|
// Initialize Battlegrounds
|
|
Log.outInfo(LogFilter.ServerLoading, "Starting BattlegroundSystem");
|
|
Global.BattlegroundMgr.LoadBattlegroundTemplates();
|
|
|
|
// Initialize outdoor pvp
|
|
Log.outInfo(LogFilter.ServerLoading, "Starting Outdoor PvP System");
|
|
Global.OutdoorPvPMgr.InitOutdoorPvP();
|
|
|
|
// Initialize Battlefield
|
|
Log.outInfo(LogFilter.ServerLoading, "Starting Battlefield System");
|
|
Global.BattleFieldMgr.InitBattlefield();
|
|
|
|
// Initialize Warden
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Warden Checks...");
|
|
Global.WardenCheckMgr.LoadWardenChecks();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading Warden Action Overrides...");
|
|
Global.WardenCheckMgr.LoadWardenOverrides();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Deleting expired bans...");
|
|
DB.Login.Execute("DELETE FROM ip_banned WHERE unbandate <= UNIX_TIMESTAMP() AND unbandate<>bandate"); // One-time query
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Initializing quest reset times...");
|
|
InitQuestResetTimes();
|
|
CheckScheduledResetTimes();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Calculate random battleground reset time...");
|
|
InitRandomBGResetTime();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Calculate deletion of old calendar events time...");
|
|
InitCalendarOldEventsDeletionTime();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Calculate Guild cap reset time...");
|
|
InitGuildResetTime();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Calculate next currency reset time...");
|
|
InitCurrencyResetTime();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading race and class expansion requirements...");
|
|
Global.ObjectMgr.LoadRaceAndClassExpansionRequirements();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading character templates...");
|
|
Global.CharacterTemplateDataStorage.LoadCharacterTemplates();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading realm names...");
|
|
Global.ObjectMgr.LoadRealmNames();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading battle pets info...");
|
|
BattlePetMgr.Initialize();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading scenarios");
|
|
Global.ScenarioMgr.LoadDB2Data();
|
|
Global.ScenarioMgr.LoadDBData();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading scenario poi data");
|
|
Global.ScenarioMgr.LoadScenarioPOI();
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loading phase names...");
|
|
Global.ObjectMgr.LoadPhaseNames();
|
|
|
|
// Preload all cells, if required for the base maps
|
|
if (WorldConfig.GetBoolValue(WorldCfg.BasemapLoadGrids))
|
|
{
|
|
Global.MapMgr.DoForAllMaps(map =>
|
|
{
|
|
if (!map.Instanceable())
|
|
{
|
|
Log.outInfo(LogFilter.ServerLoading, "Pre-loading base map data for map {0}", map.GetId());
|
|
map.LoadAllCells();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
public void LoadConfigSettings(bool reload = false)
|
|
{
|
|
WorldConfig.Load(reload);
|
|
|
|
m_defaultDbcLocale = (Locale)ConfigMgr.GetDefaultValue("DBC.Locale", 0);
|
|
|
|
if (m_defaultDbcLocale >= Locale.Total || m_defaultDbcLocale == Locale.None)
|
|
{
|
|
Log.outError(LogFilter.ServerLoading, "Incorrect DBC.Locale! Must be >= 0 and < {0} and not {1} (set to 0)", Locale.Total, Locale.None);
|
|
m_defaultDbcLocale = Locale.enUS;
|
|
}
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Using {0} DBC Locale", m_defaultDbcLocale);
|
|
|
|
// load update time related configs
|
|
_worldUpdateTime.LoadFromConfig();
|
|
|
|
Global.WorldMgr.SetPlayerAmountLimit((uint)ConfigMgr.GetDefaultValue("PlayerLimit", 100));
|
|
Global.WorldMgr.SetMotd(ConfigMgr.GetDefaultValue("Motd", "Welcome to a Cypher Core Server."));
|
|
|
|
if (reload)
|
|
{
|
|
Global.SupportMgr.SetSupportSystemStatus(WorldConfig.GetBoolValue(WorldCfg.SupportEnabled));
|
|
Global.SupportMgr.SetTicketSystemStatus(WorldConfig.GetBoolValue(WorldCfg.SupportTicketsEnabled));
|
|
Global.SupportMgr.SetBugSystemStatus(WorldConfig.GetBoolValue(WorldCfg.SupportBugsEnabled));
|
|
Global.SupportMgr.SetComplaintSystemStatus(WorldConfig.GetBoolValue(WorldCfg.SupportComplaintsEnabled));
|
|
Global.SupportMgr.SetSuggestionSystemStatus(WorldConfig.GetBoolValue(WorldCfg.SupportSuggestionsEnabled));
|
|
|
|
Global.MapMgr.SetMapUpdateInterval(WorldConfig.GetIntValue(WorldCfg.IntervalMapupdate));
|
|
Global.MapMgr.SetGridCleanUpDelay(WorldConfig.GetUIntValue(WorldCfg.IntervalGridclean));
|
|
|
|
m_timers[WorldTimers.UpTime].SetInterval(WorldConfig.GetIntValue(WorldCfg.UptimeUpdate) * Time.Minute * Time.InMilliseconds);
|
|
m_timers[WorldTimers.UpTime].Reset();
|
|
|
|
m_timers[WorldTimers.CleanDB].SetInterval(WorldConfig.GetIntValue(WorldCfg.LogdbClearinterval) * Time.Minute * Time.InMilliseconds);
|
|
m_timers[WorldTimers.CleanDB].Reset();
|
|
|
|
|
|
m_timers[WorldTimers.AutoBroadcast].SetInterval(WorldConfig.GetIntValue(WorldCfg.AutoBroadcastInterval));
|
|
m_timers[WorldTimers.AutoBroadcast].Reset();
|
|
}
|
|
|
|
for (byte i = 0; i < (int)UnitMoveType.Max; ++i)
|
|
SharedConst.playerBaseMoveSpeed[i] = SharedConst.baseMoveSpeed[i] * WorldConfig.GetFloatValue(WorldCfg.RateMovespeed);
|
|
|
|
var rateCreatureAggro = WorldConfig.GetFloatValue(WorldCfg.RateCreatureAggro);
|
|
//visibility on continents
|
|
m_MaxVisibleDistanceOnContinents = ConfigMgr.GetDefaultValue("Visibility.Distance.Continents", SharedConst.DefaultVisibilityDistance);
|
|
if (m_MaxVisibleDistanceOnContinents < 45 * rateCreatureAggro)
|
|
{
|
|
Log.outError(LogFilter.ServerLoading, "Visibility.Distance.Continents can't be less max aggro radius {0}", 45 * rateCreatureAggro);
|
|
m_MaxVisibleDistanceOnContinents = 45 * rateCreatureAggro;
|
|
}
|
|
else if (m_MaxVisibleDistanceOnContinents > SharedConst.MaxVisibilityDistance)
|
|
{
|
|
Log.outError(LogFilter.ServerLoading, "Visibility.Distance.Continents can't be greater {0}", SharedConst.MaxVisibilityDistance);
|
|
m_MaxVisibleDistanceOnContinents = SharedConst.MaxVisibilityDistance;
|
|
}
|
|
|
|
//visibility in instances
|
|
m_MaxVisibleDistanceInInstances = ConfigMgr.GetDefaultValue("Visibility.Distance.Instances", SharedConst.DefaultVisibilityInstance);
|
|
if (m_MaxVisibleDistanceInInstances < 45 * rateCreatureAggro)
|
|
{
|
|
Log.outError(LogFilter.ServerLoading, "Visibility.Distance.Instances can't be less max aggro radius {0}", 45 * rateCreatureAggro);
|
|
m_MaxVisibleDistanceInInstances = 45 * rateCreatureAggro;
|
|
}
|
|
else if (m_MaxVisibleDistanceInInstances > SharedConst.MaxVisibilityDistance)
|
|
{
|
|
Log.outError(LogFilter.ServerLoading, "Visibility.Distance.Instances can't be greater {0}", SharedConst.MaxVisibilityDistance);
|
|
m_MaxVisibleDistanceInInstances = SharedConst.MaxVisibilityDistance;
|
|
}
|
|
|
|
//visibility in BG
|
|
m_MaxVisibleDistanceInBG = ConfigMgr.GetDefaultValue("Visibility.Distance.BG", SharedConst.DefaultVisibilityBGAreans);
|
|
if (m_MaxVisibleDistanceInBG < 45 * rateCreatureAggro)
|
|
{
|
|
Log.outError(LogFilter.ServerLoading, $"Visibility.Distance.BG can't be less max aggro radius {45 * rateCreatureAggro}");
|
|
m_MaxVisibleDistanceInBG = 45 * rateCreatureAggro;
|
|
}
|
|
else if (m_MaxVisibleDistanceInBG > SharedConst.MaxVisibilityDistance)
|
|
{
|
|
Log.outError(LogFilter.ServerLoading, $"Visibility.Distance.BG can't be greater {SharedConst.MaxVisibilityDistance}");
|
|
m_MaxVisibleDistanceInBG = SharedConst.MaxVisibilityDistance;
|
|
}
|
|
|
|
// Visibility in Arenas
|
|
m_MaxVisibleDistanceInArenas = ConfigMgr.GetDefaultValue("Visibility.Distance.Arenas", SharedConst.DefaultVisibilityBGAreans);
|
|
if (m_MaxVisibleDistanceInArenas < 45 * rateCreatureAggro)
|
|
{
|
|
Log.outError(LogFilter.ServerLoading, $"Visibility.Distance.Arenas can't be less max aggro radius {45 * rateCreatureAggro}");
|
|
m_MaxVisibleDistanceInArenas = 45 * rateCreatureAggro;
|
|
}
|
|
else if (m_MaxVisibleDistanceInArenas > SharedConst.MaxVisibilityDistance)
|
|
{
|
|
Log.outError(LogFilter.ServerLoading, $"Visibility.Distance.Arenas can't be greater {SharedConst.MaxVisibilityDistance}");
|
|
m_MaxVisibleDistanceInArenas = SharedConst.MaxVisibilityDistance;
|
|
}
|
|
|
|
m_visibility_notify_periodOnContinents = ConfigMgr.GetDefaultValue("Visibility.Notify.Period.OnContinents", SharedConst.DefaultVisibilityNotifyPeriod);
|
|
m_visibility_notify_periodInInstances = ConfigMgr.GetDefaultValue("Visibility.Notify.Period.InInstances", SharedConst.DefaultVisibilityNotifyPeriod);
|
|
m_visibility_notify_periodInBG = ConfigMgr.GetDefaultValue("Visibility.Notify.Period.InBG", SharedConst.DefaultVisibilityNotifyPeriod);
|
|
m_visibility_notify_periodInArenas = ConfigMgr.GetDefaultValue("Visibility.Notify.Period.InArenas", SharedConst.DefaultVisibilityNotifyPeriod);
|
|
|
|
_guidWarningMsg = WorldConfig.GetDefaultValue("Respawn.WarningMessage", "There will be an unscheduled server restart at 03:00. The server will be available again shortly after.");
|
|
_alertRestartReason = WorldConfig.GetDefaultValue("Respawn.AlertRestartReason", "Urgent Maintenance");
|
|
|
|
string dataPath = ConfigMgr.GetDefaultValue("DataDir", "./");
|
|
if (reload)
|
|
{
|
|
if (dataPath != _dataPath)
|
|
Log.outError(LogFilter.ServerLoading, "DataDir option can't be changed at worldserver.conf reload, using current value ({0}).", _dataPath);
|
|
}
|
|
else
|
|
{
|
|
_dataPath = dataPath;
|
|
Log.outInfo(LogFilter.ServerLoading, "Using DataDir {0}", _dataPath);
|
|
}
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, @"WORLD: MMap data directory is: {0}\mmaps", _dataPath);
|
|
|
|
bool EnableIndoor = ConfigMgr.GetDefaultValue("vmap.EnableIndoorCheck", true);
|
|
bool EnableLOS = ConfigMgr.GetDefaultValue("vmap.EnableLOS", true);
|
|
bool EnableHeight = ConfigMgr.GetDefaultValue("vmap.EnableHeight", true);
|
|
|
|
if (!EnableHeight)
|
|
Log.outError(LogFilter.ServerLoading, "VMap height checking Disabled! Creatures movements and other various things WILL be broken! Expect no support.");
|
|
|
|
Global.VMapMgr.SetEnableLineOfSightCalc(EnableLOS);
|
|
Global.VMapMgr.SetEnableHeightCalc(EnableHeight);
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "VMap support included. LineOfSight: {0}, getHeight: {1}, indoorCheck: {2}", EnableLOS, EnableHeight, EnableIndoor);
|
|
Log.outInfo(LogFilter.ServerLoading, @"VMap data directory is: {0}\vmaps", GetDataPath());
|
|
}
|
|
|
|
public void SetForcedWarModeFactionBalanceState(int team, int reward = 0)
|
|
{
|
|
_warModeDominantFaction = team;
|
|
_warModeOutnumberedFactionReward = reward;
|
|
}
|
|
|
|
public void DisableForcedWarModeFactionBalanceState()
|
|
{
|
|
UpdateWarModeRewardValues();
|
|
}
|
|
|
|
public void LoadAutobroadcasts()
|
|
{
|
|
uint oldMSTime = Time.GetMSTime();
|
|
|
|
m_Autobroadcasts.Clear();
|
|
|
|
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_AUTOBROADCAST);
|
|
stmt.AddValue(0, _realm.Id.Index);
|
|
|
|
SQLResult result = DB.Login.Query(stmt);
|
|
if (result.IsEmpty())
|
|
{
|
|
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 autobroadcasts definitions. DB table `autobroadcast` is empty for this realm!");
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
byte id = result.Read<byte>(0);
|
|
|
|
m_Autobroadcasts[id] = new Autobroadcast(result.Read<string>(2), result.Read<byte>(1));
|
|
|
|
} while (result.NextRow());
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} autobroadcast definitions in {1} ms", m_Autobroadcasts.Count, Time.GetMSTimeDiffToNow(oldMSTime));
|
|
}
|
|
|
|
public void Update(uint diff)
|
|
{
|
|
///- Update the game time and check for shutdown time
|
|
UpdateGameTime();
|
|
long currentGameTime = GameTime.GetGameTime();
|
|
|
|
_worldUpdateTime.UpdateWithDiff(diff);
|
|
|
|
// Record update if recording set in log and diff is greater then minimum set in log
|
|
_worldUpdateTime.RecordUpdateTime(GameTime.GetGameTimeMS(), diff, (uint)GetActiveSessionCount());
|
|
|
|
// Update the different timers
|
|
for (WorldTimers i = 0; i < WorldTimers.Max; ++i)
|
|
{
|
|
if (m_timers[i].GetCurrent() >= 0)
|
|
m_timers[i].Update(diff);
|
|
else
|
|
m_timers[i].SetCurrent(0);
|
|
}
|
|
|
|
// Update Who List Storage
|
|
if (m_timers[WorldTimers.WhoList].Passed())
|
|
{
|
|
m_timers[WorldTimers.WhoList].Reset();
|
|
Global.WhoListStorageMgr.Update();
|
|
}
|
|
|
|
if (IsStopped || m_timers[WorldTimers.ChannelSave].Passed())
|
|
{
|
|
m_timers[WorldTimers.ChannelSave].Reset();
|
|
|
|
if (WorldConfig.GetBoolValue(WorldCfg.PreserveCustomChannels))
|
|
{
|
|
ChannelManager mgr1 = ChannelManager.ForTeam(Team.Alliance);
|
|
mgr1.SaveToDB();
|
|
ChannelManager mgr2 = ChannelManager.ForTeam(Team.Horde);
|
|
if (mgr1 != mgr2)
|
|
mgr2.SaveToDB();
|
|
}
|
|
}
|
|
|
|
CheckScheduledResetTimes();
|
|
|
|
if (currentGameTime > m_NextRandomBGReset)
|
|
ResetRandomBG();
|
|
|
|
if (currentGameTime > m_NextCalendarOldEventsDeletionTime)
|
|
CalendarDeleteOldEvents();
|
|
|
|
if (currentGameTime > m_NextGuildReset)
|
|
ResetGuildCap();
|
|
|
|
if (currentGameTime > m_NextCurrencyReset)
|
|
ResetCurrencyWeekCap();
|
|
|
|
//Handle auctions when the timer has passed
|
|
if (m_timers[WorldTimers.Auctions].Passed())
|
|
{
|
|
m_timers[WorldTimers.Auctions].Reset();
|
|
|
|
// Update mails (return old mails with item, or delete them)
|
|
if (++mail_timer > mail_timer_expires)
|
|
{
|
|
mail_timer = 0;
|
|
Global.ObjectMgr.ReturnOrDeleteOldMails(true);
|
|
}
|
|
|
|
// Handle expired auctions
|
|
Global.AuctionHouseMgr.Update();
|
|
}
|
|
|
|
if (m_timers[WorldTimers.AuctionsPending].Passed())
|
|
{
|
|
m_timers[WorldTimers.AuctionsPending].Reset();
|
|
|
|
Global.AuctionHouseMgr.UpdatePendingAuctions();
|
|
}
|
|
|
|
if (m_timers[WorldTimers.Blackmarket].Passed())
|
|
{
|
|
m_timers[WorldTimers.Blackmarket].Reset();
|
|
|
|
//- Update blackmarket, refresh auctions if necessary
|
|
if ((blackmarket_timer * m_timers[WorldTimers.Blackmarket].GetInterval() >= WorldConfig.GetIntValue(WorldCfg.BlackmarketUpdatePeriod) * Time.Hour * Time.InMilliseconds) || blackmarket_timer == 0)
|
|
{
|
|
Global.BlackMarketMgr.RefreshAuctions();
|
|
blackmarket_timer = 1; // timer is 0 on startup
|
|
}
|
|
else
|
|
{
|
|
++blackmarket_timer;
|
|
Global.BlackMarketMgr.Update();
|
|
}
|
|
}
|
|
|
|
//Handle session updates when the timer has passed
|
|
_worldUpdateTime.RecordUpdateTimeReset();
|
|
UpdateSessions(diff);
|
|
_worldUpdateTime.RecordUpdateTimeDuration("UpdateSessions");
|
|
|
|
// <li> Update uptime table
|
|
if (m_timers[WorldTimers.UpTime].Passed())
|
|
{
|
|
uint tmpDiff = GameTime.GetUptime();
|
|
uint maxOnlinePlayers = GetMaxPlayerCount();
|
|
|
|
m_timers[WorldTimers.UpTime].Reset();
|
|
|
|
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_UPTIME_PLAYERS);
|
|
|
|
stmt.AddValue(0, tmpDiff);
|
|
stmt.AddValue(1, maxOnlinePlayers);
|
|
stmt.AddValue(2, _realm.Id.Index);
|
|
stmt.AddValue(3, (uint)GameTime.GetStartTime());
|
|
|
|
DB.Login.Execute(stmt);
|
|
}
|
|
|
|
// <li> Clean logs table
|
|
if (WorldConfig.GetIntValue(WorldCfg.LogdbCleartime) > 0) // if not enabled, ignore the timer
|
|
{
|
|
if (m_timers[WorldTimers.CleanDB].Passed())
|
|
{
|
|
m_timers[WorldTimers.CleanDB].Reset();
|
|
|
|
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.DEL_OLD_LOGS);
|
|
stmt.AddValue(0, WorldConfig.GetIntValue(WorldCfg.LogdbCleartime));
|
|
stmt.AddValue(1, 0);
|
|
stmt.AddValue(2, GetRealm().Id.Index);
|
|
|
|
DB.Login.Execute(stmt);
|
|
}
|
|
}
|
|
|
|
_worldUpdateTime.RecordUpdateTimeReset();
|
|
Global.MapMgr.Update(diff);
|
|
_worldUpdateTime.RecordUpdateTimeDuration("UpdateMapMgr");
|
|
|
|
if (WorldConfig.GetBoolValue(WorldCfg.AutoBroadcast))
|
|
{
|
|
if (m_timers[WorldTimers.AutoBroadcast].Passed())
|
|
{
|
|
m_timers[WorldTimers.AutoBroadcast].Reset();
|
|
SendAutoBroadcast();
|
|
}
|
|
}
|
|
|
|
Global.BattlegroundMgr.Update(diff);
|
|
_worldUpdateTime.RecordUpdateTimeDuration("UpdateBattlegroundMgr");
|
|
|
|
Global.OutdoorPvPMgr.Update(diff);
|
|
_worldUpdateTime.RecordUpdateTimeDuration("UpdateOutdoorPvPMgr");
|
|
|
|
Global.BattleFieldMgr.Update(diff);
|
|
_worldUpdateTime.RecordUpdateTimeDuration("BattlefieldMgr");
|
|
|
|
//- Delete all characters which have been deleted X days before
|
|
if (m_timers[WorldTimers.DeleteChars].Passed())
|
|
{
|
|
m_timers[WorldTimers.DeleteChars].Reset();
|
|
Player.DeleteOldCharacters();
|
|
}
|
|
|
|
Global.LFGMgr.Update(diff);
|
|
_worldUpdateTime.RecordUpdateTimeDuration("UpdateLFGMgr");
|
|
|
|
Global.GroupMgr.Update(diff);
|
|
_worldUpdateTime.RecordUpdateTimeDuration("GroupMgr");
|
|
|
|
// execute callbacks from sql queries that were queued recently
|
|
ProcessQueryCallbacks();
|
|
_worldUpdateTime.RecordUpdateTimeDuration("ProcessQueryCallbacks");
|
|
|
|
// Erase corpses once every 20 minutes
|
|
if (m_timers[WorldTimers.Corpses].Passed())
|
|
{
|
|
m_timers[WorldTimers.Corpses].Reset();
|
|
Global.MapMgr.DoForAllMaps(map => map.RemoveOldCorpses());
|
|
}
|
|
|
|
// Process Game events when necessary
|
|
if (m_timers[WorldTimers.Events].Passed())
|
|
{
|
|
m_timers[WorldTimers.Events].Reset(); // to give time for Update() to be processed
|
|
uint nextGameEvent = Global.GameEventMgr.Update();
|
|
m_timers[WorldTimers.Events].SetInterval(nextGameEvent);
|
|
m_timers[WorldTimers.Events].Reset();
|
|
}
|
|
|
|
if (m_timers[WorldTimers.GuildSave].Passed())
|
|
{
|
|
m_timers[WorldTimers.GuildSave].Reset();
|
|
Global.GuildMgr.SaveGuilds();
|
|
}
|
|
|
|
// update the instance reset times
|
|
Global.InstanceSaveMgr.Update();
|
|
|
|
// Check for shutdown warning
|
|
if (_guidWarn && !_guidAlert)
|
|
{
|
|
_warnDiff += diff;
|
|
if (GameTime.GetGameTime() >= _warnShutdownTime)
|
|
DoGuidWarningRestart();
|
|
else if (_warnDiff > WorldConfig.GetIntValue(WorldCfg.RespawnGuidWarningFrequency) * Time.InMilliseconds)
|
|
SendGuidWarning();
|
|
}
|
|
|
|
Global.ScriptMgr.OnWorldUpdate(diff);
|
|
}
|
|
|
|
public void ForceGameEventUpdate()
|
|
{
|
|
m_timers[WorldTimers.Events].Reset(); // to give time for Update() to be processed
|
|
uint nextGameEvent = Global.GameEventMgr.Update();
|
|
m_timers[WorldTimers.Events].SetInterval(nextGameEvent);
|
|
m_timers[WorldTimers.Events].Reset();
|
|
}
|
|
|
|
public void SendGlobalMessage(ServerPacket packet, WorldSession self = null, Team team = 0)
|
|
{
|
|
foreach (var session in m_sessions.Values)
|
|
{
|
|
if (session.GetPlayer() != null && session.GetPlayer().IsInWorld && session != self &&
|
|
(team == 0 || session.GetPlayer().GetTeam() == team))
|
|
{
|
|
session.SendPacket(packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SendGlobalGMMessage(ServerPacket packet, WorldSession self = null, Team team = 0)
|
|
{
|
|
foreach (var session in m_sessions.Values)
|
|
{
|
|
// check if session and can receive global GM Messages and its not self
|
|
if (session == null || session == self || !session.HasPermission(RBACPermissions.ReceiveGlobalGmTextmessage))
|
|
continue;
|
|
|
|
// Player should be in world
|
|
Player player = session.GetPlayer();
|
|
if (player == null || !player.IsInWorld)
|
|
continue;
|
|
|
|
// Send only to same team, if team is given
|
|
if (team == 0 || player.GetTeam() == team)
|
|
session.SendPacket(packet);
|
|
}
|
|
}
|
|
|
|
// Send a System Message to all players (except self if mentioned)
|
|
public void SendWorldText(CypherStrings string_id, params object[] args)
|
|
{
|
|
WorldWorldTextBuilder wt_builder = new((uint)string_id, args);
|
|
var wt_do = new LocalizedDo(wt_builder);
|
|
foreach (var session in m_sessions.Values)
|
|
{
|
|
if (session == null || !session.GetPlayer() || !session.GetPlayer().IsInWorld)
|
|
continue;
|
|
|
|
wt_do.Invoke(session.GetPlayer());
|
|
}
|
|
}
|
|
|
|
// Send a System Message to all GMs (except self if mentioned)
|
|
public void SendGMText(CypherStrings string_id, params object[] args)
|
|
{
|
|
var wt_builder = new WorldWorldTextBuilder((uint)string_id, args);
|
|
var wt_do = new LocalizedDo(wt_builder);
|
|
foreach (var session in m_sessions.Values)
|
|
{
|
|
// Session should have permissions to receive global gm messages
|
|
if (session == null || !session.HasPermission(RBACPermissions.ReceiveGlobalGmTextmessage))
|
|
continue;
|
|
|
|
// Player should be in world
|
|
Player player = session.GetPlayer();
|
|
if (!player || !player.IsInWorld)
|
|
continue;
|
|
|
|
wt_do.Invoke(player);
|
|
}
|
|
}
|
|
|
|
// Send a packet to all players (or players selected team) in the zone (except self if mentioned)
|
|
public bool SendZoneMessage(uint zone, ServerPacket packet, WorldSession self = null, uint team = 0)
|
|
{
|
|
bool foundPlayerToSend = false;
|
|
foreach (var session in m_sessions.Values)
|
|
{
|
|
if (session != null && session.GetPlayer() && session.GetPlayer().IsInWorld &&
|
|
session.GetPlayer().GetZoneId() == zone && session != self && (team == 0 || (uint)session.GetPlayer().GetTeam() == team))
|
|
{
|
|
session.SendPacket(packet);
|
|
foundPlayerToSend = true;
|
|
}
|
|
}
|
|
|
|
return foundPlayerToSend;
|
|
}
|
|
|
|
// Send a System Message to all players in the zone (except self if mentioned)
|
|
public void SendZoneText(uint zone, string text, WorldSession self = null, uint team = 0)
|
|
{
|
|
ChatPkt data = new();
|
|
data.Initialize(ChatMsg.System, Language.Universal, null, null, text);
|
|
SendZoneMessage(zone, data, self, team);
|
|
}
|
|
|
|
public void KickAll()
|
|
{
|
|
m_QueuedPlayer.Clear(); // prevent send queue update packet and login queued sessions
|
|
|
|
// session not removed at kick and will removed in next update tick
|
|
foreach (var session in m_sessions.Values)
|
|
session.KickPlayer("World::KickAll");
|
|
}
|
|
|
|
void KickAllLess(AccountTypes sec)
|
|
{
|
|
// session not removed at kick and will removed in next update tick
|
|
foreach (var session in m_sessions.Values)
|
|
if (session.GetSecurity() < sec)
|
|
session.KickPlayer("World::KickAllLess");
|
|
}
|
|
|
|
/// Ban an account or ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
|
|
public BanReturn BanAccount(BanMode mode, string nameOrIP, string duration, string reason, string author)
|
|
{
|
|
uint duration_secs = Time.TimeStringToSecs(duration);
|
|
return BanAccount(mode, nameOrIP, duration_secs, reason, author);
|
|
}
|
|
|
|
/// Ban an account or ban an IP address, duration is in seconds if positive, otherwise permban
|
|
public BanReturn BanAccount(BanMode mode, string nameOrIP, uint duration_secs, string reason, string author)
|
|
{
|
|
// Prevent banning an already banned account
|
|
if (mode == BanMode.Account && Global.AccountMgr.IsBannedAccount(nameOrIP))
|
|
return BanReturn.Exists;
|
|
|
|
SQLResult resultAccounts;
|
|
PreparedStatement stmt;
|
|
|
|
// Update the database with ban information
|
|
switch (mode)
|
|
{
|
|
case BanMode.IP:
|
|
// No SQL injection with prepared statements
|
|
stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_ACCOUNT_BY_IP);
|
|
stmt.AddValue(0, nameOrIP);
|
|
resultAccounts = DB.Login.Query(stmt);
|
|
stmt = DB.Login.GetPreparedStatement(LoginStatements.INS_IP_BANNED);
|
|
stmt.AddValue(0, nameOrIP);
|
|
stmt.AddValue(1, duration_secs);
|
|
stmt.AddValue(2, author);
|
|
stmt.AddValue(3, reason);
|
|
DB.Login.Execute(stmt);
|
|
break;
|
|
case BanMode.Account:
|
|
// No SQL injection with prepared statements
|
|
stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_ACCOUNT_ID_BY_NAME);
|
|
stmt.AddValue(0, nameOrIP);
|
|
resultAccounts = DB.Login.Query(stmt);
|
|
break;
|
|
case BanMode.Character:
|
|
// No SQL injection with prepared statements
|
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_ACCOUNT_BY_NAME);
|
|
stmt.AddValue(0, nameOrIP);
|
|
resultAccounts = DB.Characters.Query(stmt);
|
|
break;
|
|
default:
|
|
return BanReturn.SyntaxError;
|
|
}
|
|
|
|
if (resultAccounts == null)
|
|
{
|
|
if (mode == BanMode.IP)
|
|
return BanReturn.Success; // ip correctly banned but nobody affected (yet)
|
|
else
|
|
return BanReturn.Notfound; // Nobody to ban
|
|
}
|
|
|
|
// Disconnect all affected players (for IP it can be several)
|
|
SQLTransaction trans = new();
|
|
do
|
|
{
|
|
uint account = resultAccounts.Read<uint>(0);
|
|
|
|
if (mode != BanMode.IP)
|
|
{
|
|
// make sure there is only one active ban
|
|
stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_ACCOUNT_NOT_BANNED);
|
|
stmt.AddValue(0, account);
|
|
trans.Append(stmt);
|
|
// No SQL injection with prepared statements
|
|
stmt = DB.Login.GetPreparedStatement(LoginStatements.INS_ACCOUNT_BANNED);
|
|
stmt.AddValue(0, account);
|
|
stmt.AddValue(1, duration_secs);
|
|
stmt.AddValue(2, author);
|
|
stmt.AddValue(3, reason);
|
|
trans.Append(stmt);
|
|
}
|
|
|
|
WorldSession sess = FindSession(account);
|
|
if (sess)
|
|
{
|
|
if (sess.GetPlayerName() != author)
|
|
sess.KickPlayer("World::BanAccount Banning account");
|
|
}
|
|
} while (resultAccounts.NextRow());
|
|
|
|
DB.Login.CommitTransaction(trans);
|
|
|
|
return BanReturn.Success;
|
|
}
|
|
|
|
/// Remove a ban from an account or IP address
|
|
public bool RemoveBanAccount(BanMode mode, string nameOrIP)
|
|
{
|
|
PreparedStatement stmt;
|
|
if (mode == BanMode.IP)
|
|
{
|
|
stmt = DB.Login.GetPreparedStatement(LoginStatements.DEL_IP_NOT_BANNED);
|
|
stmt.AddValue(0, nameOrIP);
|
|
DB.Login.Execute(stmt);
|
|
}
|
|
else
|
|
{
|
|
uint account = 0;
|
|
if (mode == BanMode.Account)
|
|
account = Global.AccountMgr.GetId(nameOrIP);
|
|
else if (mode == BanMode.Character)
|
|
account = Global.CharacterCacheStorage.GetCharacterAccountIdByName(nameOrIP);
|
|
|
|
if (account == 0)
|
|
return false;
|
|
|
|
//NO SQL injection as account is uint32
|
|
stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_ACCOUNT_NOT_BANNED);
|
|
stmt.AddValue(0, account);
|
|
DB.Login.Execute(stmt);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Ban an account or ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
|
|
public BanReturn BanCharacter(string name, string duration, string reason, string author)
|
|
{
|
|
Player pBanned = Global.ObjAccessor.FindConnectedPlayerByName(name);
|
|
ObjectGuid guid;
|
|
|
|
uint duration_secs = Time.TimeStringToSecs(duration);
|
|
|
|
// Pick a player to ban if not online
|
|
if (!pBanned)
|
|
{
|
|
guid = Global.CharacterCacheStorage.GetCharacterGuidByName(name);
|
|
if (guid.IsEmpty())
|
|
return BanReturn.Notfound; // Nobody to ban
|
|
}
|
|
else
|
|
guid = pBanned.GetGUID();
|
|
|
|
//Use transaction in order to ensure the order of the queries
|
|
SQLTransaction trans = new();
|
|
|
|
// make sure there is only one active ban
|
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHARACTER_BAN);
|
|
stmt.AddValue(0, guid.GetCounter());
|
|
trans.Append(stmt);
|
|
|
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHARACTER_BAN);
|
|
stmt.AddValue(0, guid.GetCounter());
|
|
stmt.AddValue(1, (long)duration_secs);
|
|
stmt.AddValue(2, author);
|
|
stmt.AddValue(3, reason);
|
|
trans.Append(stmt);
|
|
DB.Characters.CommitTransaction(trans);
|
|
|
|
if (pBanned)
|
|
pBanned.GetSession().KickPlayer("World::BanCharacter Banning character");
|
|
|
|
return BanReturn.Success;
|
|
}
|
|
|
|
// Remove a ban from a character
|
|
public bool RemoveBanCharacter(string name)
|
|
{
|
|
Player pBanned = Global.ObjAccessor.FindConnectedPlayerByName(name);
|
|
ObjectGuid guid;
|
|
|
|
// Pick a player to ban if not online
|
|
if (!pBanned)
|
|
{
|
|
guid = Global.CharacterCacheStorage.GetCharacterGuidByName(name);
|
|
if (guid.IsEmpty())
|
|
return false; // Nobody to ban
|
|
}
|
|
else
|
|
guid = pBanned.GetGUID();
|
|
|
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHARACTER_BAN);
|
|
stmt.AddValue(0, guid.GetCounter());
|
|
DB.Characters.Execute(stmt);
|
|
return true;
|
|
}
|
|
|
|
void UpdateGameTime()
|
|
{
|
|
// update the time
|
|
long lastGameTime = GameTime.GetGameTime();
|
|
GameTime.UpdateGameTimers();
|
|
|
|
uint elapsed = (uint)(GameTime.GetGameTime() - lastGameTime);
|
|
|
|
//- if there is a shutdown timer
|
|
if (!IsStopped && m_ShutdownTimer > 0 && elapsed > 0)
|
|
{
|
|
//- ... and it is overdue, stop the world
|
|
if (m_ShutdownTimer <= elapsed)
|
|
{
|
|
if (!m_ShutdownMask.HasAnyFlag(ShutdownMask.Idle) || GetActiveAndQueuedSessionCount() == 0)
|
|
IsStopped = true; // exist code already set
|
|
else
|
|
m_ShutdownTimer = 1; // minimum timer value to wait idle state
|
|
}
|
|
//- ... else decrease it and if necessary display a shutdown countdown to the users
|
|
else
|
|
{
|
|
m_ShutdownTimer -= elapsed;
|
|
|
|
ShutdownMsg();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ShutdownServ(uint time, ShutdownMask options, ShutdownExitCode exitcode, string reason = "")
|
|
{
|
|
// ignore if server shutdown at next tick
|
|
if (IsStopped)
|
|
return;
|
|
|
|
m_ShutdownMask = options;
|
|
m_ExitCode = exitcode;
|
|
|
|
// If the shutdown time is 0, evaluate shutdown on next tick (no message)
|
|
if (time == 0)
|
|
m_ShutdownTimer = 1;
|
|
// Else set the shutdown timer and warn users
|
|
else
|
|
{
|
|
m_ShutdownTimer = time;
|
|
ShutdownMsg(true, null, reason);
|
|
}
|
|
|
|
Global.ScriptMgr.OnShutdownInitiate(exitcode, options);
|
|
}
|
|
|
|
public void ShutdownMsg(bool show = false, Player player = null, string reason = "")
|
|
{
|
|
// not show messages for idle shutdown mode
|
|
if (m_ShutdownMask.HasAnyFlag(ShutdownMask.Idle))
|
|
return;
|
|
|
|
// Display a message every 12 hours, hours, 5 minutes, minute, 5 seconds and finally seconds
|
|
if (show ||
|
|
(m_ShutdownTimer < 5 * Time.Minute && (m_ShutdownTimer % 15) == 0) || // < 5 min; every 15 sec
|
|
(m_ShutdownTimer < 15 * Time.Minute && (m_ShutdownTimer % Time.Minute) == 0) || // < 15 min ; every 1 min
|
|
(m_ShutdownTimer < 30 * Time.Minute && (m_ShutdownTimer % (5 * Time.Minute)) == 0) || // < 30 min ; every 5 min
|
|
(m_ShutdownTimer < 12 * Time.Hour && (m_ShutdownTimer % Time.Hour) == 0) || // < 12 h ; every 1 h
|
|
(m_ShutdownTimer > 12 * Time.Hour && (m_ShutdownTimer % (12 * Time.Hour)) == 0)) // > 12 h ; every 12 h
|
|
{
|
|
var str = Time.secsToTimeString(m_ShutdownTimer, TimeFormat.Numeric);
|
|
if (!reason.IsEmpty())
|
|
str += " - " + reason;
|
|
|
|
ServerMessageType msgid = m_ShutdownMask.HasAnyFlag(ShutdownMask.Restart) ? ServerMessageType.RestartTime : ServerMessageType.ShutdownTime;
|
|
|
|
SendServerMessage(msgid, str, player);
|
|
Log.outDebug(LogFilter.Server, "Server is {0} in {1}", (m_ShutdownMask.HasAnyFlag(ShutdownMask.Restart) ? "restart" : "shuttingdown"), str);
|
|
}
|
|
}
|
|
|
|
public uint ShutdownCancel()
|
|
{
|
|
// nothing cancel or too late
|
|
if (m_ShutdownTimer == 0 || IsStopped)
|
|
return 0;
|
|
|
|
ServerMessageType msgid = m_ShutdownMask.HasAnyFlag(ShutdownMask.Restart) ? ServerMessageType.RestartCancelled : ServerMessageType.ShutdownCancelled;
|
|
|
|
uint oldTimer = m_ShutdownTimer;
|
|
m_ShutdownMask = 0;
|
|
m_ShutdownTimer = 0;
|
|
m_ExitCode = (byte)ShutdownExitCode.Shutdown; // to default value
|
|
SendServerMessage(msgid);
|
|
|
|
Log.outDebug(LogFilter.Server, "Server {0} cancelled.", (m_ShutdownMask.HasAnyFlag(ShutdownMask.Restart) ? "restart" : "shutdown"));
|
|
|
|
Global.ScriptMgr.OnShutdownCancel();
|
|
return oldTimer;
|
|
}
|
|
|
|
public void SendServerMessage(ServerMessageType messageID, string stringParam = "", Player player = null)
|
|
{
|
|
ChatServerMessage packet = new();
|
|
packet.MessageID = (int)messageID;
|
|
if (messageID <= ServerMessageType.String)
|
|
packet.StringParam = stringParam;
|
|
|
|
if (player)
|
|
player.SendPacket(packet);
|
|
else
|
|
SendGlobalMessage(packet);
|
|
}
|
|
|
|
public void UpdateSessions(uint diff)
|
|
{
|
|
Tuple<WorldSocket, ulong> linkInfo;
|
|
while (_linkSocketQueue.TryDequeue(out linkInfo))
|
|
ProcessLinkInstanceSocket(linkInfo);
|
|
|
|
// Add new sessions
|
|
WorldSession sess;
|
|
while (addSessQueue.TryDequeue(out sess))
|
|
AddSession_(sess);
|
|
|
|
// Then send an update signal to remaining ones
|
|
foreach (var pair in m_sessions)
|
|
{
|
|
WorldSession session = pair.Value;
|
|
WorldSessionFilter updater = new(session);
|
|
if (!session.Update(diff, updater)) // As interval = 0
|
|
{
|
|
if (!RemoveQueuedPlayer(session) && session != null && WorldConfig.GetIntValue(WorldCfg.IntervalDisconnectTolerance) != 0)
|
|
m_disconnects[session.GetAccountId()] = GameTime.GetGameTime();
|
|
|
|
RemoveQueuedPlayer(session);
|
|
m_sessions.TryRemove(pair.Key, out _);
|
|
m_sessionsByBnetGuid.Remove(session.GetBattlenetAccountGUID(), session);
|
|
session.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SendAutoBroadcast()
|
|
{
|
|
if (m_Autobroadcasts.Empty())
|
|
return;
|
|
|
|
var pair = m_Autobroadcasts.SelectRandomElementByWeight(autoPair => autoPair.Value.Weight);
|
|
|
|
uint abcenter = WorldConfig.GetUIntValue(WorldCfg.AutoBroadcastCenter);
|
|
|
|
if (abcenter == 0)
|
|
SendWorldText(CypherStrings.AutoBroadcast, pair.Value.Message);
|
|
else if (abcenter == 1)
|
|
SendGlobalMessage(new PrintNotification(pair.Value.Message));
|
|
else if (abcenter == 2)
|
|
{
|
|
SendWorldText(CypherStrings.AutoBroadcast, pair.Value.Message);
|
|
SendGlobalMessage(new PrintNotification(pair.Value.Message));
|
|
}
|
|
|
|
Log.outDebug(LogFilter.Misc, "AutoBroadcast: '{0}'", pair.Value.Message);
|
|
}
|
|
|
|
public void UpdateRealmCharCount(uint accountId)
|
|
{
|
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_COUNT);
|
|
stmt.AddValue(0, accountId);
|
|
_queryProcessor.AddCallback(DB.Characters.AsyncQuery(stmt).WithCallback(UpdateRealmCharCount));
|
|
}
|
|
|
|
void UpdateRealmCharCount(SQLResult result)
|
|
{
|
|
if (!result.IsEmpty())
|
|
{
|
|
uint Id = result.Read<uint>(0);
|
|
uint charCount = result.Read<uint>(1);
|
|
|
|
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.REP_REALM_CHARACTERS);
|
|
stmt.AddValue(0, charCount);
|
|
stmt.AddValue(1, Id);
|
|
stmt.AddValue(2, _realm.Id.Index);
|
|
DB.Login.DirectExecute(stmt);
|
|
}
|
|
}
|
|
|
|
void InitQuestResetTimes()
|
|
{
|
|
m_NextDailyQuestReset = GetWorldState(WorldStates.DailyQuestResetTime);
|
|
m_NextWeeklyQuestReset = GetWorldState(WorldStates.WeeklyQuestResetTime);
|
|
m_NextMonthlyQuestReset = GetWorldState(WorldStates.MonthlyQuestResetTime);
|
|
}
|
|
|
|
static long GetNextDailyResetTime(long t)
|
|
{
|
|
return Time.GetLocalHourTimestamp(t, WorldConfig.GetUIntValue(WorldCfg.DailyQuestResetTimeHour), true);
|
|
}
|
|
|
|
void DailyReset()
|
|
{
|
|
// reset all saved quest status
|
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_RESET_CHARACTER_QUESTSTATUS_DAILY);
|
|
DB.Characters.Execute(stmt);
|
|
|
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHARACTER_GARRISON_FOLLOWER_ACTIVATIONS);
|
|
stmt.AddValue(0, 1);
|
|
DB.Characters.Execute(stmt);
|
|
|
|
// reset all quest status in memory
|
|
foreach (var itr in m_sessions)
|
|
{
|
|
Player player = itr.Value.GetPlayer();
|
|
if (player != null)
|
|
player.DailyReset();
|
|
}
|
|
|
|
// reselect pools
|
|
Global.QuestPoolMgr.ChangeDailyQuests();
|
|
|
|
// Update faction balance
|
|
UpdateWarModeRewardValues();
|
|
|
|
// store next reset time
|
|
long now = GameTime.GetGameTime();
|
|
long next = GetNextDailyResetTime(now);
|
|
Cypher.Assert(now < next);
|
|
|
|
m_NextDailyQuestReset = next;
|
|
SetWorldState(WorldStates.DailyQuestResetTime, (ulong)next);
|
|
|
|
Log.outInfo(LogFilter.Misc, "Daily quests for all characters have been reset.");
|
|
}
|
|
|
|
static long GetNextWeeklyResetTime(long t)
|
|
{
|
|
t = GetNextDailyResetTime(t);
|
|
DateTime time = Time.UnixTimeToDateTime(t);
|
|
int wday = (int)time.DayOfWeek;
|
|
int target = WorldConfig.GetIntValue(WorldCfg.WeeklyQuestResetTimeWDay);
|
|
if (target < wday)
|
|
wday -= 7;
|
|
t += (Time.Day * (target - wday));
|
|
return t;
|
|
}
|
|
|
|
void ResetWeeklyQuests()
|
|
{
|
|
// reset all saved quest status
|
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_RESET_CHARACTER_QUESTSTATUS_WEEKLY);
|
|
DB.Characters.Execute(stmt);
|
|
// reset all quest status in memory
|
|
foreach (var itr in m_sessions)
|
|
{
|
|
Player player = itr.Value.GetPlayer();
|
|
if (player != null)
|
|
player.ResetWeeklyQuestStatus();
|
|
}
|
|
|
|
// reselect pools
|
|
Global.QuestPoolMgr.ChangeWeeklyQuests();
|
|
|
|
// store next reset time
|
|
long now = GameTime.GetGameTime();
|
|
long next = GetNextWeeklyResetTime(now);
|
|
Cypher.Assert(now < next);
|
|
|
|
m_NextWeeklyQuestReset = next;
|
|
SetWorldState(WorldStates.WeeklyQuestResetTime, (ulong)next);
|
|
|
|
Log.outInfo(LogFilter.Misc, "Weekly quests for all characters have been reset.");
|
|
}
|
|
|
|
static long GetNextMonthlyResetTime(long t)
|
|
{
|
|
t = GetNextDailyResetTime(t);
|
|
DateTime time = Time.UnixTimeToDateTime(t);
|
|
if (time.Day == 1)
|
|
return t;
|
|
|
|
var newDate = new DateTime(time.Year, time.Month + 1, 1, 0, 0, 0, time.Kind);
|
|
return Time.DateTimeToUnixTime(newDate);
|
|
}
|
|
|
|
void ResetMonthlyQuests()
|
|
{
|
|
// reset all saved quest status
|
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_RESET_CHARACTER_QUESTSTATUS_MONTHLY);
|
|
DB.Characters.Execute(stmt);
|
|
// reset all quest status in memory
|
|
foreach (var itr in m_sessions)
|
|
{
|
|
Player player = itr.Value.GetPlayer();
|
|
if (player != null)
|
|
player.ResetMonthlyQuestStatus();
|
|
}
|
|
|
|
// reselect pools
|
|
Global.QuestPoolMgr.ChangeMonthlyQuests();
|
|
|
|
// store next reset time
|
|
long now = GameTime.GetGameTime();
|
|
long next = GetNextMonthlyResetTime(now);
|
|
Cypher.Assert(now < next);
|
|
|
|
m_NextMonthlyQuestReset = next;
|
|
SetWorldState(WorldStates.MonthlyQuestResetTime, (ulong)next);
|
|
|
|
Log.outInfo(LogFilter.Misc, "Monthly quests for all characters have been reset.");
|
|
}
|
|
|
|
void CheckScheduledResetTimes()
|
|
{
|
|
long now = GameTime.GetGameTime();
|
|
if (m_NextDailyQuestReset <= now)
|
|
DailyReset();
|
|
if (m_NextWeeklyQuestReset <= now)
|
|
ResetWeeklyQuests();
|
|
if (m_NextMonthlyQuestReset <= now)
|
|
ResetMonthlyQuests();
|
|
}
|
|
|
|
void InitRandomBGResetTime()
|
|
{
|
|
long bgtime = GetWorldState(WorldStates.BgDailyResetTime);
|
|
if (bgtime == 0)
|
|
m_NextRandomBGReset = GameTime.GetGameTime(); // game time not yet init
|
|
|
|
// generate time by config
|
|
long curTime = GameTime.GetGameTime();
|
|
|
|
// current day reset time
|
|
long nextDayResetTime = Time.GetNextResetUnixTime(WorldConfig.GetIntValue(WorldCfg.RandomBgResetHour));
|
|
|
|
// next reset time before current moment
|
|
if (curTime >= nextDayResetTime)
|
|
nextDayResetTime += Time.Day;
|
|
|
|
// normalize reset time
|
|
m_NextRandomBGReset = bgtime < curTime ? nextDayResetTime - Time.Day : nextDayResetTime;
|
|
|
|
if (bgtime == 0)
|
|
SetWorldState(WorldStates.BgDailyResetTime, (ulong)m_NextRandomBGReset);
|
|
}
|
|
|
|
void InitCalendarOldEventsDeletionTime()
|
|
{
|
|
long now = GameTime.GetGameTime();
|
|
long nextDeletionTime = Time.GetLocalHourTimestamp(now, WorldConfig.GetUIntValue(WorldCfg.CalendarDeleteOldEventsHour));
|
|
long currentDeletionTime = GetWorldState(WorldStates.DailyCalendarDeletionOldEventsTime);
|
|
|
|
// If the reset time saved in the worldstate is before now it means the server was offline when the reset was supposed to occur.
|
|
// In this case we set the reset time in the past and next world update will do the reset and schedule next one in the future.
|
|
if (currentDeletionTime < now)
|
|
m_NextCalendarOldEventsDeletionTime = nextDeletionTime - Time.Day;
|
|
else
|
|
m_NextCalendarOldEventsDeletionTime = nextDeletionTime;
|
|
|
|
if (currentDeletionTime == 0)
|
|
SetWorldState(WorldStates.DailyCalendarDeletionOldEventsTime, (ulong)m_NextCalendarOldEventsDeletionTime);
|
|
}
|
|
|
|
void InitGuildResetTime()
|
|
{
|
|
long gtime = GetWorldState(WorldStates.GuildDailyResetTime);
|
|
if (gtime == 0)
|
|
m_NextGuildReset = GameTime.GetGameTime(); // game time not yet init
|
|
|
|
long curTime = GameTime.GetGameTime();
|
|
var nextDayResetTime = Time.GetNextResetUnixTime(WorldConfig.GetIntValue(WorldCfg.GuildResetHour));
|
|
|
|
if (curTime >= nextDayResetTime)
|
|
nextDayResetTime += Time.Day;
|
|
|
|
// normalize reset time
|
|
m_NextGuildReset = gtime < curTime ? nextDayResetTime - Time.Day : nextDayResetTime;
|
|
|
|
if (gtime == 0)
|
|
SetWorldState(WorldStates.GuildDailyResetTime, (ulong)m_NextGuildReset);
|
|
}
|
|
|
|
void InitCurrencyResetTime()
|
|
{
|
|
long currencytime = GetWorldState(WorldStates.CurrencyResetTime);
|
|
if (currencytime == 0)
|
|
m_NextCurrencyReset = GameTime.GetGameTime(); // game time not yet init
|
|
|
|
// generate time by config
|
|
long curTime = GameTime.GetGameTime();
|
|
|
|
var nextWeekResetTime = Time.GetNextResetUnixTime(WorldConfig.GetIntValue(WorldCfg.CurrencyResetDay), WorldConfig.GetIntValue(WorldCfg.CurrencyResetHour));
|
|
|
|
// next reset time before current moment
|
|
if (curTime >= nextWeekResetTime)
|
|
nextWeekResetTime += WorldConfig.GetIntValue(WorldCfg.CurrencyResetInterval) * Time.Day;
|
|
|
|
// normalize reset time
|
|
m_NextCurrencyReset = currencytime < curTime ? nextWeekResetTime - WorldConfig.GetIntValue(WorldCfg.CurrencyResetInterval) * Time.Day : nextWeekResetTime;
|
|
|
|
if (currencytime == 0)
|
|
SetWorldState(WorldStates.CurrencyResetTime, (ulong)m_NextCurrencyReset);
|
|
}
|
|
|
|
void ResetCurrencyWeekCap()
|
|
{
|
|
DB.Characters.Execute("UPDATE `character_currency` SET `WeeklyQuantity` = 0");
|
|
|
|
foreach (var session in m_sessions.Values)
|
|
if (session.GetPlayer() != null)
|
|
session.GetPlayer().ResetCurrencyWeekCap();
|
|
|
|
m_NextCurrencyReset += Time.Day * WorldConfig.GetIntValue(WorldCfg.CurrencyResetInterval);
|
|
SetWorldState(WorldStates.CurrencyResetTime, (ulong)m_NextCurrencyReset);
|
|
}
|
|
|
|
public void ResetEventSeasonalQuests(ushort event_id)
|
|
{
|
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_RESET_CHARACTER_QUESTSTATUS_SEASONAL_BY_EVENT);
|
|
stmt.AddValue(0, event_id);
|
|
DB.Characters.Execute(stmt);
|
|
|
|
foreach (var session in m_sessions.Values)
|
|
if (session.GetPlayer())
|
|
session.GetPlayer().ResetSeasonalQuestStatus(event_id);
|
|
}
|
|
|
|
void ResetRandomBG()
|
|
{
|
|
Log.outInfo(LogFilter.Server, "Random BG status reset for all characters.");
|
|
|
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_BATTLEGROUND_RANDOM_ALL);
|
|
DB.Characters.Execute(stmt);
|
|
|
|
foreach (var session in m_sessions.Values)
|
|
if (session.GetPlayer())
|
|
session.GetPlayer().SetRandomWinner(false);
|
|
|
|
m_NextRandomBGReset += Time.Day;
|
|
SetWorldState(WorldStates.BgDailyResetTime, (ulong)m_NextRandomBGReset);
|
|
}
|
|
|
|
void CalendarDeleteOldEvents()
|
|
{
|
|
Log.outInfo(LogFilter.Misc, "Calendar deletion of old events.");
|
|
|
|
m_NextCalendarOldEventsDeletionTime = m_NextCalendarOldEventsDeletionTime + Time.Day;
|
|
SetWorldState(WorldStates.DailyCalendarDeletionOldEventsTime, (ulong)m_NextCalendarOldEventsDeletionTime);
|
|
Global.CalendarMgr.DeleteOldEvents();
|
|
}
|
|
|
|
void ResetGuildCap()
|
|
{
|
|
m_NextGuildReset += Time.Day;
|
|
SetWorldState(WorldStates.GuildDailyResetTime, (ulong)m_NextGuildReset);
|
|
ulong week = GetWorldState(WorldStates.GuildWeeklyResetTime);
|
|
week = week < 7 ? week + 1 : 1;
|
|
|
|
Log.outInfo(LogFilter.Server, "Guild Daily Cap reset. Week: {0}", week == 1);
|
|
SetWorldState(WorldStates.GuildWeeklyResetTime, week);
|
|
Global.GuildMgr.ResetTimes(week == 1);
|
|
}
|
|
|
|
void UpdateMaxSessionCounters()
|
|
{
|
|
m_maxActiveSessionCount = Math.Max(m_maxActiveSessionCount, (uint)(m_sessions.Count - m_QueuedPlayer.Count));
|
|
m_maxQueuedSessionCount = Math.Max(m_maxQueuedSessionCount, (uint)m_QueuedPlayer.Count);
|
|
}
|
|
|
|
public string LoadDBVersion()
|
|
{
|
|
var DBVersion = "Unknown world database.";
|
|
|
|
SQLResult result = DB.World.Query("SELECT db_version, cache_id FROM version LIMIT 1");
|
|
if (!result.IsEmpty())
|
|
{
|
|
DBVersion = result.Read<string>(0);
|
|
// will be overwrite by config values if different and non-0
|
|
WorldConfig.SetValue(WorldCfg.ClientCacheVersion, result.Read<uint>(1));
|
|
}
|
|
|
|
return DBVersion;
|
|
}
|
|
|
|
void UpdateAreaDependentAuras()
|
|
{
|
|
foreach (var session in m_sessions.Values)
|
|
{
|
|
if (session.GetPlayer() != null && session.GetPlayer().IsInWorld)
|
|
{
|
|
session.GetPlayer().UpdateAreaDependentAuras(session.GetPlayer().GetAreaId());
|
|
session.GetPlayer().UpdateZoneDependentAuras(session.GetPlayer().GetZoneId());
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsBattlePetJournalLockAcquired(ObjectGuid battlenetAccountGuid)
|
|
{
|
|
foreach (var sessionForBnet in m_sessionsByBnetGuid.LookupByKey(battlenetAccountGuid))
|
|
if (sessionForBnet.GetBattlePetMgr().HasJournalLock())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void LoadWorldStates()
|
|
{
|
|
uint oldMSTime = Time.GetMSTime();
|
|
|
|
SQLResult result = DB.Characters.Query("SELECT entry, value FROM worldstates");
|
|
|
|
if (result.IsEmpty())
|
|
{
|
|
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 world states. DB table `worldstates` is empty!");
|
|
return;
|
|
}
|
|
|
|
uint count = 0;
|
|
do
|
|
{
|
|
m_worldstates[result.Read<uint>(0)] = result.Read<uint>(1);
|
|
++count;
|
|
}
|
|
while (result.NextRow());
|
|
|
|
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} world states in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
|
|
}
|
|
|
|
public void SetWorldState(WorldStates index, object value)
|
|
{
|
|
SetWorldState((uint)index, value);
|
|
}
|
|
|
|
public void SetWorldState(uint index, object value)
|
|
{
|
|
PreparedStatement stmt;
|
|
|
|
if (m_worldstates.ContainsKey(index))
|
|
{
|
|
if (m_worldstates[index] == Convert.ToUInt32(value))
|
|
return;
|
|
|
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_WORLDSTATE);
|
|
stmt.AddValue(0, Convert.ToUInt32(value));
|
|
stmt.AddValue(1, index);
|
|
}
|
|
else
|
|
{
|
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_WORLDSTATE);
|
|
|
|
stmt.AddValue(0, index);
|
|
stmt.AddValue(1, Convert.ToUInt32(value));
|
|
}
|
|
DB.Characters.Execute(stmt);
|
|
m_worldstates[index] = Convert.ToUInt32(value);
|
|
}
|
|
|
|
public uint GetWorldState(WorldStates index)
|
|
{
|
|
return GetWorldState((uint)index);
|
|
}
|
|
|
|
public uint GetWorldState(uint index)
|
|
{
|
|
return m_worldstates.LookupByKey(index);
|
|
}
|
|
|
|
void ProcessQueryCallbacks()
|
|
{
|
|
_queryProcessor.ProcessReadyCallbacks();
|
|
}
|
|
|
|
public void ReloadRBAC()
|
|
{
|
|
// Passive reload, we mark the data as invalidated and next time a permission is checked it will be reloaded
|
|
Log.outInfo(LogFilter.Rbac, "World.ReloadRBAC()");
|
|
foreach (var session in m_sessions.Values)
|
|
session.InvalidateRBACData();
|
|
}
|
|
|
|
public List<WorldSession> GetAllSessions()
|
|
{
|
|
return m_sessions.Values.ToList();
|
|
}
|
|
|
|
public int GetActiveAndQueuedSessionCount() { return m_sessions.Count; }
|
|
public int GetActiveSessionCount() { return m_sessions.Count - m_QueuedPlayer.Count; }
|
|
public int GetQueuedSessionCount() { return m_QueuedPlayer.Count; }
|
|
// Get the maximum number of parallel sessions on the server since last reboot
|
|
public uint GetMaxQueuedSessionCount() { return m_maxQueuedSessionCount; }
|
|
public uint GetMaxActiveSessionCount() { return m_maxActiveSessionCount; }
|
|
|
|
public uint GetPlayerCount() { return m_PlayerCount; }
|
|
public uint GetMaxPlayerCount() { return m_MaxPlayerCount; }
|
|
|
|
public void IncreasePlayerCount()
|
|
{
|
|
m_PlayerCount++;
|
|
m_MaxPlayerCount = Math.Max(m_MaxPlayerCount, m_PlayerCount);
|
|
}
|
|
public void DecreasePlayerCount() { m_PlayerCount--; }
|
|
|
|
public AccountTypes GetPlayerSecurityLimit() { return m_allowedSecurityLevel; }
|
|
|
|
public void SetPlayerAmountLimit(uint limit) { m_playerLimit = limit; }
|
|
public uint GetPlayerAmountLimit() { return m_playerLimit; }
|
|
|
|
/// Get the path where data (dbc, maps) are stored on disk
|
|
public string GetDataPath() { return _dataPath; }
|
|
|
|
public void SetDataPath(string path) { _dataPath = path; }
|
|
|
|
public long GetNextDailyQuestsResetTime() { return m_NextDailyQuestReset; }
|
|
public void SetNextDailyQuestsResetTime(long time) { m_NextDailyQuestReset = time; }
|
|
public long GetNextWeeklyQuestsResetTime() { return m_NextWeeklyQuestReset; }
|
|
public void SetNextWeeklyQuestsResetTime(long time) { m_NextWeeklyQuestReset = time; }
|
|
public long GetNextMonthlyQuestsResetTime() { return m_NextMonthlyQuestReset; }
|
|
public void SetNextMonthlyQuestsResetTime(long time) { m_NextMonthlyQuestReset = time; }
|
|
|
|
long GetNextRandomBGResetTime() { return m_NextRandomBGReset; }
|
|
|
|
public uint GetConfigMaxSkillValue()
|
|
{
|
|
int lvl = WorldConfig.GetIntValue(WorldCfg.MaxPlayerLevel);
|
|
return (uint)(lvl > 60 ? 300 + ((lvl - 60) * 75) / 10 : lvl * 5);
|
|
}
|
|
|
|
public bool IsShuttingDown() { return m_ShutdownTimer > 0; }
|
|
public uint GetShutDownTimeLeft() { return m_ShutdownTimer; }
|
|
|
|
public int GetExitCode() { return (int)m_ExitCode; }
|
|
|
|
public void StopNow(ShutdownExitCode exitcode = ShutdownExitCode.Error) { IsStopped = true; m_ExitCode = exitcode; }
|
|
|
|
public bool IsPvPRealm()
|
|
{
|
|
RealmType realmtype = (RealmType)WorldConfig.GetIntValue(WorldCfg.GameType);
|
|
return (realmtype == RealmType.PVP
|
|
|| realmtype == RealmType.RPPVP
|
|
|| realmtype == RealmType.FFAPVP);
|
|
}
|
|
public bool IsFFAPvPRealm()
|
|
{
|
|
return WorldConfig.GetIntValue(WorldCfg.GameType) == (int)RealmType.FFAPVP;
|
|
}
|
|
|
|
public Locale GetDefaultDbcLocale() { return m_defaultDbcLocale; }
|
|
|
|
public bool LoadRealmInfo()
|
|
{
|
|
SQLResult result = DB.Login.Query("SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild, Region, Battlegroup FROM realmlist WHERE id = {0}", _realm.Id.Index);
|
|
if (result.IsEmpty())
|
|
return false;
|
|
|
|
_realm.SetName(result.Read<string>(1));
|
|
_realm.ExternalAddress = System.Net.IPAddress.Parse(result.Read<string>(2));
|
|
_realm.LocalAddress = System.Net.IPAddress.Parse(result.Read<string>(3));
|
|
_realm.LocalSubnetMask = System.Net.IPAddress.Parse(result.Read<string>(4));
|
|
_realm.Port = result.Read<ushort>(5);
|
|
_realm.Type = result.Read<byte>(6);
|
|
_realm.Flags = (RealmFlags)result.Read<byte>(7);
|
|
_realm.Timezone = result.Read<byte>(8);
|
|
_realm.AllowedSecurityLevel = (AccountTypes)result.Read<byte>(9);
|
|
_realm.PopulationLevel = result.Read<float>(10);
|
|
_realm.Id.Region = result.Read<byte>(12);
|
|
_realm.Id.Site = result.Read<byte>(13);
|
|
_realm.Build = result.Read<uint>(11);
|
|
return true;
|
|
}
|
|
|
|
public Realm GetRealm() { return _realm; }
|
|
public RealmId GetRealmId() { return _realm.Id; }
|
|
|
|
public void RemoveOldCorpses()
|
|
{
|
|
m_timers[WorldTimers.Corpses].SetCurrent(m_timers[WorldTimers.Corpses].GetInterval());
|
|
}
|
|
|
|
void UpdateWarModeRewardValues()
|
|
{
|
|
long[] warModeEnabledFaction = new long[2];
|
|
|
|
// Search for characters that have war mode enabled and played during the last week
|
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_WAR_MODE_TUNING);
|
|
stmt.AddValue(0, (uint)PlayerFlags.WarModeDesired);
|
|
stmt.AddValue(1, (uint)PlayerFlags.WarModeDesired);
|
|
|
|
SQLResult result = DB.Characters.Query(stmt);
|
|
if (!result.IsEmpty())
|
|
{
|
|
do
|
|
{
|
|
byte race = result.Read<byte>(0);
|
|
|
|
var raceEntry = CliDB.ChrRacesStorage.LookupByKey(race);
|
|
if (raceEntry != null)
|
|
{
|
|
var raceFaction = CliDB.FactionTemplateStorage.LookupByKey(raceEntry.FactionID);
|
|
if (raceFaction != null)
|
|
{
|
|
if ((raceFaction.FactionGroup & (byte)FactionMasks.Alliance) != 0)
|
|
warModeEnabledFaction[TeamId.Alliance] += result.Read<long>(1);
|
|
else if ((raceFaction.FactionGroup & (byte)FactionMasks.Horde) != 0)
|
|
warModeEnabledFaction[TeamId.Horde] += result.Read<long>(1);
|
|
}
|
|
}
|
|
|
|
} while (result.NextRow());
|
|
}
|
|
|
|
_warModeDominantFaction = TeamId.Neutral;
|
|
_warModeOutnumberedFactionReward = 0;
|
|
|
|
if (warModeEnabledFaction.All(val => val == 0))
|
|
return;
|
|
|
|
long dominantFactionCount = warModeEnabledFaction[TeamId.Alliance];
|
|
int dominantFaction = TeamId.Alliance;
|
|
if (warModeEnabledFaction[TeamId.Alliance] < warModeEnabledFaction[TeamId.Horde])
|
|
{
|
|
dominantFactionCount = warModeEnabledFaction[TeamId.Horde];
|
|
dominantFaction = TeamId.Horde;
|
|
}
|
|
|
|
double total = warModeEnabledFaction[TeamId.Alliance] + warModeEnabledFaction[TeamId.Horde];
|
|
double pct = dominantFactionCount / total;
|
|
|
|
if (pct >= WorldConfig.GetFloatValue(WorldCfg.CallToArms20Pct))
|
|
_warModeOutnumberedFactionReward = 20;
|
|
else if (pct >= WorldConfig.GetFloatValue(WorldCfg.CallToArms10Pct))
|
|
_warModeOutnumberedFactionReward = 10;
|
|
else if (pct >= WorldConfig.GetFloatValue(WorldCfg.CallToArms5Pct))
|
|
_warModeOutnumberedFactionReward = 5;
|
|
else
|
|
return;
|
|
|
|
_warModeDominantFaction = dominantFaction;
|
|
}
|
|
|
|
public uint GetVirtualRealmAddress()
|
|
{
|
|
return _realm.Id.GetAddress();
|
|
}
|
|
|
|
public float GetMaxVisibleDistanceOnContinents() { return m_MaxVisibleDistanceOnContinents; }
|
|
public float GetMaxVisibleDistanceInInstances() { return m_MaxVisibleDistanceInInstances; }
|
|
public float GetMaxVisibleDistanceInBG() { return m_MaxVisibleDistanceInBG; }
|
|
public float GetMaxVisibleDistanceInArenas() { return m_MaxVisibleDistanceInArenas; }
|
|
|
|
public int GetVisibilityNotifyPeriodOnContinents() { return m_visibility_notify_periodOnContinents; }
|
|
public int GetVisibilityNotifyPeriodInInstances() { return m_visibility_notify_periodInInstances; }
|
|
public int GetVisibilityNotifyPeriodInBG() { return m_visibility_notify_periodInBG; }
|
|
public int GetVisibilityNotifyPeriodInArenas() { return m_visibility_notify_periodInArenas; }
|
|
|
|
public Locale GetAvailableDbcLocale(Locale locale)
|
|
{
|
|
if (m_availableDbcLocaleMask[(int)locale])
|
|
return locale;
|
|
else
|
|
return m_defaultDbcLocale;
|
|
}
|
|
|
|
public CleaningFlags GetCleaningFlags() { return m_CleaningFlags; }
|
|
public void SetCleaningFlags(CleaningFlags flags) { m_CleaningFlags = flags; }
|
|
|
|
public bool IsGuidWarning() { return _guidWarn; }
|
|
public bool IsGuidAlert() { return _guidAlert; }
|
|
|
|
// War mode balancing
|
|
public int GetWarModeDominantFaction() { return _warModeDominantFaction; }
|
|
public int GetWarModeOutnumberedFactionReward() { return _warModeOutnumberedFactionReward; }
|
|
|
|
public WorldUpdateTime GetWorldUpdateTime() { return _worldUpdateTime; }
|
|
|
|
#region Fields
|
|
uint m_ShutdownTimer;
|
|
ShutdownMask m_ShutdownMask;
|
|
ShutdownExitCode m_ExitCode;
|
|
public bool IsStopped;
|
|
|
|
Dictionary<byte, Autobroadcast> m_Autobroadcasts = new();
|
|
|
|
CleaningFlags m_CleaningFlags;
|
|
|
|
float m_MaxVisibleDistanceOnContinents = SharedConst.DefaultVisibilityDistance;
|
|
float m_MaxVisibleDistanceInInstances = SharedConst.DefaultVisibilityInstance;
|
|
float m_MaxVisibleDistanceInBG = SharedConst.DefaultVisibilityBGAreans;
|
|
float m_MaxVisibleDistanceInArenas = SharedConst.DefaultVisibilityBGAreans;
|
|
|
|
int m_visibility_notify_periodOnContinents = SharedConst.DefaultVisibilityNotifyPeriod;
|
|
int m_visibility_notify_periodInInstances = SharedConst.DefaultVisibilityNotifyPeriod;
|
|
int m_visibility_notify_periodInBG = SharedConst.DefaultVisibilityNotifyPeriod;
|
|
int m_visibility_notify_periodInArenas = SharedConst.DefaultVisibilityNotifyPeriod;
|
|
|
|
bool m_isClosed;
|
|
|
|
Dictionary<WorldTimers, IntervalTimer> m_timers = new();
|
|
long mail_timer;
|
|
long mail_timer_expires;
|
|
long blackmarket_timer;
|
|
|
|
ConcurrentDictionary<uint, WorldSession> m_sessions = new();
|
|
MultiMap<ObjectGuid, WorldSession> m_sessionsByBnetGuid = new();
|
|
Dictionary<uint, long> m_disconnects = new();
|
|
uint m_maxActiveSessionCount;
|
|
uint m_maxQueuedSessionCount;
|
|
uint m_PlayerCount;
|
|
uint m_MaxPlayerCount;
|
|
|
|
Dictionary<uint, uint> m_worldstates = new();
|
|
uint m_playerLimit;
|
|
AccountTypes m_allowedSecurityLevel;
|
|
Locale m_defaultDbcLocale; // from config for one from loaded DBC locales
|
|
BitSet m_availableDbcLocaleMask; // by loaded DBC
|
|
List<string> m_motd = new();
|
|
|
|
// scheduled reset times
|
|
long m_NextDailyQuestReset;
|
|
long m_NextWeeklyQuestReset;
|
|
long m_NextMonthlyQuestReset;
|
|
long m_NextRandomBGReset;
|
|
long m_NextCalendarOldEventsDeletionTime;
|
|
long m_NextGuildReset;
|
|
long m_NextCurrencyReset;
|
|
|
|
List<WorldSession> m_QueuedPlayer = new();
|
|
ConcurrentQueue<WorldSession> addSessQueue = new();
|
|
|
|
ConcurrentQueue<Tuple<WorldSocket, ulong>> _linkSocketQueue = new();
|
|
|
|
AsyncCallbackProcessor<QueryCallback> _queryProcessor = new();
|
|
|
|
Realm _realm;
|
|
|
|
string _dataPath;
|
|
|
|
WorldUpdateTime _worldUpdateTime;
|
|
|
|
string _guidWarningMsg;
|
|
string _alertRestartReason;
|
|
|
|
object _guidAlertLock = new();
|
|
|
|
bool _guidWarn;
|
|
bool _guidAlert;
|
|
uint _warnDiff;
|
|
long _warnShutdownTime;
|
|
|
|
// War mode balancing
|
|
int _warModeDominantFaction; // the team that has higher percentage
|
|
int _warModeOutnumberedFactionReward;
|
|
#endregion
|
|
}
|
|
|
|
/// Timers for different object refresh rates
|
|
public enum WorldTimers
|
|
{
|
|
Auctions,
|
|
AuctionsPending,
|
|
UpTime,
|
|
Corpses,
|
|
Events,
|
|
CleanDB,
|
|
AutoBroadcast,
|
|
MailBox,
|
|
DeleteChars,
|
|
AhBot,
|
|
PingDB,
|
|
GuildSave,
|
|
Blackmarket,
|
|
WhoList,
|
|
ChannelSave,
|
|
Max
|
|
}
|
|
|
|
public enum ServerMessageType
|
|
{
|
|
ShutdownTime = 1,
|
|
RestartTime = 2,
|
|
String = 3,
|
|
ShutdownCancelled = 4,
|
|
RestartCancelled = 5,
|
|
BgShutdownTime = 6,
|
|
BgRestartTime = 7,
|
|
InstanceShutdownTime = 8,
|
|
InstanceRestartTime = 9,
|
|
ContentReady = 10,
|
|
TicketServicedSoon = 11,
|
|
WaitTimeUnavailable = 12,
|
|
TicketWaitTime = 13,
|
|
}
|
|
|
|
[Flags]
|
|
public enum ShutdownMask
|
|
{
|
|
Restart = 1,
|
|
Idle = 2,
|
|
Force = 4
|
|
}
|
|
|
|
public enum ShutdownExitCode
|
|
{
|
|
Shutdown = 0,
|
|
Error = 1,
|
|
Restart = 2,
|
|
}
|
|
|
|
public class WorldWorldTextBuilder : MessageBuilder
|
|
{
|
|
public WorldWorldTextBuilder(uint textId, params object[] args)
|
|
{
|
|
i_textId = textId;
|
|
i_args = args;
|
|
}
|
|
|
|
public override MultiplePacketSender Invoke(Locale locale)
|
|
{
|
|
string text = Global.ObjectMgr.GetCypherString(i_textId, locale);
|
|
|
|
if (i_args != null)
|
|
text = string.Format(text, i_args);
|
|
|
|
MultiplePacketSender sender = new();
|
|
|
|
var lines = new StringArray(text, "\n");
|
|
for (var i = 0; i < lines.Length; ++i)
|
|
{
|
|
ChatPkt messageChat = new();
|
|
messageChat.Initialize(ChatMsg.System, Language.Universal, null, null, lines[i]);
|
|
messageChat.Write();
|
|
sender.Packets.Add(messageChat);
|
|
}
|
|
|
|
return sender;
|
|
}
|
|
|
|
uint i_textId;
|
|
object[] i_args;
|
|
|
|
public class MultiplePacketSender : IDoWork<Player>
|
|
{
|
|
public void Invoke(Player receiver)
|
|
{
|
|
foreach (var packet in Packets)
|
|
receiver.SendPacket(packet);
|
|
}
|
|
|
|
public List<ServerPacket> Packets = new();
|
|
}
|
|
}
|
|
|
|
struct Autobroadcast
|
|
{
|
|
public Autobroadcast(string message, byte weight)
|
|
{
|
|
Message = message;
|
|
Weight = weight;
|
|
}
|
|
|
|
public string Message;
|
|
public byte Weight;
|
|
}
|
|
}
|