Skip to content

Commit

Permalink
Disallow [re]entry into world if shutdown is in progress (#2794)
Browse files Browse the repository at this point in the history
* Disallow [re]entry into world if shutdown is in progress

* Update Session.cs

* Update Session.cs

* Update ServerManager.cs

* session management

* Update ServerManager.cs

* Update Session.cs
  • Loading branch information
LtRipley36706 authored Apr 12, 2020
1 parent 901c529 commit af97c1b
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 16 deletions.
67 changes: 56 additions & 11 deletions Source/ACE.Server/Managers/ServerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using ACE.Database;
using ACE.Entity.Enum;
using ACE.Server.Network.GameMessages.Messages;
using ACE.Server.Network.Managers;

namespace ACE.Server.Managers
{
Expand All @@ -29,6 +30,11 @@ public static class ServerManager
/// </summary>
public static bool ShutdownInitiated { get; private set; }

/// <summary>
/// Indicates server shutting down.
/// </summary>
public static bool ShutdownInProgress { get; private set; }

/// <summary>
/// The amount of seconds that the server will wait before unloading the application.
/// </summary>
Expand Down Expand Up @@ -116,6 +122,8 @@ private static void ShutdownServer()
Thread.Sleep(10);
}

ShutdownInProgress = true;

PropertyManager.ResyncVariables();
PropertyManager.StopUpdating();

Expand All @@ -125,42 +133,68 @@ private static void ShutdownServer()
foreach (var player in PlayerManager.GetAllOnline())
player.Session.LogOffPlayer(true);

log.Info("Waiting for all players to log off...");
// Wait for all players to log out
var logUpdateTS = DateTime.MinValue;
int playerCount;
while ((playerCount = PlayerManager.GetOnlineCount()) > 0)
{
logUpdateTS = LogStatusUpdate(logUpdateTS, $"Waiting for {playerCount} player{(playerCount > 1 ? "s" : "")} to log off...");
Thread.Sleep(10);
}

// wait 10 seconds for log-off
while (PlayerManager.GetOnlineCount() > 0)
log.Debug("Disconnecting all sessions...");

// disconnect each session
NetworkManager.DisconnectAllSessionsForShutdown();

// Wait for all sessions to drop out
logUpdateTS = DateTime.MinValue;
int sessionCount;
while ((sessionCount = NetworkManager.GetSessionCount()) > 0)
{
logUpdateTS = LogStatusUpdate(logUpdateTS, $"Waiting for {sessionCount} session{(sessionCount > 1 ? "s" : "")} to disconnect...");
Thread.Sleep(10);
}

log.Debug("Adding all landblocks to destruction queue...");

// Queue unloading of all the landblocks
// The actual unloading will happen in WorldManager.UpdateGameWorld
LandblockManager.AddAllActiveLandblocksToDestructionQueue();

log.Info("Waiting for all active landblocks to unload...");

while (LandblockManager.GetLoadedLandblocks().Count > 0)
// Wait for all landblocks to unload
logUpdateTS = DateTime.MinValue;
int landblockCount;
while ((landblockCount = LandblockManager.GetLoadedLandblocks().Count) > 0)
{
logUpdateTS = LogStatusUpdate(logUpdateTS, $"Waiting for {landblockCount} loaded landblock{(landblockCount > 1 ? "s" : "")} to unload...");
Thread.Sleep(10);
}

log.Debug("Stopping world...");

// Disabled thread update loop
WorldManager.StopWorld();

log.Info("Waiting for world to stop...");

// Wait for world to end
logUpdateTS = DateTime.MinValue;
while (WorldManager.WorldActive)
{
logUpdateTS = LogStatusUpdate(logUpdateTS, "Waiting for world to stop...");
Thread.Sleep(10);
}

log.Info("Saving OfflinePlayers that have unsaved changes...");
PlayerManager.SaveOfflinePlayersWithChanges();

log.Info("Waiting for database queue to empty...");

// Wait for the database queue to empty
while (DatabaseManager.Shard.QueueCount > 0)
logUpdateTS = DateTime.MinValue;
int shardQueueCount;
while ((shardQueueCount = DatabaseManager.Shard.QueueCount) > 0)
{
logUpdateTS = LogStatusUpdate(logUpdateTS, $"Waiting for database queue ({shardQueueCount}) to empty...");
Thread.Sleep(10);
}

// Write exit to console/log
log.Info($"Exiting at {DateTime.UtcNow}");
Expand All @@ -169,6 +203,17 @@ private static void ShutdownServer()
Environment.Exit(Environment.ExitCode);
}

private static DateTime LogStatusUpdate(DateTime logUpdateTS, string logMessage)
{
if (logUpdateTS == DateTime.MinValue || DateTime.UtcNow > logUpdateTS.ToUniversalTime())
{
log.Info(logMessage);
logUpdateTS = DateTime.UtcNow.AddSeconds(10);
}

return logUpdateTS;
}

private static DateTime NotifyPlayersOfPendingShutdown(DateTime lastNoticeTime, DateTime shutdownTime)
{
var notify = false;
Expand Down
2 changes: 2 additions & 0 deletions Source/ACE.Server/Network/Enum/SessionTerminationReason.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public enum SessionTerminationReason
WorldClosed,
AbnormalSequenceReceived,
AccountLoggedIn,
ServerShuttingDown,
AccountBanned
}
public static class SessionTerminationReasonHelper
Expand All @@ -52,6 +53,7 @@ public static class SessionTerminationReasonHelper
"World is closed",
"Client supplied an abnormal sequence",
"Account was logged in, booting currently connected account in favor of new connection",
"Server is shutting down",
"Account is banned"
};
public static string GetDescription(this SessionTerminationReason reason)
Expand Down
30 changes: 30 additions & 0 deletions Source/ACE.Server/Network/Handlers/CharacterHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ public static void CharacterCreate(ClientMessage message, Session session)
if (clientString != session.Account)
return;

