Lock Server.LobbyInfo to prevent races with callback threads.

This commit is contained in:
Paul Chote
2020-09-20 12:54:03 +01:00
committed by abcdefg30
parent 8d1f72c104
commit 0672553a07
4 changed files with 1156 additions and 1040 deletions

View File

@@ -430,6 +430,8 @@ namespace OpenRA.Server
} }
Action completeConnection = () => Action completeConnection = () =>
{
lock (LobbyInfo)
{ {
client.Slot = LobbyInfo.FirstEmptySlot(); client.Slot = LobbyInfo.FirstEmptySlot();
client.IsAdmin = !LobbyInfo.Clients.Any(c1 => c1.IsAdmin); client.IsAdmin = !LobbyInfo.Clients.Any(c1 => c1.IsAdmin);
@@ -494,6 +496,7 @@ namespace OpenRA.Server
SendOrderTo(newConn, "Message", TwoHumansRequiredText); SendOrderTo(newConn, "Message", TwoHumansRequiredText);
else if (Map.Players.Players.Where(p => p.Value.Playable).All(p => !p.Value.AllowBots)) else if (Map.Players.Players.Where(p => p.Value.Playable).All(p => !p.Value.AllowBots))
SendOrderTo(newConn, "Message", "Bots have been disabled on this map."); SendOrderTo(newConn, "Message", "Bots have been disabled on this map.");
}
}; };
if (Type == ServerType.Local) if (Type == ServerType.Local)
@@ -675,6 +678,8 @@ namespace OpenRA.Server
} }
void InterpretServerOrder(Connection conn, Order o) void InterpretServerOrder(Connection conn, Order o)
{
lock (LobbyInfo)
{ {
// Only accept handshake responses from unvalidated clients // Only accept handshake responses from unvalidated clients
// Anything else may be an attempt to exploit the server // Anything else may be an attempt to exploit the server
@@ -858,6 +863,7 @@ namespace OpenRA.Server
} }
} }
} }
}
public Session.Client GetClient(Connection conn) public Session.Client GetClient(Connection conn)
{ {
@@ -865,6 +871,8 @@ namespace OpenRA.Server
} }
public void DropClient(Connection toDrop) public void DropClient(Connection toDrop)
{
lock (LobbyInfo)
{ {
if (!PreConns.Remove(toDrop)) if (!PreConns.Remove(toDrop))
{ {
@@ -915,6 +923,7 @@ namespace OpenRA.Server
if (Type != ServerType.Dedicated && dropClient.IsAdmin) if (Type != ServerType.Dedicated && dropClient.IsAdmin)
Shutdown(); Shutdown();
} }
}
try try
{ {
@@ -924,6 +933,8 @@ namespace OpenRA.Server
} }
public void SyncLobbyInfo() public void SyncLobbyInfo()
{
lock (LobbyInfo)
{ {
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, Order.FromTargetString("SyncInfo", LobbyInfo.Serialize(), true).Serialize()); DispatchOrders(null, 0, Order.FromTargetString("SyncInfo", LobbyInfo.Serialize(), true).Serialize());
@@ -931,12 +942,15 @@ namespace OpenRA.Server
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>()) foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
t.LobbyInfoSynced(this); t.LobbyInfoSynced(this);
} }
}
public void SyncLobbyClients() public void SyncLobbyClients()
{ {
if (State != ServerState.WaitingPlayers) if (State != ServerState.WaitingPlayers)
return; return;
lock (LobbyInfo)
{
// 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 = LobbyInfo.Clients.Select(client => client.Serialize()).ToList(); var clientData = LobbyInfo.Clients.Select(client => client.Serialize()).ToList();
@@ -945,12 +959,15 @@ namespace OpenRA.Server
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>()) foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
t.LobbyInfoSynced(this); t.LobbyInfoSynced(this);
} }
}
public void SyncLobbySlots() public void SyncLobbySlots()
{ {
if (State != ServerState.WaitingPlayers) if (State != ServerState.WaitingPlayers)
return; return;
lock (LobbyInfo)
{
// TODO: Don't sync all the slots if just one changed! // TODO: Don't sync all the slots if just one changed!
var slotData = LobbyInfo.Slots.Select(slot => slot.Value.Serialize()).ToList(); var slotData = LobbyInfo.Slots.Select(slot => slot.Value.Serialize()).ToList();
@@ -959,12 +976,15 @@ namespace OpenRA.Server
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>()) foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
t.LobbyInfoSynced(this); t.LobbyInfoSynced(this);
} }
}
public void SyncLobbyGlobalSettings() public void SyncLobbyGlobalSettings()
{ {
if (State != ServerState.WaitingPlayers) if (State != ServerState.WaitingPlayers)
return; return;
lock (LobbyInfo)
{
var sessionData = new List<MiniYamlNode> { LobbyInfo.GlobalSettings.Serialize() }; var sessionData = new List<MiniYamlNode> { LobbyInfo.GlobalSettings.Serialize() };
DispatchOrders(null, 0, Order.FromTargetString("SyncLobbyGlobalSettings", sessionData.WriteToString(), true).Serialize()); DispatchOrders(null, 0, Order.FromTargetString("SyncLobbyGlobalSettings", sessionData.WriteToString(), true).Serialize());
@@ -972,8 +992,11 @@ namespace OpenRA.Server
foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>()) foreach (var t in serverTraits.WithInterface<INotifySyncLobbyInfo>())
t.LobbyInfoSynced(this); t.LobbyInfoSynced(this);
} }
}
public void SyncClientPing() public void SyncClientPing()
{
lock (LobbyInfo)
{ {
// TODO: Split this further into per client ping orders // TODO: Split this further into per client ping orders
var clientPings = LobbyInfo.ClientPings.Select(ping => ping.Serialize()).ToList(); var clientPings = LobbyInfo.ClientPings.Select(ping => ping.Serialize()).ToList();
@@ -981,8 +1004,11 @@ namespace OpenRA.Server
// Note that syncing pings doesn't trigger INotifySyncLobbyInfo // Note that syncing pings doesn't trigger INotifySyncLobbyInfo
DispatchOrders(null, 0, Order.FromTargetString("SyncClientPings", clientPings.WriteToString(), true).Serialize()); DispatchOrders(null, 0, Order.FromTargetString("SyncClientPings", clientPings.WriteToString(), true).Serialize());
} }
}
public void StartGame() public void StartGame()
{
lock (LobbyInfo)
{ {
foreach (var listener in listeners) foreach (var listener in listeners)
listener.Stop(); listener.Stop();
@@ -1047,6 +1073,7 @@ namespace OpenRA.Server
}); });
} }
} }
}
public ConnectionTarget GetEndpointForLocalConnection() public ConnectionTarget GetEndpointForLocalConnection()
{ {

View File

@@ -48,6 +48,8 @@ namespace OpenRA.Mods.Common.Server
}; };
static bool ValidateSlotCommand(S server, Connection conn, Session.Client client, string arg, bool requiresHost) static bool ValidateSlotCommand(S server, Connection conn, Session.Client client, string arg, bool requiresHost)
{
lock (server.LobbyInfo)
{ {
if (!server.LobbyInfo.Slots.ContainsKey(arg)) if (!server.LobbyInfo.Slots.ContainsKey(arg))
{ {
@@ -63,8 +65,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
public static bool ValidateCommand(S server, Connection conn, Session.Client client, string cmd) public static bool ValidateCommand(S server, Connection conn, Session.Client client, string cmd)
{
lock (server.LobbyInfo)
{ {
// Kick command is always valid for the host // Kick command is always valid for the host
if (cmd.StartsWith("kick ")) if (cmd.StartsWith("kick "))
@@ -83,6 +88,7 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
public bool InterpretCommand(S server, Connection conn, Session.Client client, string cmd) public bool InterpretCommand(S server, Connection conn, Session.Client client, string cmd)
{ {
@@ -99,6 +105,8 @@ namespace OpenRA.Mods.Common.Server
} }
static void CheckAutoStart(S server) static void CheckAutoStart(S server)
{
lock (server.LobbyInfo)
{ {
var nonBotPlayers = server.LobbyInfo.NonBotPlayers; var nonBotPlayers = server.LobbyInfo.NonBotPlayers;
@@ -117,29 +125,31 @@ namespace OpenRA.Mods.Common.Server
server.StartGame(); server.StartGame();
} }
}
static bool State(S server, Connection conn, Session.Client client, string s) static bool State(S server, Connection conn, Session.Client client, string s)
{ {
var state = Session.ClientState.Invalid; lock (server.LobbyInfo)
if (!Enum<Session.ClientState>.TryParse(s, false, out state)) {
if (!Enum<Session.ClientState>.TryParse(s, false, out var state))
{ {
server.SendOrderTo(conn, "Message", "Malformed state command"); server.SendOrderTo(conn, "Message", "Malformed state command");
return true; return true;
} }
client.State = state; client.State = state;
Log.Write("server", "Player @{0} is {1}", conn.Socket.RemoteEndPoint, client.State);
Log.Write("server", "Player @{0} is {1}",
conn.Socket.RemoteEndPoint, client.State);
server.SyncLobbyClients(); server.SyncLobbyClients();
CheckAutoStart(server); CheckAutoStart(server);
return true; return true;
} }
}
static bool StartGame(S server, Connection conn, Session.Client client, string s) static bool StartGame(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!client.IsAdmin) if (!client.IsAdmin)
{ {
@@ -161,10 +171,14 @@ namespace OpenRA.Mods.Common.Server
} }
server.StartGame(); server.StartGame();
return true; return true;
} }
}
static bool Slot(S server, Connection conn, Session.Client client, string s) static bool Slot(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!server.LobbyInfo.Slots.ContainsKey(s)) if (!server.LobbyInfo.Slots.ContainsKey(s))
{ {
@@ -193,22 +207,27 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool AllowSpectators(S server, Connection conn, Session.Client client, string s) static bool AllowSpectators(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (bool.TryParse(s, out server.LobbyInfo.GlobalSettings.AllowSpectators)) if (bool.TryParse(s, out server.LobbyInfo.GlobalSettings.AllowSpectators))
{ {
server.SyncLobbyGlobalSettings(); server.SyncLobbyGlobalSettings();
return true; return true;
} }
else
{
server.SendOrderTo(conn, "Message", "Malformed allow_spectate command"); server.SendOrderTo(conn, "Message", "Malformed allow_spectate command");
return true; return true;
} }
} }
static bool Specate(S server, Connection conn, Session.Client client, string s) static bool Specate(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (server.LobbyInfo.GlobalSettings.AllowSpectators || client.IsAdmin) if (server.LobbyInfo.GlobalSettings.AllowSpectators || client.IsAdmin)
{ {
@@ -220,11 +239,14 @@ namespace OpenRA.Mods.Common.Server
CheckAutoStart(server); CheckAutoStart(server);
return true; return true;
} }
else
return false; return false;
} }
}
static bool SlotClose(S server, Connection conn, Session.Client client, string s) static bool SlotClose(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!ValidateSlotCommand(server, conn, client, s, true)) if (!ValidateSlotCommand(server, conn, client, s, true))
return false; return false;
@@ -260,8 +282,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool SlotOpen(S server, Connection conn, Session.Client client, string s) static bool SlotOpen(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!ValidateSlotCommand(server, conn, client, s, true)) if (!ValidateSlotCommand(server, conn, client, s, true))
return false; return false;
@@ -287,11 +312,13 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool SlotBot(S server, Connection conn, Session.Client client, string s) static bool SlotBot(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
var parts = s.Split(' '); var parts = s.Split(' ');
if (parts.Length < 3) if (parts.Length < 3)
{ {
server.SendOrderTo(conn, "Message", "Malformed slot_bot command"); server.SendOrderTo(conn, "Message", "Malformed slot_bot command");
@@ -366,8 +393,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool Map(S server, Connection conn, Session.Client client, string s) static bool Map(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!client.IsAdmin) if (!client.IsAdmin)
{ {
@@ -377,6 +407,8 @@ namespace OpenRA.Mods.Common.Server
var lastMap = server.LobbyInfo.GlobalSettings.Map; var lastMap = server.LobbyInfo.GlobalSettings.Map;
Action<MapPreview> selectMap = map => Action<MapPreview> selectMap = map =>
{
lock (server.LobbyInfo)
{ {
// Make sure the map hasn't changed in the meantime // Make sure the map hasn't changed in the meantime
if (server.LobbyInfo.GlobalSettings.Map != lastMap) if (server.LobbyInfo.GlobalSettings.Map != lastMap)
@@ -456,10 +488,10 @@ namespace OpenRA.Mods.Common.Server
var briefing = MissionBriefingOrDefault(server); var briefing = MissionBriefingOrDefault(server);
if (briefing != null) if (briefing != null)
server.SendMessage(briefing); server.SendMessage(briefing);
}
}; };
Action queryFailed = () => Action queryFailed = () => server.SendOrderTo(conn, "Message", "Map was not found on server.");
server.SendOrderTo(conn, "Message", "Map was not found on server.");
var m = server.ModData.MapCache[s]; var m = server.ModData.MapCache[s];
if (m.Status == MapStatus.Available || m.Status == MapStatus.DownloadAvailable) if (m.Status == MapStatus.Available || m.Status == MapStatus.DownloadAvailable)
@@ -475,8 +507,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool Option(S server, Connection conn, Session.Client client, string s) static bool Option(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!client.IsAdmin) if (!client.IsAdmin)
{ {
@@ -530,8 +565,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool AssignTeams(S server, Connection conn, Session.Client client, string s) static bool AssignTeams(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!client.IsAdmin) if (!client.IsAdmin)
{ {
@@ -570,8 +608,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool Kick(S server, Connection conn, Session.Client client, string s) static bool Kick(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!client.IsAdmin) if (!client.IsAdmin)
{ {
@@ -608,7 +649,6 @@ namespace OpenRA.Mods.Common.Server
server.DropClient(kickConn); server.DropClient(kickConn);
bool.TryParse(split[1], out var tempBan); bool.TryParse(split[1], out var tempBan);
if (tempBan) if (tempBan)
{ {
Log.Write("server", "Temporarily banning client {0} ({1}).", kickClientID, kickClient.IPAddress); Log.Write("server", "Temporarily banning client {0} ({1}).", kickClientID, kickClient.IPAddress);
@@ -621,8 +661,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool MakeAdmin(S server, Connection conn, Session.Client client, string s) static bool MakeAdmin(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!client.IsAdmin) if (!client.IsAdmin)
{ {
@@ -655,8 +698,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool MakeSpectator(S server, Connection conn, Session.Client client, string s) static bool MakeSpectator(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!client.IsAdmin) if (!client.IsAdmin)
{ {
@@ -686,8 +732,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool Name(S server, Connection conn, Session.Client client, string s) static bool Name(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
var sanitizedName = Settings.SanitizedPlayerName(s); var sanitizedName = Settings.SanitizedPlayerName(s);
if (sanitizedName == client.Name) if (sanitizedName == client.Name)
@@ -700,8 +749,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool Faction(S server, Connection conn, Session.Client client, string s) static bool Faction(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
var parts = s.Split(' '); var parts = s.Split(' ');
var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0])); var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0]));
@@ -729,8 +781,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool Team(S server, Connection conn, Session.Client client, string s) static bool Team(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
var parts = s.Split(' '); var parts = s.Split(' ');
var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0])); var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0]));
@@ -754,8 +809,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool Spawn(S server, Connection conn, Session.Client client, string s) static bool Spawn(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
var parts = s.Split(' '); var parts = s.Split(' ');
var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0])); var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0]));
@@ -806,8 +864,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool PlayerColor(S server, Connection conn, Session.Client client, string s) static bool PlayerColor(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
var parts = s.Split(' '); var parts = s.Split(' ');
var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0])); var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0]));
@@ -832,8 +893,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
static bool SyncLobby(S server, Connection conn, Session.Client client, string s) static bool SyncLobby(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{ {
if (!client.IsAdmin) if (!client.IsAdmin)
{ {
@@ -853,8 +917,11 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
}
public void ServerStarted(S server) public void ServerStarted(S server)
{
lock (server.LobbyInfo)
{ {
// Remote maps are not supported for the initial map // Remote maps are not supported for the initial map
var uid = server.LobbyInfo.GlobalSettings.Map; var uid = server.LobbyInfo.GlobalSettings.Map;
@@ -869,10 +936,13 @@ namespace OpenRA.Mods.Common.Server
LoadMapSettings(server, server.LobbyInfo.GlobalSettings, server.Map.Rules); LoadMapSettings(server, server.LobbyInfo.GlobalSettings, server.Map.Rules);
} }
}
static Session.Slot MakeSlotFromPlayerReference(PlayerReference pr) static Session.Slot MakeSlotFromPlayerReference(PlayerReference pr)
{ {
if (!pr.Playable) return null; if (!pr.Playable)
return null;
return new Session.Slot return new Session.Slot
{ {
PlayerReference = pr.Name, PlayerReference = pr.Name,
@@ -887,6 +957,8 @@ namespace OpenRA.Mods.Common.Server
} }
public static void LoadMapSettings(S server, Session.Global gs, Ruleset rules) public static void LoadMapSettings(S server, Session.Global gs, Ruleset rules)
{
lock (server.LobbyInfo)
{ {
var options = rules.Actors["player"].TraitInfos<ILobbyOptions>() var options = rules.Actors["player"].TraitInfos<ILobbyOptions>()
.Concat(rules.Actors["world"].TraitInfos<ILobbyOptions>()) .Concat(rules.Actors["world"].TraitInfos<ILobbyOptions>())
@@ -925,8 +997,11 @@ namespace OpenRA.Mods.Common.Server
} }
} }
} }
}
static Color SanitizePlayerColor(S server, Color askedColor, int playerIndex, Connection connectionToEcho = null) static Color SanitizePlayerColor(S server, Color askedColor, int playerIndex, Connection connectionToEcho = null)
{
lock (server.LobbyInfo)
{ {
var validator = server.ModData.Manifest.Get<ColorValidator>(); var validator = server.ModData.Manifest.Get<ColorValidator>();
var askColor = askedColor; var askColor = askedColor;
@@ -944,6 +1019,7 @@ namespace OpenRA.Mods.Common.Server
return validator.MakeValid(askColor, server.Random, terrainColors, playerColors, onError); return validator.MakeValid(askColor, server.Random, terrainColors, playerColors, onError);
} }
}
static string MissionBriefingOrDefault(S server) static string MissionBriefingOrDefault(S server)
{ {
@@ -955,6 +1031,8 @@ namespace OpenRA.Mods.Common.Server
} }
public void ClientJoined(S server, Connection conn) public void ClientJoined(S server, Connection conn)
{
lock (server.LobbyInfo)
{ {
var client = server.GetClient(conn); var client = server.GetClient(conn);
@@ -969,8 +1047,11 @@ namespace OpenRA.Mods.Common.Server
if (briefing != null) if (briefing != null)
server.SendOrderTo(conn, "Message", briefing); server.SendOrderTo(conn, "Message", briefing);
} }
}
void INotifyServerEmpty.ServerEmpty(S server) void INotifyServerEmpty.ServerEmpty(S server)
{
lock (server.LobbyInfo)
{ {
// Expire any temporary bans // Expire any temporary bans
server.TempBans.Clear(); server.TempBans.Clear();
@@ -984,6 +1065,7 @@ namespace OpenRA.Mods.Common.Server
.Where(ss => ss != null) .Where(ss => ss != null)
.ToDictionary(ss => ss.PlayerReference, ss => ss); .ToDictionary(ss => ss.PlayerReference, ss => ss);
} }
}
public static PlayerReference PlayerReferenceForSlot(S server, Session.Slot slot) public static PlayerReference PlayerReferenceForSlot(S server, Session.Slot slot)
{ {

View File

@@ -20,6 +20,8 @@ namespace OpenRA.Mods.Common.Server
public class LobbySettingsNotification : ServerTrait, IClientJoined public class LobbySettingsNotification : ServerTrait, IClientJoined
{ {
public void ClientJoined(OpenRA.Server.Server server, Connection conn) public void ClientJoined(OpenRA.Server.Server server, Connection conn)
{
lock (server.LobbyInfo)
{ {
if (server.LobbyInfo.ClientWithIndex(conn.PlayerIndex).IsAdmin) if (server.LobbyInfo.ClientWithIndex(conn.PlayerIndex).IsAdmin)
return; return;
@@ -43,4 +45,5 @@ namespace OpenRA.Mods.Common.Server
} }
} }
} }
}
} }

View File

@@ -36,7 +36,11 @@ namespace OpenRA.Mods.Common.Server
lastPing = Game.RunTime; lastPing = Game.RunTime;
// Ignore client timeout in singleplayer games to make debugging easier // Ignore client timeout in singleplayer games to make debugging easier
if (server.LobbyInfo.NonBotClients.Count() < 2 && server.Type != ServerType.Dedicated) var nonBotClientCount = 0;
lock (server.LobbyInfo)
nonBotClientCount = server.LobbyInfo.NonBotClients.Count();
if (nonBotClientCount < 2 && server.Type != ServerType.Dedicated)
foreach (var c in server.Conns.ToList()) foreach (var c in server.Conns.ToList())
server.SendOrderTo(c, "Ping", Game.RunTime.ToString()); server.SendOrderTo(c, "Ping", Game.RunTime.ToString());
else else