public void ReadData(Server server) { if (ReadDataInner(server)) while (data.Count >= ExpectLength) { var bytes = PopBytes(ExpectLength); switch (State) { case ReceiveState.Header: { ExpectLength = BitConverter.ToInt32(bytes, 0) - 4; Frame = BitConverter.ToInt32(bytes, 4); State = ReceiveState.Data; if (ExpectLength < 0 || ExpectLength > MaxOrderLength) { server.DropClient(this); Log.Write("server", "Dropping client {0} for excessive order length = {1}", PlayerIndex, ExpectLength); return; } } break; case ReceiveState.Data: { server.DispatchOrders(this, Frame, bytes); MostRecentFrame = Frame; ExpectLength = 8; State = ReceiveState.Header; } break; } } }
public void ReadData( Server server ) { if (ReadDataInner(server)) while (data.Count >= ExpectLength) { var bytes = PopBytes(ExpectLength); switch (State) { case ReceiveState.Header: { ExpectLength = BitConverter.ToInt32(bytes, 0) - 4; Frame = BitConverter.ToInt32(bytes, 4); State = ReceiveState.Data; } break; case ReceiveState.Data: { server.DispatchOrders(this, Frame, bytes); MostRecentFrame = Frame; ExpectLength = 8; State = ReceiveState.Header; server.UpdateInFlightFrames(this); } break; } } }
public void ClientJoined(S server, Connection newConn) { var defaults = new GameRules.PlayerSettings(); var client = new Session.Client() { Index = newConn.PlayerIndex, Color1 = defaults.Color1, Color2 = defaults.Color2, Name = defaults.Name, Country = "random", State = Session.ClientState.NotReady, SpawnPoint = 0, Team = 0, Slot = ChooseFreeSlot(server), }; var slotData = server.lobbyInfo.Slots.FirstOrDefault( x => x.Index == client.Slot ); if (slotData != null) SyncClientToPlayerReference(client, server.Map.Players[slotData.MapPlayer]); server.lobbyInfo.Clients.Add(client); Log.Write("server", "Client {0}: Accepted connection from {1}", newConn.PlayerIndex, newConn.socket.RemoteEndPoint); server.SendChat(newConn, "has joined the game."); server.SyncLobbyInfo(); }
bool ReadDataInner( Server server ) { var rx = new byte[1024]; var len = 0; for (; ; ) { try { // NOTE(jsd): Poll the socket first to see if there's anything there. // This avoids the exception with SocketErrorCode == `SocketError.WouldBlock` thrown // from `socket.Receive(rx)`. if (!socket.Poll(0, SelectMode.SelectRead)) break; if (0 < (len = socket.Receive(rx))) data.AddRange(rx.Take(len)); else { if (len == 0) server.DropClient(this); break; } } catch (SocketException e) { // NOTE(jsd): This should no longer be needed with the socket.Poll call above. if (e.SocketErrorCode == SocketError.WouldBlock) break; server.DropClient(this); return false; } } return true; }
bool ReadDataInner( Server server ) { var rx = new byte[1024]; var len = 0; for (; ; ) { try { if (0 < (len = socket.Receive(rx))) data.AddRange(rx.Take(len)); else { if (len == 0) server.DropClient(this); break; } } catch (SocketException e) { if (e.SocketErrorCode == SocketError.WouldBlock) break; server.DropClient(this); return false; } } return true; }
public void ClientJoined(S server, Connection conn) { var client = server.GetClient(conn); // Validate whether color is allowed and get an alternative if it isn't if (client.Slot == null || !server.LobbyInfo.Slots[client.Slot].LockColor) client.Color = SanitizePlayerColor(server, client.Color, client.Index); }
public void Tick(S server) { if ((Game.RunTime - lastPing > MasterPingInterval * 1000) || isInitialPing) PingMasterServer(server); else lock (masterServerMessages) while (masterServerMessages.Count > 0) server.SendMessage(masterServerMessages.Dequeue()); }
public void Tick(S server) { if ((Environment.TickCount - lastPing > PingInterval) || isInitialPing) { isInitialPing = false; lastPing = Environment.TickCount; foreach (var p in server.Conns) server.SendOrderTo(p, "Ping", Environment.TickCount.ToString()); } }
public void PingMasterServer(S server) { if (isBusy || !server.Settings.AdvertiseOnline) return; lastPing = Environment.TickCount; isBusy = true; var mod = server.ModData.Manifest.Mod; // important to grab these on the main server thread, not in the worker we're about to spawn -- they may be modified // by the main thread as clients join and leave. var numPlayers = server.LobbyInfo.Clients.Where(c1 => c1.Bot == null).Count(); var numBots = server.LobbyInfo.Clients.Where(c1 => c1.Bot != null).Count(); Action a = () => { try { var url = "ping.php?port={0}&name={1}&state={2}&players={3}&bots={4}&mods={5}&map={6}&maxplayers={7}"; if (isInitialPing) url += "&new=1"; using (var wc = new WebClient()) { wc.Proxy = null; wc.DownloadData( server.Settings.MasterServer + url.F( server.Settings.ExternalPort, Uri.EscapeUriString(server.Settings.Name), (int)server.State, numPlayers, numBots, "{0}@{1}".F(mod.Id, mod.Version), server.LobbyInfo.GlobalSettings.Map, server.Map.PlayerCount)); if (isInitialPing) { isInitialPing = false; lock (masterServerMessages) masterServerMessages.Enqueue("Master server communication established."); } } } catch (Exception ex) { Log.Write("server", ex.ToString()); lock (masterServerMessages) masterServerMessages.Enqueue("Master server communication failed."); } isBusy = false; }; a.BeginInvoke(null, null); }
public static ConnectionTarget CreateServer(ServerSettings settings) { var endpoints = new List <IPEndPoint> { new IPEndPoint(IPAddress.IPv6Any, settings.ListenPort), new IPEndPoint(IPAddress.Any, settings.ListenPort) }; server = new Server.Server(endpoints, settings, ModData, ServerType.Multiplayer); return(server.GetEndpointForLocalConnection()); }
public static int CreateLocalCampaignServer(string map, string campaignName, int campaignLevel) { var settings = new ServerSettings() { Name = campaignName + campaignLevel.ToString(), Map = map, AdvertiseOnline = false }; server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), settings, ModData, false); return(server.Port); }
public static int CreateLocalServer(string map) { var settings = new ServerSettings() { Name = "Skirmish Game", Map = map, AdvertiseOnline = false }; server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), settings, ModData, false); return(server.Port); }
static void Main(string[] args) { var arguments = new Arguments(args); Log.AddChannel("debug", "dedicated-debug.log"); Log.AddChannel("perf", "dedicated-perf.log"); Log.AddChannel("server", "dedicated-server.log"); Log.AddChannel("nat", "dedicated-nat.log"); // Special case handling of Game.Mod argument: if it matches a real filesystem path // then we use this to override the mod search path, and replace it with the mod id var modArgument = arguments.GetValue("Game.Mod", null); string customModPath = null; if (modArgument != null && (File.Exists(modArgument) || Directory.Exists(modArgument))) { customModPath = modArgument; arguments.ReplaceValue("Game.Mod", Path.GetFileNameWithoutExtension(modArgument)); } // HACK: The engine code assumes that Game.Settings is set. // This isn't nearly as bad as ModData, but is still not very nice. Game.InitializeSettings(arguments); var settings = Game.Settings.Server; var mod = Game.Settings.Game.Mod; var mods = new InstalledMods(customModPath); // HACK: The engine code *still* assumes that Game.ModData is set var modData = Game.ModData = new ModData(mods[mod], mods); modData.MapCache.LoadMaps(); settings.Map = modData.MapCache.ChooseInitialMap(settings.Map, new MersenneTwister()); Console.WriteLine("[{0}] Starting dedicated server for mod: {1}", DateTime.Now.ToString(settings.TimestampFormat), mod); while (true) { var server = new Server(new IPEndPoint(IPAddress.Any, settings.ListenPort), settings, modData, true); while (true) { Thread.Sleep(1000); if (server.State == ServerState.GameStarted && server.Conns.Count < 1) { Console.WriteLine("[{0}] No one is playing, shutting down...", DateTime.Now.ToString(settings.TimestampFormat)); server.Shutdown(); break; } } Console.WriteLine("[{0}] Starting a new server instance...", DateTime.Now.ToString(settings.TimestampFormat)); } }
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 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; }
public static bool ValidateCommand(S server, Connection conn, Session.Client client, string cmd) { if (server.GameStarted) { server.SendChatTo(conn, "Cannot change state when game started. ({0})".F(cmd)); return false; } else if (client.State == Session.ClientState.Ready && !(cmd == "ready" || cmd == "normalstart")) { server.SendChatTo(conn, "Cannot change state when marked as ready."); return false; } return true; }
public void PingMasterServer(S server) { if (isBusy || !server.Settings.AdvertiseOnline) return; lastPing = Environment.TickCount; isBusy = true; Action a = () => { try { var url = "ping.php?port={0}&name={1}&state={2}&players={3}&bots={4}&mods={5}&map={6}&maxplayers={7}"; if (isInitialPing) url += "&new=1"; using (var wc = new WebClient()) { wc.Proxy = null; wc.DownloadData( server.Settings.MasterServer + url.F( server.Settings.ExternalPort, Uri.EscapeUriString(server.Settings.Name), (int) server.State, server.lobbyInfo.Clients.Where(c1 => c1.Bot == null).Count(), server.lobbyInfo.Clients.Where(c1 => c1.Bot != null).Count(), Game.CurrentMods.Select(f => "{0}@{1}".F(f.Key, f.Value.Version)).JoinWith(","), server.lobbyInfo.GlobalSettings.Map, server.Map.PlayerCount)); if (isInitialPing) { isInitialPing = false; lock (masterServerMessages) masterServerMessages.Enqueue("Master server communication established."); } } } catch(Exception ex) { Log.Write("server", ex.ToString()); lock( masterServerMessages ) masterServerMessages.Enqueue( "Master server communication failed." ); } isBusy = false; }; a.BeginInvoke(null, null); }
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 int CreateLocalServer(string map) { var settings = new ServerSettings() { Name = "Skirmish Game", Map = map }; // Work around a miscompile in mono 2.6.7: // booleans that default to true cannot be set false by an initializer settings.AdvertiseOnline = false; server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), Game.Settings.Game.Mods, settings, modData); return(server.Port); }
public void PingMasterServer(S server) { if (isBusy || !isInternetServer) return; lastPing = Environment.TickCount; isBusy = true; Action a = () => { try { var url = "ping.php?port={0}&name={1}&state={2}&players={3}&mods={4}&map={5}&maxplayers={6}"; if (isInitialPing) url += "&new=1"; using (var wc = new WebClient()) { wc.Proxy = null; wc.DownloadData( masterServerUrl + url.F( externalPort, Uri.EscapeUriString(server.Name), server.GameStarted ? 2 : 1, // todo: post-game states, etc. server.lobbyInfo.Clients.Count, string.Join(",", Game.CurrentMods.Select(f => "{0}@{1}".F(f.Key, f.Value.Version)).ToArray()), server.lobbyInfo.GlobalSettings.Map, server.lobbyInfo.Slots.Count( s => !s.Spectator ))); if (isInitialPing) { isInitialPing = false; lock (masterServerMessages) masterServerMessages.Enqueue("Master server communication established."); } } } catch(Exception ex) { Log.Write("server", ex.ToString()); lock( masterServerMessages ) masterServerMessages.Enqueue( "Master server communication failed." ); } isBusy = false; }; a.BeginInvoke(null, null); }
public static int MaxSpectators = 4; // How many spectators to allow // @todo Expose this as an option #endregion Fields #region Methods public static void LoadMap(S server) { server.Map = new Map(server.ModData.AvailableMaps[server.lobbyInfo.GlobalSettings.Map]); server.lobbyInfo.Slots = server.Map.Players .Select(p => MakeSlotFromPlayerReference(p.Value)) .Where(s => s != null) .Select((s, i) => { s.Index = i; return s; }) .ToList(); // Generate slots for spectators for (int i = 0; i < MaxSpectators; i++) server.lobbyInfo.Slots.Add(new Session.Slot { Spectator = true, Index = server.lobbyInfo.Slots.Count(), MapPlayer = null, Bot = null }); }
public static ConnectionTarget CreateLocalServer(string map) { var settings = new ServerSettings() { Name = "Skirmish Game", Map = map, AdvertiseOnline = false }; var endpoints = new List <IPEndPoint> { new IPEndPoint(IPAddress.IPv6Loopback, 0), new IPEndPoint(IPAddress.Loopback, 0) }; server = new Server.Server(endpoints, settings, ModData, ServerType.Local); return(server.GetEndpointForLocalConnection()); }
static bool SlotClose(S server, Connection conn, Session.Client client, string s) { lock (server.LobbyInfo) { 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); } }
public static void LoadMapSettings(S server, Session.Global gs, MapPreview map) { lock (server.LobbyInfo) { var options = map.PlayerActorInfo.TraitInfos <ILobbyOptions>() .Concat(map.WorldActorInfo.TraitInfos <ILobbyOptions>()) .SelectMany(t => t.LobbyOptions(map)); foreach (var o in options) { var value = o.DefaultValue; var preferredValue = o.DefaultValue; if (gs.LobbyOptions.TryGetValue(o.Id, out var state)) { // Propagate old state on map change if (!o.IsLocked) { if (o.Values.Keys.Contains(state.PreferredValue)) { value = state.PreferredValue; } else if (o.Values.Keys.Contains(state.Value)) { value = state.Value; } } preferredValue = state.PreferredValue; } else { state = new Session.LobbyOptionState(); } state.IsLocked = o.IsLocked; state.Value = value; state.PreferredValue = preferredValue; gs.LobbyOptions[o.Id] = state; } } }
public static ConnectionTarget CreateLocalServer(string map) { var settings = new ServerSettings() { Name = "Skirmish Game", Map = map, AdvertiseOnline = false }; // Always connect to local games using the same loopback connection // Exposing multiple endpoints introduces a race condition on the client's PlayerIndex (sometimes 0, sometimes 1) // This would break the Restart button, which relies on the PlayerIndex always being the same for local servers var endpoints = new List <IPEndPoint> { new IPEndPoint(IPAddress.Loopback, 0) }; server = new Server.Server(endpoints, settings, ModData, ServerType.Local); return(server.GetEndpointForLocalConnection()); }
public static bool ValidateCommand(S server, Connection conn, Session.Client client, string cmd) { // Kick command is always valid for the host if (cmd.StartsWith("kick ")) { return(true); } 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 bool State(S server, Connection conn, Session.Client client, string 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); }
public static void LoadMapSettings(S server, Session.Global gs, Ruleset rules) { var options = rules.Actors["player"].TraitInfos<ILobbyOptions>() .Concat(rules.Actors["world"].TraitInfos<ILobbyOptions>()) .SelectMany(t => t.LobbyOptions(rules)); foreach (var o in options) { var value = o.DefaultValue; var preferredValue = o.DefaultValue; Session.LobbyOptionState state; if (gs.LobbyOptions.TryGetValue(o.Id, out state)) { // Propagate old state on map change if (!o.Locked) { if (o.Values.Keys.Contains(state.PreferredValue)) value = state.PreferredValue; else if (o.Values.Keys.Contains(state.Value)) value = state.Value; } preferredValue = state.PreferredValue; } else state = new Session.LobbyOptionState(); state.Locked = o.Locked; state.Value = value; state.PreferredValue = preferredValue; gs.LobbyOptions[o.Id] = state; if (o.Id == "gamespeed") { var speed = server.ModData.Manifest.Get<GameSpeeds>().Speeds[value]; gs.Timestep = speed.Timestep; gs.OrderLatency = speed.OrderLatency; } } }
public void Tick(S server) { if ((Game.RunTime - lastPing > PingInterval) || isInitialPing) { isInitialPing = false; lastPing = Game.RunTime; foreach (var c in server.Conns.ToList()) { if (c.TimeSinceLastResponse < ConnTimeout) { server.SendOrderTo(c, "Ping", Game.RunTime.ToString()); if (!c.TimeoutMessageShown && c.TimeSinceLastResponse > PingInterval * 2) { server.SendMessage(server.GetClient(c).Name + " is experiencing connection problems."); c.TimeoutMessageShown = true; } } else { server.SendMessage(server.GetClient(c).Name + " has been dropped after timing out."); server.DropClient(c, -1); } } } if (Game.RunTime - lastConnReport > ConnReportInterval) { lastConnReport = Game.RunTime; var timeouts = server.Conns .Where(c => c.TimeSinceLastResponse > ConnReportInterval && c.TimeSinceLastResponse < ConnTimeout) .OrderBy(c => c.TimeSinceLastResponse); foreach (var c in timeouts) { server.SendMessage("{0} will be dropped in {1} seconds.".F( server.GetClient(c).Name, (ConnTimeout - c.TimeSinceLastResponse) / 1000)); } } }
bool ReadDataInner(Server server) { var rx = new byte[1024]; var len = 0; for (;;) { try { // NOTE(jsd): Poll the socket first to see if there's anything there. // This avoids the exception with SocketErrorCode == `SocketError.WouldBlock` thrown // from `socket.Receive(rx)`. if (!Socket.Poll(0, SelectMode.SelectRead)) break; if (0 < (len = Socket.Receive(rx))) Data.AddRange(rx.Take(len)); else { if (len == 0) server.DropClient(this); break; } } catch (SocketException e) { // NOTE(jsd): This should no longer be needed with the socket.Poll call above. if (e.SocketErrorCode == SocketError.WouldBlock) break; server.DropClient(this); Log.Write("server", "Dropping client {0} because reading the data failed: {1}", PlayerIndex, e); return false; } } lastReceivedTime = Game.RunTime; TimeoutMessageShown = false; return true; }
static bool MakeAdmin(S server, Connection conn, Session.Client client, string s) { if (!client.IsAdmin) { server.SendOrderTo(conn, "Message", "Only admins can transfer admin to another player."); return(true); } int newAdminId; Exts.TryParseIntegerInvariant(s, out newAdminId); var newAdminConn = server.Conns.SingleOrDefault(c => server.GetClient(c) != null && server.GetClient(c).Index == newAdminId); if (newAdminConn == null) { server.SendOrderTo(conn, "Message", "No-one in that slot."); return(true); } var newAdminClient = server.GetClient(newAdminConn); client.IsAdmin = false; newAdminClient.IsAdmin = true; var bots = server.LobbyInfo.Slots .Select(slot => server.LobbyInfo.ClientInSlot(slot.Key)) .Where(c => c != null && c.Bot != null); foreach (var b in bots) { b.BotControllerClientIndex = newAdminId; } server.SendMessage("{0} is now the admin.".F(newAdminClient.Name)); Log.Write("server", "{0} is now the admin.".F(newAdminClient.Name)); server.SyncLobbyClients(); return(true); }
static Color SanitizePlayerColor(S server, Color askedColor, int playerIndex, Connection connectionToEcho = null) { lock (server.LobbyInfo) { var validator = server.ModData.Manifest.Get <ColorValidator>(); var askColor = askedColor; Action <string> onError = message => { if (connectionToEcho != null) { server.SendOrderTo(connectionToEcho, "Message", message); } }; var terrainColors = server.Map.Rules.TerrainInfo.RestrictedPlayerColors; var playerColors = server.LobbyInfo.Clients.Where(c => c.Index != playerIndex).Select(c => c.Color) .Concat(server.Map.Players.Players.Values.Select(p => p.Color)).ToList(); return(validator.MakeValid(askColor, server.Random, terrainColors, playerColors, onError)); } }
static bool SyncLobby(S server, Connection conn, Session.Client client, string s) { if (!client.IsAdmin) { server.SendOrderTo(conn, "Message", "Only the host can set lobby info"); return(true); } var lobbyInfo = Session.Deserialize(s); if (lobbyInfo == null) { server.SendOrderTo(conn, "Message", "Invalid Lobby Info Sent"); return(true); } server.LobbyInfo = lobbyInfo; server.SyncLobbyInfo(); return(true); }
void UpdateLANGameBeacon(S server, int numPlayers, int numSlots, int numBots, int numSpectators, Manifest mod, bool passwordProtected) { var settings = server.Settings; // TODO: Serialize and send client names var lanGameYaml = @"Game: Id: {0} Name: {1} Address: {2}:{3} State: {4} Players: {5} MaxPlayers: {6} Bots: {7} Spectators: {8} Map: {9} Mods: {10}@{11} Protected: {12}" .F(Platform.SessionGUID, settings.Name, server.Ip, settings.ListenPort, (int)server.State, numPlayers, numSlots, numBots, numSpectators, server.Map.Uid, mod.Id, mod.Metadata.Version, passwordProtected); LanGameBeacon.BeaconData = lanGameYaml; }
static Color SanitizePlayerColor(S server, Color askedColor, int playerIndex, Connection connectionToEcho = null) { lock (server.LobbyInfo) { var colorManager = server.ModData.DefaultRules.Actors[SystemActors.World].TraitInfo <ColorPickerManagerInfo>(); var askColor = askedColor; Action <string> onError = message => { if (connectionToEcho != null) { server.SendOrderTo(connectionToEcho, "Message", message); } }; var terrainColors = server.ModData.DefaultTerrainInfo[server.Map.TileSet].RestrictedPlayerColors; var playerColors = server.LobbyInfo.Clients.Where(c => c.Index != playerIndex).Select(c => c.Color) .Concat(server.Map.Players.Players.Values.Select(p => p.Color)).ToList(); return(colorManager.MakeValid(askColor, server.Random, terrainColors, playerColors, onError)); } }
static bool Handicap(S server, Connection conn, Session.Client client, string s) { lock (server.LobbyInfo) { var parts = s.Split(' '); var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0])); // Only the host can change other client's info if (targetClient.Index != client.Index && !client.IsAdmin) { return(true); } // Map has disabled handicap changes if (server.LobbyInfo.Slots[targetClient.Slot].LockHandicap) { return(true); } if (!Exts.TryParseIntegerInvariant(parts[1], out var handicap)) { Log.Write("server", "Invalid handicap: {0}", s); return(false); } // Handicaps may be set between 0 - 95% in steps of 5% var options = Enumerable.Range(0, 20).Select(i => 5 * i); if (!options.Contains(handicap)) { Log.Write("server", "Invalid handicap: {0}", s); return(false); } targetClient.Handicap = handicap; server.SyncLobbyClients(); return(true); } }
public void ClientJoined(S server, Connection conn) { lock (server.LobbyInfo) { var client = server.GetClient(conn); // Validate whether color is allowed and get an alternative if it isn't if (client.Slot != null && !server.LobbyInfo.Slots[client.Slot].LockColor) { client.Color = SanitizePlayerColor(server, client.Color, client.Index); } // Report any custom map details // HACK: this isn't the best place for this to live, but if we move it somewhere else // then we need a larger hack to hook the map change event. var briefing = MissionBriefingOrDefault(server); if (briefing != null) { server.SendOrderTo(conn, "Message", briefing); } } }
static bool Slot(S server, Connection conn, Session.Client client, string s) { lock (server.LobbyInfo) { 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); } // If the previous slot had a locked spawn then we must not carry that to the new slot var oldSlot = client.Slot != null ? server.LobbyInfo.Slots[client.Slot] : null; if (oldSlot != null && oldSlot.LockSpawn) { client.SpawnPoint = 0; } client.Slot = s; S.SyncClientToPlayerReference(client, server.Map.Players.Players[s]); if (!slot.LockColor) { client.PreferredColor = client.Color = SanitizePlayerColor(server, client.Color, client.Index, conn); } server.SyncLobbyClients(); CheckAutoStart(server); return(true); } }
public void Tick(S server) { if ((Game.RunTime - lastPing > PingInterval) || isInitialPing) { isInitialPing = false; lastPing = Game.RunTime; foreach (var c in server.Conns.ToList()) { if (c.TimeSinceLastResponse < ConnTimeout) { server.SendOrderTo(c, "Ping", Game.RunTime.ToString()); if (!c.TimeoutMessageShown && c.TimeSinceLastResponse > PingInterval * 2) { server.SendMessage(server.GetClient(c).Name + " is experiencing connection problems."); c.TimeoutMessageShown = true; } } else { server.SendMessage(server.GetClient(c).Name + " has been dropped after timing out."); server.DropClient(c, -1); } } } if (Game.RunTime - lastConnReport > ConnReportInterval) { lastConnReport = Game.RunTime; var timeouts = server.Conns .Where(c => c.TimeSinceLastResponse > ConnReportInterval && c.TimeSinceLastResponse < ConnTimeout) .OrderBy(c => c.TimeSinceLastResponse); foreach (var c in timeouts) server.SendMessage("{0} will be dropped in {1} seconds.".F( server.GetClient(c).Name, (ConnTimeout - c.TimeSinceLastResponse) / 1000)); } }
static bool SyncLobby(S server, Connection conn, Session.Client client, string s) { lock (server.LobbyInfo) { if (!client.IsAdmin) { server.SendOrderTo(conn, "Message", "Only the host can set lobby info"); return(true); } try { server.LobbyInfo = Session.Deserialize(s); server.SyncLobbyInfo(); } catch (Exception) { server.SendOrderTo(conn, "Message", "Invalid Lobby Info Sent"); } return(true); } }
static void Main(string[] args) { Log.AddChannel("debug", "dedicated-debug.log"); Log.AddChannel("perf", "dedicated-perf.log"); Log.AddChannel("server", "dedicated-server.log"); // HACK: The engine code assumes that Game.Settings is set. // This isn't nearly as bad as ModData, but is still not very nice. Game.InitializeSettings(new Arguments(args)); var settings = Game.Settings.Server; // HACK: The engine code *still* assumes that Game.ModData is set var mod = Game.Settings.Game.Mod; var modData = Game.ModData = new ModData(mod, false); modData.MapCache.LoadMaps(); settings.Map = modData.MapCache.ChooseInitialMap(settings.Map, new MersenneTwister()); Console.WriteLine("[{0}] Starting dedicated server for mod: {1}", DateTime.Now.ToString(settings.TimestampFormat), mod); while (true) { var server = new Server(new IPEndPoint(IPAddress.Any, settings.ListenPort), settings, modData, true); while (true) { Thread.Sleep(1000); if (server.State == ServerState.GameStarted && server.Conns.Count < 1) { Console.WriteLine("[{0}] No one is playing, shutting down...", DateTime.Now.ToString(settings.TimestampFormat)); server.Shutdown(); break; } } Console.WriteLine("[{0}] Starting a new server instance...", DateTime.Now.ToString(settings.TimestampFormat)); } }
public void ClientJoined(OpenRA.Server.Server server, Connection conn) { lock (server.LobbyInfo) { var defaults = new Session.Global(); LobbyCommands.LoadMapSettings(server, defaults, server.Map.Rules); var options = server.Map.Rules.Actors[SystemActors.Player].TraitInfos <ILobbyOptions>() .Concat(server.Map.Rules.Actors[SystemActors.World].TraitInfos <ILobbyOptions>()) .SelectMany(t => t.LobbyOptions(server.Map.Rules)) .ToDictionary(o => o.Id, o => o); foreach (var kv in server.LobbyInfo.GlobalSettings.LobbyOptions) { if (!defaults.LobbyOptions.TryGetValue(kv.Key, out var def) || kv.Value.Value != def.Value) { if (options.TryGetValue(kv.Key, out var option)) { server.SendOrderTo(conn, "Message", option.Name + ": " + option.Values[kv.Value.Value]); } } } } }
static bool StartGame(S server, Connection conn, Session.Client client, string 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); } if (!server.LobbyInfo.GlobalSettings.EnableSingleplayer && server.LobbyInfo.NonBotPlayers.Count() < 2) { server.SendOrderTo(conn, "Message", server.TwoHumansRequiredText); return(true); } server.StartGame(); return(true); }
public void GameEnded(S server) { PingMasterServer(server); }
public void Tick(S server) { if ((Environment.TickCount - lastPing > MasterPingInterval * 1000) || isInitialPing) PingMasterServer(server); else lock (masterServerMessages) while (masterServerMessages.Count > 0) server.SendChat(null, masterServerMessages.Dequeue()); }
public void PingMasterServer(S server) { if (isBusy || !server.Settings.AdvertiseOnline) { return; } lastPing = Game.RunTime; isBusy = true; var mod = server.ModData.Manifest.Mod; // important to grab these on the main server thread, not in the worker we're about to spawn -- they may be modified // by the main thread as clients join and leave. var numPlayers = server.LobbyInfo.Clients.Where(c1 => c1.Bot == null && c1.Slot != null).Count(); var numBots = server.LobbyInfo.Clients.Where(c1 => c1.Bot != null).Count(); var numSpectators = server.LobbyInfo.Clients.Where(c1 => c1.Bot == null && c1.Slot == null).Count(); var numSlots = server.LobbyInfo.Slots.Where(s => !s.Value.Closed).Count() - numBots; var passwordProtected = string.IsNullOrEmpty(server.Settings.Password) ? 0 : 1; var clients = server.LobbyInfo.Clients.Where(c1 => c1.Bot == null).Select(c => Convert.ToBase64String(Encoding.UTF8.GetBytes(c.Name))).ToArray(); Action a = () => { try { var url = "ping?port={0}&name={1}&state={2}&players={3}&bots={4}&mods={5}&map={6}&maxplayers={7}&spectators={8}&protected={9}&clients={10}"; if (isInitialPing) { url += "&new=1"; } using (var wc = new WebClient()) { wc.Proxy = null; var masterResponse = wc.DownloadData( server.Settings.MasterServer + url.F( server.Settings.ExternalPort, Uri.EscapeUriString(server.Settings.Name), (int)server.State, numPlayers, numBots, "{0}@{1}".F(mod.Id, mod.Version), server.LobbyInfo.GlobalSettings.Map, numSlots, numSpectators, passwordProtected, string.Join(",", clients))); if (isInitialPing) { var masterResponseText = Encoding.UTF8.GetString(masterResponse); isInitialPing = false; lock (masterServerMessages) { masterServerMessages.Enqueue("Master server communication established."); if (masterResponseText.Contains("[001]")) // Server does not respond code { Log.Write("server", masterResponseText); masterServerMessages.Enqueue("Warning: Server ports are not forwarded."); masterServerMessages.Enqueue("Game has not been advertised online."); } } } } } catch (Exception ex) { Log.Write("server", ex.ToString()); lock (masterServerMessages) masterServerMessages.Enqueue("Master server communication failed."); } isBusy = false; }; a.BeginInvoke(null, null); }
public void GameEnded(S server) { PingMasterServer(server); }
public void GameStarted(S server) { PingMasterServer(server); }
public void ServerShutdown(Server server) { Console.WriteLine("ServerShutdown()"); }
public static void CreateServer(ServerSettings settings) { server = new Server.Server(new IPEndPoint(IPAddress.Any, settings.ListenPort), settings, ModData, false); }
public void Tick(S server) { if (queue.TryDequeue(out var line)) { // Do your line processing logic here, on the server thread. Console.WriteLine("You typed: {0}", line); if (line == "exit") { // Console.WriteLine("OK, shutting down!"); Console.WriteLine("This command is disabled"); // server.Shutdown(); } if (line == "ads") { var adFile = Path.Combine(Platform.SupportDir, "mrads.txt"); if (!File.Exists(adFile)) { File.WriteAllText(adFile, "[MR] Welcome to our server. Good luck, have fun!"); } if (File.Exists(adFile)) { var xlines = File.ReadAllLines(adFile); var lineStr = xlines.Random(server.Random); server.SendMessage(lineStr); } } if (line == "testmsg") { var testmsg = string.Format("Hello this is a message from the console!"); Console.WriteLine("Sending Test Message to game"); server.SendMessage(testmsg); } if (line == "help") { Console.WriteLine("Available commands: say, psay, status, kick, tban, admin, deladmin, ads, help. Type help commandName for more information."); } if (line.StartsWith("say ")) { var smsg = line.Remove(0, 4); string.Format(smsg); Console.WriteLine(smsg); server.SendMessage("[MR] {0}".F(smsg)); } if (line == "help psay") { Console.WriteLine("Syntax: psay userID 'message here'"); Console.WriteLine("WARNING: Private messages are unencrypted, and may be saved to replay files. Use with caution"); } if (line.StartsWith("psay ") && line.Length > 7) { var smsg = line.Remove(0, 5); int index = smsg.IndexOf('\''); if (index >= 0) { // Console.WriteLine("Index was greater than 0"); int msgUID = Int32.Parse(smsg.Substring(0, index)); var msgConn = server.Conns.SingleOrDefault(c => server.GetClient(c) != null && server.GetClient(c).Index == msgUID); if (msgConn != null) { // Console.WriteLine("msgConn was not null"); smsg = smsg.Remove(0, index - 1); Match match = Regex.Match(smsg, @"'([^']*)"); if (match.Success) { var msgClient = server.GetClient(msgConn); server.SendOrderTo(msgConn, "Message", "[MR] Private Msg: {0}".F(match.Groups[1].Value)); Console.WriteLine("[MR] PMsg to {0}: {1}", msgClient.Name, match.Groups[1].Value); } } } } if (line.StartsWith("map ") && server.State != ServerState.GameStarted) { // lock(server.LobbyInfo) // { var mapID = line.Remove(0, 4); string.Format(mapID); // server.Map = server.ModData.MapCache[mapID];; // LobbyCommands.LoadMapSettings(server, server.LobbyInfo.GlobalSettings, server.Map.Rules); Console.WriteLine("Changed map to {0}", mapID); server.SendMessage("[MR] Changed map to {0}".F(mapID)); // } } if (line == "help kick") { Console.WriteLine("Syntax: kick UserID"); } if (line.StartsWith("kick ")) { var aUID = line.Remove(0, 5); int kickUID; int.TryParse(aUID, out kickUID); var kickConn = server.Conns.SingleOrDefault(c => server.GetClient(c) != null && server.GetClient(c).Index == kickUID); if (kickConn != null) { var kickClient = server.GetClient(kickConn); Console.WriteLine("Kicking CID {0} - {1}", kickUID.ToString(), kickClient.Name); server.SendMessage("[MR] Kicking CID {0} - {1}".F(kickUID.ToString(), kickClient.Name)); server.SendOrderTo(kickConn, "ServerError", "You were kicked by the console."); server.DropClient(kickConn); if (server.State != ServerState.GameStarted) { server.SyncLobbyClients(); server.SyncLobbySlots(); } } } if (line.StartsWith("tban ")) { var aUID = line.Remove(0, 5); int banUID; int.TryParse(aUID, out banUID); var banConn = server.Conns.SingleOrDefault(c => server.GetClient(c) != null && server.GetClient(c).Index == banUID); if (banConn != null) { var banClient = server.GetClient(banConn); server.SendOrderTo(banConn, "ServerError", "You were temp banned by the console."); server.TempBans.Add(banClient.IPAddress); server.DropClient(banConn); Console.WriteLine("Tempbanning CID {0} - {1}", banUID.ToString(), banClient.Name); server.SendMessage("[MR] Tempbanning CID {0} - {1}".F(banUID.ToString(), banClient.Name)); if (server.State != ServerState.GameStarted) { server.SyncLobbyClients(); server.SyncLobbySlots(); } } } if (line.StartsWith("ipban ")) { string aIPstr = line.Remove(0, 5); Regex validIpV4AddressRegex = new Regex(@"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", RegexOptions.IgnoreCase); if (!string.IsNullOrWhiteSpace(aIPstr)) { bool flag = validIpV4AddressRegex.IsMatch(aIPstr.Trim()); if (flag) { server.TempBans.Add(aIPstr.Trim()); Console.WriteLine("TempIP Ban {0}", aIPstr.Trim()); Log.Write("server", "TempIP Ban {0}", aIPstr.Trim()); } else { Console.WriteLine("{0} is not a valid IP", aIPstr.ToString()); } } } if (line == "banlist") { foreach (var item in server.TempBans) { Console.WriteLine(item); } } if (line.StartsWith("admin ")) { var aUID = line.Remove(0, 6); int adminUID; int.TryParse(aUID, out adminUID); var adminConn = server.Conns.SingleOrDefault(c => server.GetClient(c) != null && server.GetClient(c).Index == adminUID); if (adminConn != null) { var adminClient = server.GetClient(adminConn); adminClient.IsAdmin = true; Console.WriteLine("Setting Admin {0} - {1}]", adminUID.ToString(), adminClient.Name); server.SendMessage("[MR] Setting Admin {0} - {1}".F(adminUID.ToString(), adminClient.Name)); server.SendOrderTo(adminConn, "Message", "You have been set as an admin by the console"); if (server.State != ServerState.GameStarted) { server.SyncLobbyClients(); server.SyncLobbySlots(); } } } if (line.StartsWith("deladmin ")) { var aUID = line.Remove(0, 9); int adminUID; int.TryParse(aUID, out adminUID); var adminConn = server.Conns.SingleOrDefault(c => server.GetClient(c) != null && server.GetClient(c).Index == adminUID); if (adminConn != null) { var adminClient = server.GetClient(adminConn); adminClient.IsAdmin = false; Console.WriteLine("Removing Admin {0} - {1}]", adminUID.ToString(), adminClient.Name); server.SendMessage("[MR] Removing Admin {0} - {1}".F(adminUID.ToString(), adminClient.Name)); server.SendOrderTo(adminConn, "Message", "Your admin status was revoked by the console"); if (server.State != ServerState.GameStarted) { server.SyncLobbyClients(); server.SyncLobbySlots(); } } } if (line == "status") { Console.WriteLine(string.Format("CurMap: {0} \n", server.Map.Title)); Console.WriteLine(string.Format("MapHash: {0} \n", server.LobbyInfo.GlobalSettings.Map)); Console.WriteLine(string.Format("CurConnections: {0} \n", server.LobbyInfo.Clients.Count())); Console.WriteLine(string.Format("MapMaxPlayers: {0} \n", server.LobbyInfo.Slots.Count())); Console.WriteLine(string.Format("GameState: {0} \n", server.State.ToString())); var testbuffer = server.Conns.ToArray(); foreach (var item in testbuffer) { var clientIdT = server.GetClient(item); // make the temporary array the client ID Console.WriteLine(string.Format("ID: {0} IP: {1} TEAM: {2} SPEC: {3}, NAME: {4} \n", clientIdT.Index.ToString(), clientIdT.IPAddress.ToString(), clientIdT.Team.ToString(), clientIdT.IsObserver.ToString(), clientIdT.Name.ToString())); } } } }
public void ServerShutdown(S server) { consoleThread.Abort(); }
public void Tick(S server) { if ((Game.RunTime - lastPing > PingInterval) || isInitialPing) { isInitialPing = false; lastPing = Game.RunTime; // Ignore client timeout in singleplayer games to make debugging easier if (server.LobbyInfo.NonBotClients.Count() < 2 && !server.Dedicated) { foreach (var c in server.Conns.ToList()) { server.SendOrderTo(c, "Ping", Game.RunTime.ToString()); } } else { foreach (var c in server.Conns.ToList()) { if (c == null || c.Socket == null) { continue; } var client = server.GetClient(c); if (client == null) { server.DropClient(c); server.SendMessage("A player has been dropped after timing out."); continue; } if (c.TimeSinceLastResponse < ConnTimeout) { server.SendOrderTo(c, "Ping", Game.RunTime.ToString()); if (!c.TimeoutMessageShown && c.TimeSinceLastResponse > PingInterval * 2) { server.SendMessage(client.Name + " is experiencing connection problems."); c.TimeoutMessageShown = true; } } else { server.SendMessage(client.Name + " has been dropped after timing out."); server.DropClient(c); } } if (Game.RunTime - lastConnReport > ConnReportInterval) { lastConnReport = Game.RunTime; var timeouts = server.Conns .Where(c => c.TimeSinceLastResponse > ConnReportInterval && c.TimeSinceLastResponse < ConnTimeout) .OrderBy(c => c.TimeSinceLastResponse); foreach (var c in timeouts) { if (c == null || c.Socket == null) { continue; } var client = server.GetClient(c); if (client != null) { server.SendMessage("{0} will be dropped in {1} seconds.".F(client.Name, (ConnTimeout - c.TimeSinceLastResponse) / 1000)); } } } } } }
public void LobbyInfoSynced(Server server) { Console.WriteLine("LobbyInfoSynced()"); }
public void GameEnded(Server server) { Console.WriteLine("GameEnded()"); }
public void GameStarted(S server) { PingMasterServer(server); }
public void LobbyInfoSynced(S server) { PingMasterServer(server); }
public bool InterpretCommand(Server server, Connection conn, Session.Client client, string cmd) { Console.WriteLine("Server received command from player {1}: {0}",cmd, conn.PlayerIndex); return false; }
public void LobbyInfoSynced(S server) { PingMasterServer(server); }
public void ServerStarted(Server server) { Console.WriteLine("ServerStarted()"); }