From 298314626eb20e39cb11c7b96e86b90f36a1757b Mon Sep 17 00:00:00 2001 From: geckosoft Date: Mon, 1 Nov 2010 05:10:55 +0100 Subject: [PATCH] Added: Dedicated server support Added: The ability to not render anything when using the client as a dedicated server Added: The basic server extension (NullServerExtension) Exposed: Made some fields public, so that the server extension can access it --- OpenRA.Game/Game.cs | 154 ++++++++++++++++++++------ OpenRA.Game/GameRules/Settings.cs | 21 +++- OpenRA.Game/Graphics/Renderer.cs | 6 +- OpenRA.Game/Misc.cs | 176 ++++++++++++++++++++++++++++++ OpenRA.Game/Network/UnitOrders.cs | 13 +++ OpenRA.Game/OpenRA.Game.csproj | 1 + OpenRA.Game/Server/Connection.cs | 20 ++-- OpenRA.Game/Server/Server.cs | 82 +++++++++++--- 8 files changed, 412 insertions(+), 61 deletions(-) create mode 100644 OpenRA.Game/Misc.cs diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 57bad26216..d02ed323c6 100755 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -244,46 +244,138 @@ namespace OpenRA Console.WriteLine("Loading mods: {0}",string.Join(",",mods)); modData = new ModData( mods ); - + + // when this client is running in dedicated mode ... + if (Settings.Server.IsDedicated) + { + // it may specify a yaml extension file (to add non synced traits) + if (!string.IsNullOrEmpty(Settings.Server.ExtensionYaml)) + { + var r = modData.Manifest.LocalRules.ToList(); + r.Add(Settings.Server.ExtensionYaml); + modData.Manifest.LocalRules = r.ToArray(); + } + // and a dll to the assemblies (to add those non synced traits) + if (!string.IsNullOrEmpty(Settings.Server.ExtensionDll)) + { + var r = modData.Manifest.LocalAssemblies.ToList(); + r.Add(Settings.Server.ExtensionDll); + modData.Manifest.LocalAssemblies = r.ToArray(); + } + + if (!string.IsNullOrEmpty(Settings.Server.ExtensionClass)) + Settings.Server.Extension = modData.ObjectCreator.CreateObject(Settings.Server.ExtensionClass); + } + Sound.Initialize(); PerfHistory.items["render"].hasNormalTick = false; PerfHistory.items["batches"].hasNormalTick = false; PerfHistory.items["text"].hasNormalTick = false; PerfHistory.items["cursor"].hasNormalTick = false; - - JoinLocal(); - StartGame(modData.Manifest.ShellmapUid); - Game.ConnectionStateChanged += orderManager => + if (!Settings.Graphics.UseNullRenderer) { - Widget.CloseWindow(); - switch( orderManager.Connection.ConnectionState ) - { - case ConnectionState.PreConnecting: - Widget.OpenWindow("MAINMENU_BG"); - break; - case ConnectionState.Connecting: - Widget.OpenWindow( "CONNECTING_BG", - new Dictionary { { "host", orderManager.Host }, { "port", orderManager.Port } } ); - break; - case ConnectionState.NotConnected: - Widget.OpenWindow( "CONNECTION_FAILED_BG", - new Dictionary { { "host", orderManager.Host }, { "port", orderManager.Port } } ); - break; - case ConnectionState.Connected: - var lobby = Widget.OpenWindow( "SERVER_LOBBY", new Dictionary { { "orderManager", orderManager } } ); - lobby.GetWidget("CHAT_DISPLAY").ClearChat(); - lobby.GetWidget("CHANGEMAP_BUTTON").Visible = true; - lobby.GetWidget("LOCKTEAMS_CHECKBOX").Visible = true; - lobby.GetWidget("DISCONNECT_BUTTON").Visible = true; - //r.GetWidget("INGAME_ROOT").GetWidget("CHAT_DISPLAY").ClearChat(); - break; - } - }; + JoinLocal(); + StartGame(modData.Manifest.ShellmapUid); - modData.WidgetLoader.LoadWidget( new Dictionary(), Widget.RootWidget, "PERF_BG" ); - Widget.OpenWindow("MAINMENU_BG"); + Game.ConnectionStateChanged += om => + { + Widget.CloseWindow(); + switch (om.Connection.ConnectionState) + { + case ConnectionState.PreConnecting: + Widget.OpenWindow("MAINMENU_BG"); + break; + case ConnectionState.Connecting: + Widget.OpenWindow("CONNECTING_BG", + new Dictionary + {{"host", om.Host}, {"port", om.Port}}); + break; + case ConnectionState.NotConnected: + Widget.OpenWindow("CONNECTION_FAILED_BG", + new Dictionary + {{"host", om.Host}, {"port", om.Port}}); + break; + case ConnectionState.Connected: + var lobby = Widget.OpenWindow("SERVER_LOBBY", + new Dictionary + {{"orderManager", om}}); + lobby.GetWidget("CHAT_DISPLAY").ClearChat(); + lobby.GetWidget("CHANGEMAP_BUTTON").Visible = true; + lobby.GetWidget("LOCKTEAMS_CHECKBOX").Visible = true; + lobby.GetWidget("DISCONNECT_BUTTON").Visible = true; + + // Inform whoever is willing to hear it that the player is connected to the lobby + if (ConnectedToLobby != null) + ConnectedToLobby(); + + if (Settings.Server.IsDedicated) + { + // Force spectator as a default + Game.orderManager.IssueOrder(Order.Command("spectator")); + } + + if (Game.Settings.Server.Extension != null) + Game.Settings.Server.Extension.OnLobbyUp(); + break; + } + }; + + modData.WidgetLoader.LoadWidget(new Dictionary(), Widget.RootWidget, "PERF_BG"); + Widget.OpenWindow("MAINMENU_BG"); + }else + { + JoinLocal(); + StartGame(modData.Manifest.ShellmapUid); + + Game.ConnectionStateChanged += om => + { + Widget.CloseWindow(); + switch (om.Connection.ConnectionState) + { + case ConnectionState.PreConnecting: + Widget.OpenWindow("MAINMENU_BG"); + break; + case ConnectionState.Connecting: + Widget.OpenWindow("CONNECTING_BG", + new Dictionary { { "host", om.Host }, { "port", om.Port } }); + break; + case ConnectionState.NotConnected: + Widget.OpenWindow("CONNECTION_FAILED_BG", + new Dictionary { { "host", om.Host }, { "port", om.Port } }); + break; + case ConnectionState.Connected: + var lobby = Widget.OpenWindow("SERVER_LOBBY", + new Dictionary { { "orderManager", om } }); + + // Inform whoever is willing to hear it that the player is connected to the lobby + if (ConnectedToLobby != null) + ConnectedToLobby(); + + if (Settings.Server.IsDedicated) + { + // Force spectator as a default + Game.orderManager.IssueOrder(Order.Command("spectator")); + } + + if (Game.Settings.Server.Extension != null) + Game.Settings.Server.Extension.OnLobbyUp(); + break; + } + }; + + modData.WidgetLoader.LoadWidget(new Dictionary(), Widget.RootWidget, "PERF_BG"); + Widget.OpenWindow("MAINMENU_BG"); + } + + if (Settings.Server.IsDedicated) + { + // Auto-host + var map = Game.modData.AvailableMaps.FirstOrDefault(m => m.Value.Selectable).Key; + Server.Server.ServerMain(Game.modData, Settings, map); + Game.JoinServer(IPAddress.Loopback.ToString(), Settings.Server.ListenPort); + } Game.orderManager.LastTickTime = Environment.TickCount; } diff --git a/OpenRA.Game/GameRules/Settings.cs b/OpenRA.Game/GameRules/Settings.cs index 7d78290a9e..282c14442a 100755 --- a/OpenRA.Game/GameRules/Settings.cs +++ b/OpenRA.Game/GameRules/Settings.cs @@ -26,6 +26,13 @@ namespace OpenRA.GameRules public bool AdvertiseOnline = true; public string MasterServer = "http://master.open-ra.org/"; public bool AllowCheats = false; + public string ExtensionDll = ""; + public string ExtensionClass = ""; + + /* not storeable */ + public IServerExtension Extension { get; set; } + public string ExtensionYaml { get; set; } + public bool IsDedicated { get; set; } } public class DebugSettings @@ -34,13 +41,16 @@ namespace OpenRA.GameRules public bool RecordSyncReports = true; public float LongTickThreshold = 0.001f; } - + public class GraphicSettings { public WindowMode Mode = WindowMode.PseudoFullscreen; - public int2 FullscreenSize = new int2(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height); - public int2 WindowedSize = new int2(1024,768); + public int2 FullscreenSize = new int2(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); + public int2 WindowedSize = new int2(1024, 768); public readonly int2 MinResolution = new int2(800, 600); + public readonly string RenderEngine = "OpenRA.Gl.dll"; + public readonly string NullRenderEngine = "OpenRA.Renderer.Null.dll"; + public bool UseNullRenderer { get; set; } } public class SoundSettings @@ -103,6 +113,11 @@ namespace OpenRA.GameRules {"Debug", Debug} }; + // Should we run in dedicated mode (use the server extension) + Server.IsDedicated = args.GetValue("Server.IsDedicated", false); + if (Server.IsDedicated) + Graphics.UseNullRenderer = args.GetValue("Graphics.UseNullRenderer", false); + // Override fieldloader to ignore invalid entries var err1 = FieldLoader.UnknownFieldAction; var err2 = FieldLoader.InvalidValueAction; diff --git a/OpenRA.Game/Graphics/Renderer.cs b/OpenRA.Game/Graphics/Renderer.cs index e0b510f0e8..0a463f425e 100644 --- a/OpenRA.Game/Graphics/Renderer.cs +++ b/OpenRA.Game/Graphics/Renderer.cs @@ -135,7 +135,11 @@ namespace OpenRA.Graphics internal static void Initialize( OpenRA.FileFormats.Graphics.WindowMode windowMode ) { var resolution = GetResolution( windowMode ); - device = CreateDevice( Assembly.LoadFile( Path.GetFullPath( "OpenRA.Gl.dll" ) ), resolution.Width, resolution.Height, windowMode, false ); + + if (Game.Settings.Graphics.UseNullRenderer) + device = CreateDevice(Assembly.LoadFile(Path.GetFullPath(Game.Settings.Graphics.NullRenderEngine)), resolution.Width, resolution.Height, windowMode, false); + else + device = CreateDevice( Assembly.LoadFile( Path.GetFullPath( Game.Settings.Graphics.RenderEngine ) ), resolution.Width, resolution.Height, windowMode, false ); } static Size GetResolution(WindowMode windowmode) diff --git a/OpenRA.Game/Misc.cs b/OpenRA.Game/Misc.cs new file mode 100644 index 0000000000..dee66b8200 --- /dev/null +++ b/OpenRA.Game/Misc.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using OpenRA.Network; +using OpenRA.Server; +using OpenRA.Traits; + +namespace OpenRA +{ + public interface IServerExtension + { + /// + /// Return true to use the build-in handling + /// + bool OnReadyUp(Connection conn, Session.Client client); + void OnStartGame(); + /// + /// Return true to use the build-in handling + /// + bool OnNickChange(Connection conn, Session.Client client, string newName); + + /// + /// Return true to use the build-in handling + /// + bool OnRaceChange(Connection conn, Session.Client client, string newRace); + + /// + /// Return true to use the build-in handling + /// + bool OnSlotChange(Connection conn, Session.Client client, Session.Slot slot, Map map); + + /// + /// Return true to use the build-in handling + /// + bool OnTeamChange(Connection conn, Session.Client getClient, int team); + + /// + /// Return true to use the build-in handling + /// + bool OnSpawnpointChange(Connection conn, Session.Client getClient, int spawnPoint); + + /// + /// Return true to use the build-in handling + /// + bool OnColorChange(Connection conn, Session.Client getClient, Color fromArgb, Color color); + + /// + /// Return true to use the build-in handling + /// + bool OnChat(Connection conn, string message, bool teamChat); + + void OnServerStart(); + void OnServerStop(bool forced); + void OnLoadMap(Map map); + + /// + /// Return false to drop the connection + /// + bool OnValidateConnection(bool gameStarted, Connection newConn); + + void OnLobbySync(Session lobbyInfo, bool gameStarted); + /// + /// Return true to use the build-in handling + /// + bool OnPingMasterServer(Session lobbyInfo, bool gameStarted); + + /// + /// Return true to use the build-in handling + /// + bool OnIngameChat(Session.Client client, string message, bool teamChat); + + void OnIngameSetStance(Player player, Player stanceForPlayer, Stance newStance); + + void OnLobbyUp(); + void OnRejoinLobby(World world); + } + + public class NullServerExtension : IServerExtension + { + public virtual bool OnReadyUp(Connection conn, Session.Client client) + { + return true; + } + + public virtual void OnStartGame() + { + + } + + public virtual bool OnNickChange(Connection conn, Session.Client client, string newName) + { + return true; + } + + public virtual bool OnRaceChange(Connection conn, Session.Client client, string newRace) + { + return true; + } + + public virtual bool OnSlotChange(Connection conn, Session.Client client, Session.Slot slot, Map map) + { + return true; + } + + public virtual bool OnTeamChange(Connection conn, Session.Client getClient, int team) + { + return true; + } + + public virtual bool OnSpawnpointChange(Connection conn, Session.Client getClient, int spawnPoint) + { + return true; + } + + public virtual bool OnColorChange(Connection conn, Session.Client getClient, Color fromArgb, Color color) + { + return true; + } + + public virtual bool OnChat(Connection conn, string message, bool teamChat) + { + return true; + } + + public virtual void OnServerStart() + { + } + + public virtual void OnServerStop(bool forced) + { + + } + + public virtual void OnLoadMap(Map map) + { + // Good spot to manipulate amount of spectators! ie set Server.MaxSpectators + } + + public virtual bool OnValidateConnection(bool gameStarted, Connection newConn) + { + return true; + } + + public virtual void OnLobbySync(Session lobbyInfo, bool gameStarted) + { + + } + + public virtual bool OnPingMasterServer(Session lobbyInfo, bool gameStarted) + { + return true; + } + + public virtual bool OnIngameChat(Session.Client client, string message, bool teamChat) + { + return true; + } + + public virtual void OnIngameSetStance(Player player, Player stanceForPlayer, Stance newStance) + { + + } + + public virtual void OnLobbyUp() + { + + } + + public virtual void OnRejoinLobby(World world) + { + + } + } +} diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 4819f544df..b62cfac2ad 100755 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -39,6 +39,10 @@ namespace OpenRA.Network case "Chat": { var client = orderManager.LobbyInfo.ClientWithIndex( clientId ); + + if (Game.IsHost && Game.Settings.Server.Extension != null && !Game.Settings.Server.Extension.OnIngameChat(client, order.TargetString, false)) + break; + if (client != null) { var player = world != null ? world.FindPlayerByClient(client) : null; @@ -52,6 +56,10 @@ namespace OpenRA.Network case "TeamChat": { var client = orderManager.LobbyInfo.ClientWithIndex(clientId); + + if (Game.IsHost && Game.Settings.Server.Extension != null && !Game.Settings.Server.Extension.OnIngameChat(client, order.TargetString, true)) + break; + if (client != null) { if (world == null) @@ -98,9 +106,14 @@ namespace OpenRA.Network } case "SetStance": { + var targetPlayer = order.Player.World.players[order.TargetLocation.X]; var newStance = (Stance)order.TargetLocation.Y; + if (Game.IsHost && Game.Settings.Server.Extension != null) + Game.Settings.Server.Extension.OnIngameSetStance(order.Player, targetPlayer, newStance); + + SetPlayerStance(world, order.Player, targetPlayer, newStance); // automatically declare war reciprocally diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index c5012c3577..ca0a89839f 100755 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -213,6 +213,7 @@ + diff --git a/OpenRA.Game/Server/Connection.cs b/OpenRA.Game/Server/Connection.cs index e9621a05eb..dfb6df2941 100644 --- a/OpenRA.Game/Server/Connection.cs +++ b/OpenRA.Game/Server/Connection.cs @@ -15,20 +15,20 @@ using System.Net.Sockets; namespace OpenRA.Server { - class Connection + public class Connection { - public Socket socket; - public List data = new List(); - public ReceiveState State = ReceiveState.Header; - public int ExpectLength = 8; - public int Frame = 0; + internal Socket socket; + internal List data = new List(); + internal ReceiveState State = ReceiveState.Header; + internal int ExpectLength = 8; + internal int Frame = 0; - public int MostRecentFrame = 0; + internal int MostRecentFrame = 0; /* client data */ - public int PlayerIndex; + public int PlayerIndex { get; internal set; } - public byte[] PopBytes(int n) + internal byte[] PopBytes(int n) { var result = data.GetRange(0, n); data.RemoveRange(0, n); @@ -65,7 +65,7 @@ namespace OpenRA.Server return true; } - public void ReadData() + internal void ReadData() { if (ReadDataInner()) while (data.Count >= ExpectLength) diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index fff2d9ca8d..395eebf9a8 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -25,12 +25,17 @@ namespace OpenRA.Server { static class Server { + public static Connection[] Connections + { + get { return conns.ToArray(); } + } + static List conns = new List(); static TcpListener listener = null; static Dictionary> inFlightFrames = new Dictionary>(); static Session lobbyInfo; - static bool GameStarted = false; + internal static bool GameStarted = false; static string Name; static int ExternalPort; static int randomSeed; @@ -61,6 +66,9 @@ namespace OpenRA.Server { } + + if (Game.Settings.Server.Extension != null) + Game.Settings.Server.Extension.OnServerStop(true); } public static void ServerMain(ModData modData, Settings settings, string map) { @@ -69,6 +77,7 @@ namespace OpenRA.Server isInitialPing = true; Server.masterServerUrl = settings.Server.MasterServer; isInternetServer = settings.Server.AdvertiseOnline; + listener = new TcpListener(IPAddress.Any, settings.Server.ListenPort); Name = settings.Server.Name; ExternalPort = settings.Server.ExternalPort; @@ -98,6 +107,9 @@ namespace OpenRA.Server throw new InvalidOperationException( "Unable to start server: port is already in use" ); } + if (Game.Settings.Server.Extension != null) + Game.Settings.Server.Extension.OnServerStart(); + new Thread( _ => { for( ; ; ) @@ -123,6 +135,9 @@ namespace OpenRA.Server { listener.Stop(); GameStarted = false; + + if (Game.Settings.Server.Extension != null) + Game.Settings.Server.Extension.OnServerStop(false); break; } } @@ -151,6 +166,9 @@ namespace OpenRA.Server .Select((s, i) => { s.Index = i; return s; }) .ToList(); + if (Game.Settings.Server.Extension != null) + Game.Settings.Server.Extension.OnLoadMap(Map); + // Generate slots for spectators for (int i = 0; i < MaxSpectators; i++) { @@ -302,7 +320,7 @@ namespace OpenRA.Server catch (NotImplementedException) { } } - static void SyncClientToPlayerReference(Session.Client c, PlayerReference pr) + public static void SyncClientToPlayerReference(Session.Client c, PlayerReference pr) { if (pr == null) return; @@ -315,7 +333,7 @@ namespace OpenRA.Server c.Country = pr.Race; } - static bool InterpretCommand(Connection conn, string cmd) + public static bool InterpretCommand(Connection conn, string cmd) { var dict = new Dictionary> { @@ -335,9 +353,12 @@ namespace OpenRA.Server SyncLobbyInfo(); - if (conns.Count > 0 && conns.All(c => GetClient(c).State == Session.ClientState.Ready)) - InterpretCommand(conn, "startgame"); - + if (Game.Settings.Server.Extension== null || Game.Settings.Server.Extension.OnReadyUp(conn, client)) + { + if (conns.Count > 0 && conns.All(c => GetClient(c).State == Session.ClientState.Ready)) + InterpretCommand(conn, "startgame"); + } + return true; }}, { "startgame", @@ -351,6 +372,8 @@ namespace OpenRA.Server DispatchOrders(null, 0, new ServerOrder("StartGame", "").Serialize()); + if (Game.Settings.Server.Extension != null) Game.Settings.Server.Extension.OnStartGame(); + PingMasterServer(); return true; }}, @@ -358,7 +381,11 @@ namespace OpenRA.Server s => { Log.Write("server", "Player@{0} is now known as {1}", conn.socket.RemoteEndPoint, s); - GetClient(conn).Name = s; + + if (Game.Settings.Server.Extension == null || Game.Settings.Server.Extension.OnNickChange(conn, GetClient(conn), s)) + GetClient(conn).Name = s; + + SyncLobbyInfo(); return true; }}, @@ -377,6 +404,7 @@ namespace OpenRA.Server { "race", s => { + if (Game.Settings.Server.Extension == null || Game.Settings.Server.Extension.OnRaceChange(conn, GetClient(conn), s)) GetClient(conn).Country = s; SyncLobbyInfo(); @@ -391,9 +419,12 @@ namespace OpenRA.Server var cl = GetClient(conn); + if (Game.Settings.Server.Extension == null || Game.Settings.Server.Extension.OnSlotChange(conn, cl, slotData, Map)) + { cl.Slot = slotData.Index; SyncClientToPlayerReference(cl, slotData.MapPlayer != null ? Map.Players[slotData.MapPlayer] : null); + } SyncLobbyInfo(); return true; @@ -403,8 +434,10 @@ namespace OpenRA.Server { int team; if (!int.TryParse(s, out team)) { Log.Write("server", "Invalid team: {0}", s ); return false; } - - GetClient(conn).Team = team; + if (Game.Settings.Server.Extension == null || Game.Settings.Server.Extension.OnTeamChange(conn, GetClient(conn), team)) + { + GetClient(conn).Team = team; + } SyncLobbyInfo(); return true; }}, @@ -423,8 +456,10 @@ namespace OpenRA.Server SendChatTo( conn, "You can't be at the same spawn point as another player" ); return true; } - - GetClient(conn).SpawnPoint = spawnPoint; + if (Game.Settings.Server.Extension == null || Game.Settings.Server.Extension.OnSpawnpointChange(conn, GetClient(conn), spawnPoint)) + { + GetClient(conn).SpawnPoint = spawnPoint; + } SyncLobbyInfo(); return true; }}, @@ -432,8 +467,12 @@ namespace OpenRA.Server s => { var c = s.Split(',').Select(cc => int.Parse(cc)).ToArray(); - GetClient(conn).Color1 = Color.FromArgb(c[0],c[1],c[2]); - GetClient(conn).Color2 = Color.FromArgb(c[3],c[4],c[5]); + + if (Game.Settings.Server.Extension == null || Game.Settings.Server.Extension.OnColorChange(conn, GetClient(conn), Color.FromArgb(c[0], c[1], c[2]), Color.FromArgb(c[3], c[4], c[5]))) + { + GetClient(conn).Color1 = Color.FromArgb(c[0], c[1], c[2]); + GetClient(conn).Color2 = Color.FromArgb(c[3], c[4], c[5]); + } SyncLobbyInfo(); return true; }}, @@ -449,10 +488,14 @@ namespace OpenRA.Server return false; var cl = GetClient(conn); - cl.Slot = slot; - - SyncClientToPlayerReference(cl, slotData.MapPlayer != null ? Map.Players[slotData.MapPlayer] : null); + if (Game.Settings.Server.Extension == null || Game.Settings.Server.Extension.OnSlotChange(conn, cl, slotData, Map)) + { + cl.Slot = slot; + + SyncClientToPlayerReference(cl, slotData.MapPlayer != null ? Map.Players[slotData.MapPlayer] : null); + } + SyncLobbyInfo(); return true; }}, @@ -628,6 +671,7 @@ namespace OpenRA.Server case "Chat": case "TeamChat": + if (Game.Settings.Server.Extension == null || Game.Settings.Server.Extension.OnChat(conn, so.Data, so.Name == "TeamChat")) foreach (var c in conns.Except(conn).ToArray()) DispatchOrdersToClient(c, GetClient(conn).Index, 0, so.Serialize()); break; @@ -654,6 +698,9 @@ namespace OpenRA.Server static void SyncLobbyInfo() { + if (Game.Settings.Server.Extension != null) + Game.Settings.Server.Extension.OnLobbySync(lobbyInfo, GameStarted); + if (!GameStarted) /* don't do this while the game is running, it breaks things. */ DispatchOrders(null, 0, new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize()); @@ -667,6 +714,9 @@ namespace OpenRA.Server { if (isBusy || !isInternetServer) return; + if (Game.Settings.Server.Extension != null && !Game.Settings.Server.Extension.OnPingMasterServer(lobbyInfo, GameStarted)) + return; + lastPing = Environment.TickCount; isBusy = true;