void InterpretServerOrder(Connection conn, ServerOrder so) { var fromClient = GetClient(conn); var fromIndex = fromClient != null ? fromClient.Index : 0; switch (so.Name) { case "Command": bool handled = false; foreach (var t in ServerTraits.WithInterface <IInterpretCommand>()) { if ((handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data))) { break; } } if (!handled) { Log.Write("server", "Unknown server command: {0}", so.Data); SendChatTo(conn, "Unknown server command: {0}".F(so.Data)); } break; case "HandshakeResponse": ValidateClient(conn, so.Data); break; case "Chat": case "TeamChat": foreach (var c in conns.Except(conn).ToArray()) { DispatchOrdersToClient(c, fromIndex, 0, so.Serialize()); } break; case "PauseRequest": foreach (var c in conns.ToArray()) { var x = Order.PauseGame(); DispatchOrdersToClient(c, fromIndex, 0, x.Serialize()); } break; } }
void InterpretServerOrders(Connection conn, byte[] data) { var ms = new MemoryStream(data); var br = new BinaryReader(ms); try { while (ms.Position < ms.Length) { var so = ServerOrder.Deserialize(br); if (so == null) { return; } InterpretServerOrder(conn, so); } } catch (EndOfStreamException) { } catch (NotImplementedException) { } }
void InterpretServerOrder(Connection conn, ServerOrder so) { switch (so.Name) { case "Command": { var handled = false; foreach (var t in serverTraits.WithInterface <IInterpretCommand>()) { if (handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data)) { break; } } if (!handled) { Log.Write("server", "Unknown server command: {0}", so.Data); SendOrderTo(conn, "Message", "Unknown server command: {0}".F(so.Data)); } break; } case "HandshakeResponse": ValidateClient(conn, so.Data); break; case "Chat": case "TeamChat": case "PauseGame": DispatchOrdersToClients(conn, 0, so.Serialize()); break; case "Pong": { int pingSent; if (!OpenRA.Exts.TryParseIntegerInvariant(so.Data, out pingSent)) { Log.Write("server", "Invalid order pong payload: {0}", so.Data); break; } var pingFromClient = LobbyInfo.PingFromClient(GetClient(conn)); if (pingFromClient == null) { return; } var history = pingFromClient.LatencyHistory.ToList(); history.Add(Game.RunTime - pingSent); // Cap ping history at 5 values (25 seconds) if (history.Count > 5) { history.RemoveRange(0, history.Count - 5); } pingFromClient.Latency = history.Sum() / history.Count; pingFromClient.LatencyJitter = (history.Max() - history.Min()) / 2; pingFromClient.LatencyHistory = history.ToArray(); SyncClientPing(); break; } } }
void InterpretServerOrder(Connection conn, ServerOrder so) { // Only accept handshake responses from unvalidated clients // Anything else may be an attempt to exploit the server if (!conn.Validated) { if (so.Name == "HandshakeResponse") { ValidateClient(conn, so.Data); } else { Log.Write("server", "Rejected connection from {0}; Order `{1}` is not a `HandshakeResponse`.", conn.Socket.RemoteEndPoint, so.Name); DropClient(conn); } return; } switch (so.Name) { case "Command": { var handledBy = serverTraits.WithInterface <IInterpretCommand>() .FirstOrDefault(t => t.InterpretCommand(this, conn, GetClient(conn), so.Data)); if (handledBy == null) { Log.Write("server", "Unknown server command: {0}", so.Data); SendOrderTo(conn, "Message", "Unknown server command: {0}".F(so.Data)); } break; } case "Chat": case "TeamChat": case "PauseGame": DispatchOrdersToClients(conn, 0, so.Serialize()); break; case "Pong": { long pingSent; if (!OpenRA.Exts.TryParseInt64Invariant(so.Data, out pingSent)) { Log.Write("server", "Invalid order pong payload: {0}", so.Data); break; } var client = GetClient(conn); if (client == null) { return; } var pingFromClient = LobbyInfo.PingFromClient(client); if (pingFromClient == null) { return; } var history = pingFromClient.LatencyHistory.ToList(); history.Add(Game.RunTime - pingSent); // Cap ping history at 5 values (25 seconds) if (history.Count > 5) { history.RemoveRange(0, history.Count - 5); } pingFromClient.Latency = history.Sum() / history.Count; pingFromClient.LatencyJitter = (history.Max() - history.Min()) / 2; pingFromClient.LatencyHistory = history.ToArray(); SyncClientPing(); break; } } }
void InterpretServerOrder(Connection conn, ServerOrder so) { switch (so.Name) { case "Command": { var handledBy = serverTraits.WithInterface<IInterpretCommand>() .FirstOrDefault(t => t.InterpretCommand(this, conn, GetClient(conn), so.Data)); if (handledBy == null) { Log.Write("server", "Unknown server command: {0}", so.Data); SendOrderTo(conn, "Message", "Unknown server command: {0}".F(so.Data)); } break; } case "HandshakeResponse": ValidateClient(conn, so.Data); break; case "Chat": case "TeamChat": case "PauseGame": DispatchOrdersToClients(conn, 0, so.Serialize()); break; case "Pong": { long pingSent; if (!OpenRA.Exts.TryParseInt64Invariant(so.Data, out pingSent)) { Log.Write("server", "Invalid order pong payload: {0}", so.Data); break; } var client = GetClient(conn); if (client == null) return; var pingFromClient = LobbyInfo.PingFromClient(client); if (pingFromClient == null) return; var history = pingFromClient.LatencyHistory.ToList(); history.Add(Game.RunTime - pingSent); // Cap ping history at 5 values (25 seconds) if (history.Count > 5) history.RemoveRange(0, history.Count - 5); pingFromClient.Latency = history.Sum() / history.Count; pingFromClient.LatencyJitter = (history.Max() - history.Min()) / 2; pingFromClient.LatencyHistory = history.ToArray(); SyncClientPing(); break; } } }
void InterpretServerOrder(Connection conn, ServerOrder so) { switch (so.Name) { case "Command": bool handled = false; foreach (var t in ServerTraits.WithInterface<IInterpretCommand>()) if ((handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data))) break; if (!handled) { Log.Write("server", "Unknown server command: {0}", so.Data); SendOrderTo(conn, "Message", "Unknown server command: {0}".F(so.Data)); } break; case "HandshakeResponse": ValidateClient(conn, so.Data); break; case "Chat": case "TeamChat": case "PauseGame": DispatchOrdersToClients(conn, 0, so.Serialize()); break; case "Pong": { int pingSent; if (!int.TryParse(so.Data, out pingSent)) { Log.Write("server", "Invalid order pong payload: {0}", so.Data); break; } var fromClient = GetClient(conn); var history = fromClient.LatencyHistory.ToList(); history.Add(Environment.TickCount - pingSent); // Cap ping history at 5 values (25 seconds) if (history.Count > 5) history.RemoveRange(0, history.Count - 5); fromClient.Latency = history.Sum() / history.Count; fromClient.LatencyJitter = (history.Max() - history.Min())/2; fromClient.LatencyHistory = history.ToArray(); if (State == ServerState.WaitingPlayers) SyncLobbyInfo(); break; } } }
static void InterpretServerOrder(Connection conn, ServerOrder so) { switch (so.Name) { case "Command": { if(GameStarted) SendChatTo(conn, "Cannot change state when game started."); else if (GetClient(conn).State == Session.ClientState.Ready && !(so.Data == "ready" || so.Data == "startgame") ) SendChatTo(conn, "Cannot change state when marked as ready."); else if (!InterpretCommand(conn, so.Data)) { Log.Write("server", "Bad server command: {0}", so.Data); SendChatTo(conn, "Bad server command."); }; } break; case "Chat": case "TeamChat": foreach (var c in conns.Except(conn).ToArray()) DispatchOrdersToClient(c, GetClient(conn).Index, 0, so.Serialize()); break; } }
static void InterpretServerOrder(Connection conn, ServerOrder so) { switch (so.Name) { case "Chat": if (so.Data.StartsWith("/")) { if (!InterpretCommand(conn, so.Data.Substring(1))) { Console.WriteLine("Bad server command: {0}", so.Data.Substring(1)); SendChatTo(conn, "Bad server command."); } } else foreach (var c in conns.Except(conn).ToArray()) DispatchOrdersToClient(c, GetClient(conn).Index, 0, so.Serialize()); break; case "RequestFile": { Console.WriteLine("** Requesting file: `{0}`", so.Data); var client = GetClient(conn); client.State = Session.ClientState.Downloading; var filename = so.Data.Split(':')[0]; if (conn.Stream != null) conn.Stream.Dispose(); conn.Stream = File.OpenRead(filename); // todo: validate that the SHA1 they asked for matches what we've got. var length = (int) new FileInfo(filename).Length; conn.NextChunk = 0; conn.NumChunks = (length + DownloadChunkSize - 1) / DownloadChunkSize; conn.RemainingBytes = length; SyncLobbyInfo(); } break; } }
void InterpretServerOrder(Connection conn, ServerOrder so) { var fromClient = GetClient(conn); var fromIndex = fromClient != null ? fromClient.Index : 0; switch (so.Name) { case "Command": bool handled = false; foreach (var t in ServerTraits.WithInterface<IInterpretCommand>()) if ((handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data))) break; if (!handled) { Log.Write("server", "Unknown server command: {0}", so.Data); SendChatTo(conn, "Unknown server command: {0}".F(so.Data)); } break; case "HandshakeResponse": ValidateClient(conn, so.Data); break; case "Chat": case "TeamChat": foreach (var c in conns.Except(conn).ToArray()) DispatchOrdersToClient(c, fromIndex, 0, so.Serialize()); break; case "PauseRequest": foreach (var c in conns.ToArray()) { var x = Order.PauseGame(); DispatchOrdersToClient(c, fromIndex, 0, x.Serialize()); } break; } }
void InterpretServerOrder(Connection conn, ServerOrder so) { switch (so.Name) { case "Command": bool handled = false; foreach (var t in ServerTraits.WithInterface <IInterpretCommand>()) { if ((handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data))) { break; } } if (!handled) { Log.Write("server", "Unknown server command: {0}", so.Data); SendOrderTo(conn, "Message", "Unknown server command: {0}".F(so.Data)); } break; case "HandshakeResponse": ValidateClient(conn, so.Data); break; case "Chat": case "TeamChat": case "PauseGame": DispatchOrdersToClients(conn, 0, so.Serialize()); break; case "Pong": { int pingSent; if (!int.TryParse(so.Data, out pingSent)) { Log.Write("server", "Invalid order pong payload: {0}", so.Data); break; } var fromClient = GetClient(conn); var history = fromClient.LatencyHistory.ToList(); history.Add(Environment.TickCount - pingSent); // Cap ping history at 5 values (25 seconds) if (history.Count > 5) { history.RemoveRange(0, history.Count - 5); } fromClient.Latency = history.Sum() / history.Count; fromClient.LatencyJitter = (history.Max() - history.Min()) / 2; fromClient.LatencyHistory = history.ToArray(); if (State == ServerState.WaitingPlayers) { SyncLobbyInfo(); } break; } } }
void InterpretServerOrder(Connection conn, ServerOrder so) { // Only accept handshake responses from unvalidated clients // Anything else may be an attempt to exploit the server if (!conn.Validated) { if (so.Name == "HandshakeResponse") { ValidateClient(conn, so.Data); } else { Log.Write("server", "Rejected connection from {0}; Order `{1}` is not a `HandshakeResponse`.", conn.Socket.RemoteEndPoint, so.Name); DropClient(conn); } return; } switch (so.Name) { case "Command": { var handledBy = serverTraits.WithInterface <IInterpretCommand>() .FirstOrDefault(t => t.InterpretCommand(this, conn, GetClient(conn), so.Data)); if (handledBy == null) { Log.Write("server", "Unknown server command: {0}", so.Data); SendOrderTo(conn, "Message", "Unknown server command: {0}".F(so.Data)); } break; } case "Chat": case "PauseGame": DispatchOrdersToClients(conn, 0, so.Serialize()); break; case "Pong": { long pingSent; if (!Primitives.Exts.TryParseInt64Invariant(so.Data, out pingSent)) { Log.Write("server", "Invalid order pong payload: {0}", so.Data); break; } var client = GetClient(conn); if (client == null) { return; } var pingFromClient = LobbyInfo.PingFromClient(client); if (pingFromClient == null) { return; } var history = pingFromClient.LatencyHistory.ToList(); history.Add(Game.RunTime - pingSent); // Cap ping history at 5 values (25 seconds) if (history.Count > 5) { history.RemoveRange(0, history.Count - 5); } pingFromClient.Latency = history.Sum() / history.Count; pingFromClient.LatencyJitter = (history.Max() - history.Min()) / 2; pingFromClient.LatencyHistory = history.ToArray(); SyncClientPing(); break; } case "GameSaveTraitData": { if (GameSave != null) { var data = MiniYaml.FromString(so.Data)[0]; GameSave.AddTraitData(int.Parse(data.Key), data.Value); } break; } case "CreateGameSave": { if (GameSave != null) { // Sanitize potentially malicious input var filename = so.Data; var invalidIndex = -1; var invalidChars = Path.GetInvalidFileNameChars(); while ((invalidIndex = filename.IndexOfAny(invalidChars)) != -1) { filename = filename.Remove(invalidIndex, 1); } var baseSavePath = Platform.ResolvePath( Platform.SupportDirPrefix, "Saves", ModData.Manifest.Id, ModData.Manifest.Metadata.Version); if (!Directory.Exists(baseSavePath)) { Directory.CreateDirectory(baseSavePath); } GameSave.Save(Path.Combine(baseSavePath, filename)); DispatchOrdersToClients(null, 0, new ServerOrder("GameSaved", filename).Serialize()); } break; } case "LoadGameSave": { if (Dedicated || State >= ServerState.GameStarted) { break; } // Sanitize potentially malicious input var filename = so.Data; var invalidIndex = -1; var invalidChars = Path.GetInvalidFileNameChars(); while ((invalidIndex = filename.IndexOfAny(invalidChars)) != -1) { filename = filename.Remove(invalidIndex, 1); } var savePath = Platform.ResolvePath( Platform.SupportDirPrefix, "Saves", ModData.Manifest.Id, ModData.Manifest.Metadata.Version, filename); GameSave = new GameSave(savePath); LobbyInfo.GlobalSettings = GameSave.GlobalSettings; LobbyInfo.Slots = GameSave.Slots; // Reassign clients to slots // - Bot ordering is preserved // - Humans are assigned on a first-come-first-serve basis // - Leftover humans become spectators // Start by removing all bots and assigning all players as spectators foreach (var c in LobbyInfo.Clients) { if (c.Bot != null) { LobbyInfo.Clients.Remove(c); var ping = LobbyInfo.PingFromClient(c); if (ping != null) { LobbyInfo.ClientPings.Remove(ping); } } else { c.Slot = null; } } // Rebuild/remap the saved client state // TODO: Multiplayer saves should leave all humans as spectators so they can manually pick slots var adminClientIndex = LobbyInfo.Clients.First(c => c.IsAdmin).Index; foreach (var kv in GameSave.SlotClients) { if (kv.Value.Bot != null) { var bot = new Session.Client() { Index = ChooseFreePlayerIndex(), State = Session.ClientState.NotReady, BotControllerClientIndex = adminClientIndex }; kv.Value.ApplyTo(bot); LobbyInfo.Clients.Add(bot); } else { // This will throw if the server doesn't have enough human clients to fill all player slots // See TODO above - this isn't a problem in practice because MP saves won't use this var client = LobbyInfo.Clients.First(c => c.Slot == null); kv.Value.ApplyTo(client); } } SyncLobbyInfo(); SyncLobbyClients(); SyncClientPing(); break; } } }
void InterpretServerOrder(Connection conn, ServerOrder so) { switch (so.Name) { case "Command": { bool handled = false; foreach (var t in ServerTraits.WithInterface<IInterpretCommand>()) if ((handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data))) break; if (!handled) { Log.Write("server", "Unknown server command: {0}", so.Data); SendChatTo(conn, "Unknown server command: {0}".F(so.Data)); } } break; case "Chat": case "TeamChat": foreach (var c in conns.Except(conn).ToArray()) DispatchOrdersToClient(c, GetClient(conn).Index, 0, so.Serialize()); break; } }