context: #region Copyright & License Information
  /*
   * Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation. For more information,
   * see COPYING.
   */
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.Drawing;
  using System.Linq;
  using OpenRA.Graphics;
  using OpenRA.Mods.Common.Traits;
  using OpenRA.Network;
  using OpenRA.Server;
  using S = OpenRA.Server.Server;
  
  namespace OpenRA.Mods.Common.Server
  {
  	public class LobbyCommands : ServerTrait, IInterpretCommand, INotifyServerStart
  	{
  		static bool ValidateSlotCommand(S server, Connection conn, Session.Client client, string arg, bool requiresHost)
  		{
  			if (!server.LobbyInfo.Slots.ContainsKey(arg))
  			{
  				Log.Write("server", "Invalid slot: {0}", arg);
  				return false;
  			}
  
  			if (requiresHost && !client.IsAdmin)
  			{
  				server.SendOrderTo(conn, "Message", "Only the host can do that.");
  				return false;
  			}
  
  			return true;
  		}
  
  		public static bool ValidateCommand(S server, Connection conn, Session.Client client, string cmd)
  		{
  			if (server.State == ServerState.GameStarted)
  			{
  				server.SendOrderTo(conn, "Message", "Cannot change state when game started. ({0})".F(cmd));
  				return false;
  			}
  			else if (client.State == Session.ClientState.Ready && !(cmd.StartsWith("state") || cmd == "startgame"))
  			{
  				server.SendOrderTo(conn, "Message", "Cannot change state when marked as ready.");
  				return false;
  			}
  
  			return true;
  		}
  
  		static void CheckAutoStart(S server)
  		{
  			var playerClients = server.LobbyInfo.Clients.Where(c => c.Bot == null && c.Slot != null);
  
  			// Are all players ready?
  			if (!playerClients.Any() || playerClients.Any(c => c.State != Session.ClientState.Ready))
  				return;
  
  			// Are the map conditions satisfied?
  			if (server.LobbyInfo.Slots.Any(sl => sl.Value.Required && server.LobbyInfo.ClientInSlot(sl.Key) == null))
  				return;
  
  			server.StartGame();
  		}
  
  		public bool InterpretCommand(S server, Connection conn, Session.Client client, string cmd)
  		{
  			if (server == null || conn == null || client == null || !ValidateCommand(server, conn, client, cmd))
  				return false;
  
  			var dict = new Dictionary<string, Func<string, bool>>
  			{
  				{ "state",
  					s =>
  					{
  						var state = Session.ClientState.Invalid;
  						if (!Enum<Session.ClientState>.TryParse(s, false, out state))
  						{
  							server.SendOrderTo(conn, "Message", "Malformed state command");
  							return true;
  						}
  
  						client.State = state;
  
  						Log.Write("server", "Player @{0} is {1}",
  							conn.Socket.RemoteEndPoint, client.State);
  
  						server.SyncLobbyClients();
  
  						CheckAutoStart(server);
  
  						return true;
  					}
  				},
  				{ "startgame",
  					s =>
  					{
  						if (!client.IsAdmin)
  						{
  							server.SendOrderTo(conn, "Message", "Only the host can start the game.");
  							return true;
  						}
  
  						if (server.LobbyInfo.Slots.Any(sl => sl.Value.Required &&
  							server.LobbyInfo.ClientInSlot(sl.Key) == null))
  						{
  							server.SendOrderTo(conn, "Message", "Unable to start the game until required slots are full.");
  							return true;
  						}
  
  						server.StartGame();
  						return true;
  					}
  				},
  				{ "slot",
  					s =>
  					{
  						if (!server.LobbyInfo.Slots.ContainsKey(s))
  						{
  							Log.Write("server", "Invalid slot: {0}", s);
  							return false;
  						}
  
  						var slot = server.LobbyInfo.Slots[s];
  
  						if (slot.Closed || server.LobbyInfo.ClientInSlot(s) != null)
  							return false;
  
  						client.Slot = s;
  						S.SyncClientToPlayerReference(client, server.MapPlayers.Players[s]);
  						server.SyncLobbyClients();
  						CheckAutoStart(server);
  
  						return true;
  					}
  				},
  				{ "allow_spectators",
  					s =>
  					{
  						if (bool.TryParse(s, out server.LobbyInfo.GlobalSettings.AllowSpectators))
  						{
  							server.SyncLobbyGlobalSettings();
  							return true;
  						}
  						else
  						{
  							server.SendOrderTo(conn, "Message", "Malformed allow_spectate command");
  							return true;
  						}
  					}
  				},
  				{ "spectate",
  					s =>
  					{
  						if (server.LobbyInfo.GlobalSettings.AllowSpectators || client.IsAdmin)
  						{
  							client.Slot = null;
  							client.SpawnPoint = 0;
  							client.Color = HSLColor.FromRGB(255, 255, 255);
  							server.SyncLobbyClients();
  							return true;
  						}
  						else
  							return false;
  					}
  				},
  				{ "slot_close",
  					s =>
  					{
  						if (!ValidateSlotCommand(server, conn, client, s, true))
  							return false;
  
  						// kick any player that's in the slot
  						var occupant = server.LobbyInfo.ClientInSlot(s);
  						if (occupant != null)
  						{
  							if (occupant.Bot != null)
  							{
  								server.LobbyInfo.Clients.Remove(occupant);
  								server.SyncLobbyClients();
  								var ping = server.LobbyInfo.PingFromClient(occupant);
  								if (ping != null)
  								{
  									server.LobbyInfo.ClientPings.Remove(ping);
  									server.SyncClientPing();
  								}
  							}
  							else
  							{
  								var occupantConn = server.Conns.FirstOrDefault(c => c.PlayerIndex == occupant.Index);
  								if (occupantConn != null)
  								{
  									server.SendOrderTo(occupantConn, "ServerError", "Your slot was closed by the host.");
  									server.DropClient(occupantConn);
  								}
  							}
  						}
  
  						server.LobbyInfo.Slots[s].Closed = true;
  						server.SyncLobbySlots();
  						return true;
  					}
  				},
  				{ "slot_open",
  					s =>
  					{
  						if (!ValidateSlotCommand(server, conn, client, s, true))
  							return false;
  
  						var slot = server.LobbyInfo.Slots[s];
  						slot.Closed = false;
  						server.SyncLobbySlots();
  
  						// Slot may have a bot in it
  						var occupant = server.LobbyInfo.ClientInSlot(s);
  						if (occupant != null && occupant.Bot != null)
  						{
  							server.LobbyInfo.Clients.Remove(occupant);
  							var ping = server.LobbyInfo.PingFromClient(occupant);
  							if (ping != null)
  							{
  								server.LobbyInfo.ClientPings.Remove(ping);
  								server.SyncClientPing();
  							}
  						}
  
  						server.SyncLobbyClients();
  						return true;
  					}
  				},
  				{ "slot_bot",
  					s =>
  					{
  						var parts = s.Split(' ');
  
  						if (parts.Length < 3)
  						{
  							server.SendOrderTo(conn, "Message", "Malformed slot_bot command");
  							return true;
  						}
  
  						if (!ValidateSlotCommand(server, conn, client, parts[0], true))
  							return false;
  
  						var slot = server.LobbyInfo.Slots[parts[0]];
  						var bot = server.LobbyInfo.ClientInSlot(parts[0]);
  						int controllerClientIndex;
  						if (!Exts.TryParseIntegerInvariant(parts[1], out controllerClientIndex))
  						{
  							Log.Write("server", "Invalid bot controller client index: {0}", parts[1]);
  							return false;
  						}
  
  						var botType = parts.Skip(2).JoinWith(" ");
  
  						// Invalid slot
  						if (bot != null && bot.Bot == null)
  						{
  							server.SendOrderTo(conn, "Message", "Can't add bots to a slot with another client.");
  							return true;
  						}
  
  						slot.Closed = false;
  						if (bot == null)
  						{
  							// Create a new bot
  							bot = new Session.Client()
  							{
  								Index = server.ChooseFreePlayerIndex(),
  								Name = botType,
  								Bot = botType,
  								Slot = parts[0],
  								Race = "Random",
  								SpawnPoint = 0,
  								Team = 0,
  								State = Session.ClientState.NotReady,
  								BotControllerClientIndex = controllerClientIndex
  							};
  
  							// Pick a random color for the bot
  							HSLColor botColor;
  							do
  							{
  								var hue = (byte)server.Random.Next(255);
  								var sat = (byte)server.Random.Next(255);
  								var lum = (byte)server.Random.Next(51, 255);
  								botColor = new HSLColor(hue, sat, lum);
  							} while (!ColorValidator.ValidatePlayerNewColor(server, botColor.RGB, bot.Index));
  
  							bot.Color = bot.PreferredColor = botColor;
  
  							server.LobbyInfo.Clients.Add(bot);
  						}
  						else
  						{
  							// Change the type of the existing bot
  							bot.Name = botType;
  							bot.Bot = botType;
  						}
  
  						S.SyncClientToPlayerReference(bot, server.MapPlayers.Players[parts[0]]);
  						server.SyncLobbyClients();
  						server.SyncLobbySlots();
  						return true;
  					}
  				},
  				{ "map",
  					s =>
  					{
  						if (!client.IsAdmin)
  						{
  							server.SendOrderTo(conn, "Message", "Only the host can change the map.");
  							return true;
  						}
  
  
gt: 						if (server.ModData.MapCache[s].Status != MapStatus.Available)