if (ServerManager.ShutdownInProgress)
{
session.SendCharacterError(CharacterError.LogonServerFull);
return;
}

if (WorldManager.WorldStatus == WorldManager.WorldStatusState.Open || session.AccessLevel > AccessLevel.Player)
CharacterCreateEx(message, session);
else
Expand Down Expand Up @@ -177,6 +183,12 @@ private static void CharacterCreateEx(ClientMessage message, Session session)
[GameMessage(GameMessageOpcode.CharacterEnterWorldRequest, SessionState.AuthConnected)]
public static void CharacterEnterWorldRequest(ClientMessage message, Session session)
{
if (ServerManager.ShutdownInProgress)
{
session.SendCharacterError(CharacterError.LogonServerFull);
return;
}

if (WorldManager.WorldStatus == WorldManager.WorldStatusState.Open || session.AccessLevel > AccessLevel.Player)
session.Network.EnqueueSend(new GameMessageCharacterEnterWorldServerReady());
else
Expand All @@ -190,6 +202,12 @@ public static void CharacterEnterWorld(ClientMessage message, Session session)

string clientString = message.Payload.ReadString16L();

if (ServerManager.ShutdownInProgress)
{
session.SendCharacterError(CharacterError.LogonServerFull);
return;
}

if (clientString != session.Account)
{
session.SendCharacterError(CharacterError.EnterGameCharacterNotOwned);
Expand Down Expand Up @@ -252,6 +270,12 @@ public static void CharacterDelete(ClientMessage message, Session session)
string clientString = message.Payload.ReadString16L();
uint characterSlot = message.Payload.ReadUInt32();

if (ServerManager.ShutdownInProgress)
{
session.SendCharacterError(CharacterError.Delete);
return;
}

if (WorldManager.WorldStatus == WorldManager.WorldStatusState.Closed && session.AccessLevel < AccessLevel.Advocate)
{
session.SendCharacterError(CharacterError.LogonServerFull);
Expand Down Expand Up @@ -303,6 +327,12 @@ public static void CharacterRestore(ClientMessage message, Session session)
{
var guid = message.Payload.ReadUInt32();

if (ServerManager.ShutdownInProgress)
{
session.SendCharacterError(CharacterError.EnterGameCouldntPlaceCharacter);
return;
}

if (WorldManager.WorldStatus == WorldManager.WorldStatusState.Closed && session.AccessLevel < AccessLevel.Advocate)
{
session.SendCharacterError(CharacterError.LogonServerFull);
Expand Down
13 changes: 13 additions & 0 deletions Source/ACE.Server/Network/Managers/NetworkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ public static void ProcessPacket(ConnectionListener connectionListener, ClientPa
log.InfoFormat("Login Request from {0} rejected. Server full.", endPoint);
SendLoginRequestReject(connectionListener, endPoint, CharacterError.LogonServerFull);
}
else if (ServerManager.ShutdownInProgress)
{
log.InfoFormat("Login Request from {0} rejected. Server is shutting down.", endPoint);
SendLoginRequestReject(connectionListener, endPoint, CharacterError.ServerCrash1);
}
else if (ServerManager.ShutdownInitiated && (ServerManager.ShutdownTime - DateTime.UtcNow).TotalMinutes < 2)
{
log.InfoFormat("Login Request from {0} rejected. Server shutting down in less than 2 minutes.", endPoint);
Expand Down Expand Up @@ -359,5 +364,13 @@ public static int DoSessionWork()
}
return sessionCount;
}

public static void DisconnectAllSessionsForShutdown()
{
foreach (var session in sessionMap)
{
session?.Terminate(SessionTerminationReason.ServerShuttingDown, new GameMessages.Messages.GameMessageCharacterError(CharacterError.ServerCrash1));
}
}
}
}
15 changes: 10 additions & 5 deletions Source/ACE.Server/Network/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ public void SetPlayer(Player player)
/// </summary>
public void LogOffPlayer(bool forceImmediate = false)
{
if (Player == null) return;

if (logOffRequestTime == DateTime.MinValue)
{
var result = Player.LogOut(false, forceImmediate);
Expand All @@ -201,14 +203,17 @@ private void SendFinalLogOffMessages()

Player = null;

Network.EnqueueSend(new GameMessageCharacterLogOff());
if (!ServerManager.ShutdownInProgress)
{
Network.EnqueueSend(new GameMessageCharacterLogOff());

CheckCharactersForDeletion();
CheckCharactersForDeletion();

Network.EnqueueSend(new GameMessageCharacterList(Characters, this));
Network.EnqueueSend(new GameMessageCharacterList(Characters, this));

GameMessageServerName serverNameMessage = new GameMessageServerName(ConfigManager.Config.Server.WorldName, PlayerManager.GetOnlineCount(), (int)ConfigManager.Config.Server.Network.MaximumAllowedSessions);
Network.EnqueueSend(serverNameMessage);
GameMessageServerName serverNameMessage = new GameMessageServerName(ConfigManager.Config.Server.WorldName, PlayerManager.GetOnlineCount(), (int)ConfigManager.Config.Server.Network.MaximumAllowedSessions);
Network.EnqueueSend(serverNameMessage);
}

State = SessionState.AuthConnected;
}
Expand Down

0 comments on commit af97c1b

Please sign in to comment.