Random refactoring and beautifying of Server.cs
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@@ -24,7 +25,7 @@ using XTimer = System.Timers.Timer;
|
|||||||
|
|
||||||
namespace OpenRA.Server
|
namespace OpenRA.Server
|
||||||
{
|
{
|
||||||
public enum ServerState : int
|
public enum ServerState
|
||||||
{
|
{
|
||||||
WaitingPlayers = 1,
|
WaitingPlayers = 1,
|
||||||
GameStarted = 2,
|
GameStarted = 2,
|
||||||
@@ -35,8 +36,6 @@ namespace OpenRA.Server
|
|||||||
{
|
{
|
||||||
public readonly IPAddress Ip;
|
public readonly IPAddress Ip;
|
||||||
public readonly int Port;
|
public readonly int Port;
|
||||||
|
|
||||||
int randomSeed;
|
|
||||||
public readonly MersenneTwister Random = new MersenneTwister();
|
public readonly MersenneTwister Random = new MersenneTwister();
|
||||||
|
|
||||||
// Valid player connections
|
// Valid player connections
|
||||||
@@ -45,44 +44,53 @@ namespace OpenRA.Server
|
|||||||
// Pre-verified player connections
|
// Pre-verified player connections
|
||||||
public List<Connection> PreConns = new List<Connection>();
|
public List<Connection> PreConns = new List<Connection>();
|
||||||
|
|
||||||
TcpListener listener = null;
|
|
||||||
|
|
||||||
TypeDictionary serverTraits = new TypeDictionary();
|
|
||||||
public Session LobbyInfo;
|
public Session LobbyInfo;
|
||||||
|
|
||||||
public ServerSettings Settings;
|
public ServerSettings Settings;
|
||||||
public ModData ModData;
|
public ModData ModData;
|
||||||
|
public List<string> TempBans = new List<string>();
|
||||||
|
|
||||||
// Managed by LobbyCommands
|
// Managed by LobbyCommands
|
||||||
public Map Map;
|
public Map Map;
|
||||||
public MapPlayers MapPlayers;
|
public MapPlayers MapPlayers;
|
||||||
|
|
||||||
|
readonly int randomSeed;
|
||||||
|
readonly TcpListener listener;
|
||||||
|
readonly TypeDictionary serverTraits = new TypeDictionary();
|
||||||
|
|
||||||
XTimer gameTimeout;
|
XTimer gameTimeout;
|
||||||
|
|
||||||
|
protected volatile ServerState internalState = ServerState.WaitingPlayers;
|
||||||
|
|
||||||
|
public ServerState State
|
||||||
|
{
|
||||||
|
get { return internalState; }
|
||||||
|
protected set { internalState = value; }
|
||||||
|
}
|
||||||
|
|
||||||
public static void SyncClientToPlayerReference(Session.Client c, PlayerReference pr)
|
public static void SyncClientToPlayerReference(Session.Client c, PlayerReference pr)
|
||||||
{
|
{
|
||||||
if (pr == null)
|
if (pr == null)
|
||||||
return;
|
return;
|
||||||
if (pr.LockColor)
|
|
||||||
c.Color = pr.Color;
|
|
||||||
else
|
|
||||||
c.Color = c.PreferredColor;
|
|
||||||
if (pr.LockRace)
|
if (pr.LockRace)
|
||||||
c.Race = pr.Race;
|
c.Race = pr.Race;
|
||||||
if (pr.LockSpawn)
|
if (pr.LockSpawn)
|
||||||
c.SpawnPoint = pr.Spawn;
|
c.SpawnPoint = pr.Spawn;
|
||||||
if (pr.LockTeam)
|
if (pr.LockTeam)
|
||||||
c.Team = pr.Team;
|
c.Team = pr.Team;
|
||||||
|
|
||||||
|
c.Color = pr.LockColor ? pr.Color : c.PreferredColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SendData(Socket s, byte[] data)
|
static void SendData(Socket s, byte[] data)
|
||||||
{
|
{
|
||||||
var start = 0;
|
var start = 0;
|
||||||
var length = data.Length;
|
var length = data.Length;
|
||||||
SocketError error;
|
|
||||||
|
|
||||||
// Non-blocking sends are free to send only part of the data
|
// Non-blocking sends are free to send only part of the data
|
||||||
while (start < length)
|
while (start < length)
|
||||||
{
|
{
|
||||||
|
SocketError error;
|
||||||
var sent = s.Send(data, start, length - start, SocketFlags.None, out error);
|
var sent = s.Send(data, start, length - start, SocketFlags.None, out error);
|
||||||
if (error == SocketError.WouldBlock)
|
if (error == SocketError.WouldBlock)
|
||||||
{
|
{
|
||||||
@@ -98,15 +106,6 @@ namespace OpenRA.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected volatile ServerState internalState = new ServerState();
|
|
||||||
public ServerState State
|
|
||||||
{
|
|
||||||
get { return internalState; }
|
|
||||||
protected set { internalState = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<string> TempBans = new List<string>();
|
|
||||||
|
|
||||||
public void Shutdown()
|
public void Shutdown()
|
||||||
{
|
{
|
||||||
State = ServerState.ShuttingDown;
|
State = ServerState.ShuttingDown;
|
||||||
@@ -122,7 +121,6 @@ namespace OpenRA.Server
|
|||||||
{
|
{
|
||||||
Log.AddChannel("server", "server.log");
|
Log.AddChannel("server", "server.log");
|
||||||
|
|
||||||
internalState = ServerState.WaitingPlayers;
|
|
||||||
listener = new TcpListener(endpoint);
|
listener = new TcpListener(endpoint);
|
||||||
listener.Start();
|
listener.Start();
|
||||||
var localEndpoint = (IPEndPoint)listener.LocalEndpoint;
|
var localEndpoint = (IPEndPoint)listener.LocalEndpoint;
|
||||||
@@ -143,11 +141,17 @@ namespace OpenRA.Server
|
|||||||
foreach (var trait in modData.Manifest.ServerTraits)
|
foreach (var trait in modData.Manifest.ServerTraits)
|
||||||
serverTraits.Add(modData.ObjectCreator.CreateObject<ServerTrait>(trait));
|
serverTraits.Add(modData.ObjectCreator.CreateObject<ServerTrait>(trait));
|
||||||
|
|
||||||
LobbyInfo = new Session();
|
LobbyInfo = new Session
|
||||||
LobbyInfo.GlobalSettings.RandomSeed = randomSeed;
|
{
|
||||||
LobbyInfo.GlobalSettings.Map = settings.Map;
|
GlobalSettings =
|
||||||
LobbyInfo.GlobalSettings.ServerName = settings.Name;
|
{
|
||||||
LobbyInfo.GlobalSettings.Dedicated = settings.Dedicated;
|
RandomSeed = randomSeed,
|
||||||
|
Map = settings.Map,
|
||||||
|
ServerName = settings.Name,
|
||||||
|
Dedicated = settings.Dedicated
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
FieldLoader.Load(LobbyInfo.GlobalSettings, modData.Manifest.LobbyDefaults);
|
FieldLoader.Load(LobbyInfo.GlobalSettings, modData.Manifest.LobbyDefaults);
|
||||||
|
|
||||||
foreach (var t in serverTraits.WithInterface<INotifyServerStart>())
|
foreach (var t in serverTraits.WithInterface<INotifyServerStart>())
|
||||||
@@ -162,11 +166,15 @@ namespace OpenRA.Server
|
|||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
var checkRead = new List<Socket>();
|
var checkRead = new List<Socket>();
|
||||||
if (State == ServerState.WaitingPlayers) checkRead.Add(listener.Server);
|
if (State == ServerState.WaitingPlayers)
|
||||||
foreach (var c in Conns) checkRead.Add(c.Socket);
|
checkRead.Add(listener.Server);
|
||||||
foreach (var c in PreConns) checkRead.Add(c.Socket);
|
|
||||||
|
checkRead.AddRange(Conns.Select(c => c.Socket));
|
||||||
|
checkRead.AddRange(PreConns.Select(c => c.Socket));
|
||||||
|
|
||||||
|
if (checkRead.Count > 0)
|
||||||
|
Socket.Select(checkRead, null, null, timeout);
|
||||||
|
|
||||||
if (checkRead.Count > 0) Socket.Select(checkRead, null, null, timeout);
|
|
||||||
if (State == ServerState.ShuttingDown)
|
if (State == ServerState.ShuttingDown)
|
||||||
{
|
{
|
||||||
EndGame();
|
EndGame();
|
||||||
@@ -174,7 +182,9 @@ namespace OpenRA.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (var s in checkRead)
|
foreach (var s in checkRead)
|
||||||
if (s == listener.Server) AcceptConnection();
|
{
|
||||||
|
if (s == listener.Server)
|
||||||
|
AcceptConnection();
|
||||||
else if (PreConns.Count > 0)
|
else if (PreConns.Count > 0)
|
||||||
{
|
{
|
||||||
var p = PreConns.SingleOrDefault(c => c.Socket == s);
|
var p = PreConns.SingleOrDefault(c => c.Socket == s);
|
||||||
@@ -185,6 +195,7 @@ namespace OpenRA.Server
|
|||||||
var conn = Conns.SingleOrDefault(c => c.Socket == s);
|
var conn = Conns.SingleOrDefault(c => c.Socket == s);
|
||||||
if (conn != null) conn.ReadData(this);
|
if (conn != null) conn.ReadData(this);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var t in serverTraits.WithInterface<ITick>())
|
foreach (var t in serverTraits.WithInterface<ITick>())
|
||||||
t.Tick(this);
|
t.Tick(this);
|
||||||
@@ -211,7 +222,7 @@ namespace OpenRA.Server
|
|||||||
* - "teams together" option for team games -- will eliminate most need
|
* - "teams together" option for team games -- will eliminate most need
|
||||||
* for manual spawnpoint choosing.
|
* for manual spawnpoint choosing.
|
||||||
*/
|
*/
|
||||||
int nextPlayerIndex = 0;
|
int nextPlayerIndex;
|
||||||
public int ChooseFreePlayerIndex()
|
public int ChooseFreePlayerIndex()
|
||||||
{
|
{
|
||||||
return nextPlayerIndex++;
|
return nextPlayerIndex++;
|
||||||
@@ -219,11 +230,13 @@ namespace OpenRA.Server
|
|||||||
|
|
||||||
void AcceptConnection()
|
void AcceptConnection()
|
||||||
{
|
{
|
||||||
Socket newSocket = null;
|
Socket newSocket;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!listener.Server.IsBound) return;
|
if (!listener.Server.IsBound)
|
||||||
|
return;
|
||||||
|
|
||||||
newSocket = listener.AcceptSocket();
|
newSocket = listener.AcceptSocket();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -247,7 +260,7 @@ namespace OpenRA.Server
|
|||||||
PreConns.Add(newConn);
|
PreConns.Add(newConn);
|
||||||
|
|
||||||
// Dispatch a handshake order
|
// Dispatch a handshake order
|
||||||
var request = new HandshakeRequest()
|
var request = new HandshakeRequest
|
||||||
{
|
{
|
||||||
Mod = ModData.Manifest.Mod.Id,
|
Mod = ModData.Manifest.Mod.Id,
|
||||||
Version = ModData.Manifest.Mod.Version,
|
Version = ModData.Manifest.Mod.Version,
|
||||||
@@ -259,7 +272,7 @@ namespace OpenRA.Server
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
DropClient(newConn);
|
DropClient(newConn);
|
||||||
Log.Write("server", "Dropping client {0} because handshake failed: {1}", newConn.PlayerIndex.ToString(), e);
|
Log.Write("server", "Dropping client {0} because handshake failed: {1}", newConn.PlayerIndex.ToString(CultureInfo.InvariantCulture), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +300,7 @@ namespace OpenRA.Server
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var client = new Session.Client()
|
var client = new Session.Client
|
||||||
{
|
{
|
||||||
Name = OpenRA.Settings.SanitizedPlayerName(handshake.Client.Name),
|
Name = OpenRA.Settings.SanitizedPlayerName(handshake.Client.Name),
|
||||||
IpAddress = ((IPEndPoint)newConn.Socket.RemoteEndPoint).Address.ToString(),
|
IpAddress = ((IPEndPoint)newConn.Socket.RemoteEndPoint).Address.ToString(),
|
||||||
@@ -348,8 +361,7 @@ namespace OpenRA.Server
|
|||||||
PreConns.Remove(newConn);
|
PreConns.Remove(newConn);
|
||||||
Conns.Add(newConn);
|
Conns.Add(newConn);
|
||||||
LobbyInfo.Clients.Add(client);
|
LobbyInfo.Clients.Add(client);
|
||||||
var clientPing = new Session.ClientPing();
|
var clientPing = new Session.ClientPing { Index = client.Index };
|
||||||
clientPing.Index = client.Index;
|
|
||||||
LobbyInfo.ClientPings.Add(clientPing);
|
LobbyInfo.ClientPings.Add(clientPing);
|
||||||
|
|
||||||
Log.Write("server", "Client {0}: Accepted connection from {1}.",
|
Log.Write("server", "Client {0}: Accepted connection from {1}.",
|
||||||
@@ -362,21 +374,22 @@ namespace OpenRA.Server
|
|||||||
SendMessage("{0} has joined the game.".F(client.Name));
|
SendMessage("{0} has joined the game.".F(client.Name));
|
||||||
|
|
||||||
// Send initial ping
|
// Send initial ping
|
||||||
SendOrderTo(newConn, "Ping", Game.RunTime.ToString());
|
SendOrderTo(newConn, "Ping", Game.RunTime.ToString(CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
if (Settings.Dedicated)
|
if (Settings.Dedicated)
|
||||||
{
|
{
|
||||||
var motdFile = Platform.ResolvePath("^", "motd.txt");
|
var motdFile = Platform.ResolvePath("^", "motd.txt");
|
||||||
if (!File.Exists(motdFile))
|
if (!File.Exists(motdFile))
|
||||||
System.IO.File.WriteAllText(motdFile, "Welcome, have fun and good luck!");
|
File.WriteAllText(motdFile, "Welcome, have fun and good luck!");
|
||||||
var motd = System.IO.File.ReadAllText(motdFile);
|
|
||||||
|
var motd = File.ReadAllText(motdFile);
|
||||||
if (!string.IsNullOrEmpty(motd))
|
if (!string.IsNullOrEmpty(motd))
|
||||||
SendOrderTo(newConn, "Message", motd);
|
SendOrderTo(newConn, "Message", motd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handshake.Mod == "{DEV_VERSION}")
|
if (handshake.Mod == "{DEV_VERSION}")
|
||||||
SendMessage("{0} is running an unversioned development build, ".F(client.Name) +
|
SendMessage("{0} is running an unversioned development build, ".F(client.Name) +
|
||||||
"and may desynchronize the game state if they have incompatible rules.");
|
"and may desynchronize the game state if they have incompatible rules.");
|
||||||
|
|
||||||
SetOrderLag();
|
SetOrderLag();
|
||||||
}
|
}
|
||||||
@@ -390,11 +403,7 @@ namespace OpenRA.Server
|
|||||||
|
|
||||||
void SetOrderLag()
|
void SetOrderLag()
|
||||||
{
|
{
|
||||||
if (LobbyInfo.IsSinglePlayer)
|
LobbyInfo.GlobalSettings.OrderLatency = LobbyInfo.IsSinglePlayer ? 1 : 3;
|
||||||
LobbyInfo.GlobalSettings.OrderLatency = 1;
|
|
||||||
else
|
|
||||||
LobbyInfo.GlobalSettings.OrderLatency = 3;
|
|
||||||
|
|
||||||
SyncLobbyGlobalSettings();
|
SyncLobbyGlobalSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,7 +419,8 @@ namespace OpenRA.Server
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
DropClient(c);
|
DropClient(c);
|
||||||
Log.Write("server", "Dropping client {0} because dispatching orders failed: {1}", client.ToString(), e);
|
Log.Write("server", "Dropping client {0} because dispatching orders failed: {1}",
|
||||||
|
client.ToString(CultureInfo.InvariantCulture), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,12 +476,10 @@ namespace OpenRA.Server
|
|||||||
{
|
{
|
||||||
case "Command":
|
case "Command":
|
||||||
{
|
{
|
||||||
var handled = false;
|
var handledBy = serverTraits.WithInterface<IInterpretCommand>()
|
||||||
foreach (var t in serverTraits.WithInterface<IInterpretCommand>())
|
.FirstOrDefault(t => t.InterpretCommand(this, conn, GetClient(conn), so.Data));
|
||||||
if (handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!handled)
|
if (handledBy == null)
|
||||||
{
|
{
|
||||||
Log.Write("server", "Unknown server command: {0}", so.Data);
|
Log.Write("server", "Unknown server command: {0}", so.Data);
|
||||||
SendOrderTo(conn, "Message", "Unknown server command: {0}".F(so.Data));
|
SendOrderTo(conn, "Message", "Unknown server command: {0}".F(so.Data));
|
||||||
@@ -597,8 +605,7 @@ namespace OpenRA.Server
|
|||||||
public void SyncLobbyInfo()
|
public void SyncLobbyInfo()
|
||||||
{
|
{
|
||||||
if (State == ServerState.WaitingPlayers) // Don't do this while the game is running, it breaks things!
|
if (State == ServerState.WaitingPlayers) // Don't do this while the game is running, it breaks things!
|
||||||
DispatchOrders(null, 0,
|
DispatchOrders(null, 0, new ServerOrder("SyncInfo", LobbyInfo.Serialize()).Serialize());
|
||||||
new ServerOrder("SyncInfo", LobbyInfo.Serialize()).Serialize());
|
|
||||||
|
|
||||||
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
|
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
|
||||||
t.LobbyInfoSynced(this);
|
t.LobbyInfoSynced(this);
|
||||||
@@ -609,13 +616,10 @@ namespace OpenRA.Server
|
|||||||
if (State != ServerState.WaitingPlayers)
|
if (State != ServerState.WaitingPlayers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO: only need to sync the specific client that has changed to avoid conflicts
|
// TODO: Only need to sync the specific client that has changed to avoid conflicts!
|
||||||
var clientData = new List<MiniYamlNode>();
|
var clientData = LobbyInfo.Clients.Select(client => client.Serialize()).ToList();
|
||||||
foreach (var client in LobbyInfo.Clients)
|
|
||||||
clientData.Add(client.Serialize());
|
|
||||||
|
|
||||||
DispatchOrders(null, 0,
|
DispatchOrders(null, 0, new ServerOrder("SyncLobbyClients", clientData.WriteToString()).Serialize());
|
||||||
new ServerOrder("SyncLobbyClients", clientData.WriteToString()).Serialize());
|
|
||||||
|
|
||||||
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
|
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
|
||||||
t.LobbyInfoSynced(this);
|
t.LobbyInfoSynced(this);
|
||||||
@@ -626,13 +630,10 @@ namespace OpenRA.Server
|
|||||||
if (State != ServerState.WaitingPlayers)
|
if (State != ServerState.WaitingPlayers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO: don't sync all the slots if just one changed
|
// TODO: Don't sync all the slots if just one changed!
|
||||||
var slotData = new List<MiniYamlNode>();
|
var slotData = LobbyInfo.Slots.Select(slot => slot.Value.Serialize()).ToList();
|
||||||
foreach (var slot in LobbyInfo.Slots)
|
|
||||||
slotData.Add(slot.Value.Serialize());
|
|
||||||
|
|
||||||
DispatchOrders(null, 0,
|
DispatchOrders(null, 0, new ServerOrder("SyncLobbySlots", slotData.WriteToString()).Serialize());
|
||||||
new ServerOrder("SyncLobbySlots", slotData.WriteToString()).Serialize());
|
|
||||||
|
|
||||||
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
|
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
|
||||||
t.LobbyInfoSynced(this);
|
t.LobbyInfoSynced(this);
|
||||||
@@ -643,11 +644,9 @@ namespace OpenRA.Server
|
|||||||
if (State != ServerState.WaitingPlayers)
|
if (State != ServerState.WaitingPlayers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var sessionData = new List<MiniYamlNode>();
|
var sessionData = new List<MiniYamlNode> { LobbyInfo.GlobalSettings.Serialize() };
|
||||||
sessionData.Add(LobbyInfo.GlobalSettings.Serialize());
|
|
||||||
|
|
||||||
DispatchOrders(null, 0,
|
DispatchOrders(null, 0, new ServerOrder("SyncLobbyGlobalSettings", sessionData.WriteToString()).Serialize());
|
||||||
new ServerOrder("SyncLobbyGlobalSettings", sessionData.WriteToString()).Serialize());
|
|
||||||
|
|
||||||
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
|
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
|
||||||
t.LobbyInfoSynced(this);
|
t.LobbyInfoSynced(this);
|
||||||
@@ -655,13 +654,10 @@ namespace OpenRA.Server
|
|||||||
|
|
||||||
public void SyncClientPing()
|
public void SyncClientPing()
|
||||||
{
|
{
|
||||||
// TODO: split this further into per client ping orders
|
// TODO: Split this further into per client ping orders
|
||||||
var clientPings = new List<MiniYamlNode>();
|
var clientPings = LobbyInfo.ClientPings.Select(ping => ping.Serialize()).ToList();
|
||||||
foreach (var ping in LobbyInfo.ClientPings)
|
|
||||||
clientPings.Add(ping.Serialize());
|
|
||||||
|
|
||||||
DispatchOrders(null, 0,
|
DispatchOrders(null, 0, new ServerOrder("SyncClientPings", clientPings.WriteToString()).Serialize());
|
||||||
new ServerOrder("SyncClientPings", clientPings.WriteToString()).Serialize());
|
|
||||||
|
|
||||||
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
|
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
|
||||||
t.LobbyInfoSynced(this);
|
t.LobbyInfoSynced(this);
|
||||||
@@ -678,13 +674,10 @@ namespace OpenRA.Server
|
|||||||
DropClient(c);
|
DropClient(c);
|
||||||
|
|
||||||
// Drop any players who are not ready
|
// Drop any players who are not ready
|
||||||
foreach (var c in Conns.ToArray())
|
foreach (var c in Conns.Where(c => GetClient(c).IsInvalid))
|
||||||
{
|
{
|
||||||
if (GetClient(c).IsInvalid)
|
SendOrderTo(c, "ServerError", "You have been kicked from the server!");
|
||||||
{
|
DropClient(c);
|
||||||
SendOrderTo(c, "ServerError", "You have been kicked from the server");
|
|
||||||
DropClient(c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncLobbyInfo();
|
SyncLobbyInfo();
|
||||||
|
|||||||
Reference in New Issue
Block a user