From 2da4e87b9452f7443044d20b516fd92c8e15cf7d Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 20 Jan 2017 20:29:51 +0000 Subject: [PATCH] Implement new server-connection mod switching logic. --- OpenRA.Game/Network/GameServer.cs | 23 +++- OpenRA.Game/Network/OrderManager.cs | 2 + OpenRA.Game/Network/UnitOrders.cs | 21 ++- .../Widgets/Logic/ConnectionLogic.cs | 120 +++++++++++++++++- .../Widgets/Logic/Lobby/LobbyLogic.cs | 4 +- mods/cnc/chrome/connection.yaml | 69 ++++++++++ mods/ra/chrome/connection.yaml | 68 ++++++++++ 7 files changed, 292 insertions(+), 15 deletions(-) diff --git a/OpenRA.Game/Network/GameServer.cs b/OpenRA.Game/Network/GameServer.cs index 62ffb58264..0ef4ac4c3a 100644 --- a/OpenRA.Game/Network/GameServer.cs +++ b/OpenRA.Game/Network/GameServer.cs @@ -39,16 +39,29 @@ namespace OpenRA.Network FieldLoader.Load(this, yaml); Manifest mod; + ExternalMod external; var modVersion = Mods.Split('@'); - if (modVersion.Length == 2 && Game.Mods.TryGetValue(modVersion[0], out mod)) + + ModLabel = "Unknown mod: {0}".F(Mods); + if (modVersion.Length == 2) { ModId = modVersion[0]; ModVersion = modVersion[1]; - ModLabel = "{0} ({1})".F(mod.Metadata.Title, modVersion[1]); - IsCompatible = Game.Settings.Debug.IgnoreVersionMismatch || ModVersion == mod.Metadata.Version; + + if (Game.Mods.TryGetValue(modVersion[0], out mod)) + { + ModLabel = "{0} ({1})".F(mod.Metadata.Title, modVersion[1]); + IsCompatible = Game.Settings.Debug.IgnoreVersionMismatch || ModVersion == mod.Metadata.Version; + } + + var externalKey = ExternalMod.MakeKey(modVersion[0], modVersion[1]); + if (!IsCompatible && Game.ExternalMods.TryGetValue(externalKey, out external) + && external.Version == modVersion[1]) + { + ModLabel = "{0} ({1})".F(external.Title, external.Version); + IsCompatible = true; + } } - else - ModLabel = "Unknown mod: {0}".F(Mods); var mapAvailable = Game.Settings.Game.AllowDownloading || Game.ModData.MapCache[Map].Status == MapStatus.Available; IsJoinable = IsCompatible && State == 1 && mapAvailable; diff --git a/OpenRA.Game/Network/OrderManager.cs b/OpenRA.Game/Network/OrderManager.cs index 246a221673..b103e4c7ae 100644 --- a/OpenRA.Game/Network/OrderManager.cs +++ b/OpenRA.Game/Network/OrderManager.cs @@ -34,6 +34,8 @@ namespace OpenRA.Network public string ServerError = "Server is not responding"; public bool AuthenticationFailed = false; + public Manifest ServerMod = null; + public ExternalMod ServerExternalMod = null; public int NetFrameNumber { get; private set; } public int LocalFrameNumber; diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 4e2bbc9f4c..798d430908 100644 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -140,14 +140,20 @@ namespace OpenRA.Network Game.Mods.TryGetValue(request.Mod, out serverMod) && serverMod.Metadata.Version == request.Version) { - var replay = orderManager.Connection as ReplayConnection; - var launchCommand = replay != null ? - "Launch.Replay=" + replay.Filename : - "Launch.Connect=" + orderManager.Host + ":" + orderManager.Port; - - Game.ModData.LoadScreen.Display(); - Game.InitializeMod(request.Mod, new Arguments(launchCommand)); + // The ConnectionFailedLogic will prompt the user to switch mods + orderManager.ServerMod = serverMod; + orderManager.Connection.Dispose(); + break; + } + var externalKey = ExternalMod.MakeKey(request.Mod, request.Version); + ExternalMod external; + if ((request.Mod != mod.Id || request.Version != mod.Metadata.Version) + && Game.ExternalMods.TryGetValue(externalKey, out external)) + { + // The ConnectionFailedLogic will prompt the user to switch mods + orderManager.ServerExternalMod = external; + orderManager.Connection.Dispose(); break; } @@ -187,6 +193,7 @@ namespace OpenRA.Network case "AuthenticationError": { + // The ConnectionFailedLogic will prompt the user for the password orderManager.ServerError = order.TargetString; orderManager.AuthenticationFailed = true; break; diff --git a/OpenRA.Mods.Common/Widgets/Logic/ConnectionLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ConnectionLogic.cs index 096ab6eb7b..fae22d4c5c 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ConnectionLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ConnectionLogic.cs @@ -10,6 +10,7 @@ #endregion using System; +using OpenRA.Graphics; using OpenRA.Network; using OpenRA.Widgets; @@ -30,7 +31,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic else if (om.Connection.ConnectionState == ConnectionState.NotConnected) { CloseWindow(); - Ui.OpenWindow("CONNECTIONFAILED_PANEL", new WidgetArgs() + + var switchPanel = (om.ServerExternalMod != null || om.ServerMod != null) ? + "CONNECTION_SWITCHMOD_PANEL" : "CONNECTIONFAILED_PANEL"; + Ui.OpenWindow(switchPanel, new WidgetArgs() { { "orderManager", om }, { "onAbort", onAbort }, @@ -146,4 +150,116 @@ namespace OpenRA.Mods.Common.Widgets.Logic } } } -} \ No newline at end of file + + public class ConnectionSwitchModLogic : ChromeLogic + { + [ObjectCreator.UseCtor] + public ConnectionSwitchModLogic(Widget widget, OrderManager orderManager, Action onAbort, Action onRetry) + { + var panel = widget; + var abortButton = panel.Get("ABORT_BUTTON"); + var switchButton = panel.Get("SWITCH_BUTTON"); + + var modTitle = ""; + var modVersion = ""; + Sprite modIcon = null; + if (orderManager.ServerExternalMod != null) + { + modTitle = orderManager.ServerExternalMod.Title; + modVersion = orderManager.ServerExternalMod.Version; + modIcon = orderManager.ServerExternalMod.Icon; + } + else + { + modTitle = orderManager.ServerMod.Metadata.Title; + modVersion = orderManager.ServerMod.Metadata.Version; + modIcon = Game.Mods.Icons[orderManager.ServerMod.Id]; + } + + switchButton.OnClick = () => + { + // Note: Switching mods for directly launched replays is handled by the load screen + // and the replay browser prevents loading replays from other mods, so it doesn't look + // like this replay case is ever hit. + var replay = orderManager.Connection as ReplayConnection; + var launchCommand = replay != null ? + "Launch.Replay=" + replay.Filename : + "Launch.Connect=" + orderManager.Host + ":" + orderManager.Port; + + if (orderManager.ServerExternalMod != null) + { + Game.SwitchToExternalMod(orderManager.ServerExternalMod, new[] { launchCommand }, () => + { + orderManager.ServerError = "Failed to switch mod."; + Ui.CloseWindow(); + Ui.OpenWindow("CONNECTIONFAILED_PANEL", new WidgetArgs() + { + { "orderManager", orderManager }, + { "onAbort", onAbort }, + { "onRetry", onRetry } + }); + }); + } + else + { + Game.ModData.LoadScreen.Display(); + Game.InitializeMod(orderManager.ServerMod.Id, new Arguments(launchCommand)); + } + }; + + abortButton.Visible = onAbort != null; + abortButton.OnClick = () => { Ui.CloseWindow(); onAbort(); }; + + var width = 0; + var title = panel.GetOrNull("MOD_TITLE"); + if (title != null) + { + var font = Game.Renderer.Fonts[title.Font]; + var label = WidgetUtils.TruncateText(modTitle, title.Bounds.Width, font); + var labelWidth = font.Measure(label).X; + width = Math.Max(width, title.Bounds.X + labelWidth); + title.Bounds.Width = labelWidth; + title.GetText = () => label; + } + + var version = panel.GetOrNull("MOD_VERSION"); + if (version != null) + { + var font = Game.Renderer.Fonts[version.Font]; + var label = WidgetUtils.TruncateText(modVersion, version.Bounds.Width, font); + var labelWidth = font.Measure(label).X; + width = Math.Max(width, version.Bounds.X + labelWidth); + version.Bounds.Width = labelWidth; + version.GetText = () => label; + } + + var logo = panel.GetOrNull("MOD_ICON"); + if (logo != null && modIcon != null) + logo.GetSprite = () => modIcon; + + if (logo != null && modIcon == null) + { + // Hide the logo and center just the text + if (title != null) + title.Bounds.Offset(logo.Bounds.Left - title.Bounds.Left, 0); + + if (version != null) + version.Bounds.Offset(logo.Bounds.Left - version.Bounds.Left, 0); + + width -= logo.Bounds.Width; + } + else + { + // Add an equal logo margin on the right of the text + width += logo.Bounds.Width; + } + + var container = panel.GetOrNull("MOD_CONTAINER"); + if (container != null) + { + container.Bounds.Offset((container.Bounds.Width - width) / 2, 0); + container.Bounds.Width = width; + } + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs index 65978de228..2e734f48a8 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs @@ -91,7 +91,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic Action onRetry = password => ConnectionLogic.Connect(om.Host, om.Port, password, onConnect, onExit); - Ui.OpenWindow("CONNECTIONFAILED_PANEL", new WidgetArgs() + var switchPanel = (om.ServerExternalMod != null || om.ServerMod != null) ? + "CONNECTION_SWITCHMOD_PANEL" : "CONNECTIONFAILED_PANEL"; + Ui.OpenWindow(switchPanel, new WidgetArgs() { { "orderManager", om }, { "onAbort", onExit }, diff --git a/mods/cnc/chrome/connection.yaml b/mods/cnc/chrome/connection.yaml index 1ef3c5f73b..1527a3f0ae 100644 --- a/mods/cnc/chrome/connection.yaml +++ b/mods/cnc/chrome/connection.yaml @@ -90,3 +90,72 @@ Container@CONNECTIONFAILED_PANEL: Width: 140 Height: 35 Text: Retry + +Container@CONNECTION_SWITCHMOD_PANEL: + Logic: ConnectionSwitchModLogic + X: (WINDOW_RIGHT - WIDTH)/2 + Y: (WINDOW_BOTTOM - 90)/2 + Width: 370 + Height: 134 + Children: + Label@TITLE: + Width: PARENT_RIGHT + Y: 0-25 + Font: BigBold + Contrast: true + Align: Center + Text: Switch Mod + Background@CONNECTION_BACKGROUND: + Width: 370 + Height: 120 + Background: panel-black + Children: + Label@DESC: + Y: 15 + Width: PARENT_RIGHT + Height: 25 + Text: This server is running a different mod: + Font: Bold + Align: Center + Container@MOD_CONTAINER: + X: 0 + Y: 42 + Width: PARENT_RIGHT + Children: + RGBASprite@MOD_ICON: + Y: 4 + Width: 32 + Height: 32 + Label@MOD_TITLE: + X: 37 + Width: PARENT_RIGHT - 37 + Height: 25 + Font: Bold + Align: Left + Label@MOD_VERSION: + X: 37 + Y: 15 + Width: PARENT_RIGHT - 37 + Height: 25 + Font: Tiny + Align: Left + Label@DESC2: + Y: 80 + Width: PARENT_RIGHT + Height: 25 + Text: Switch mods and join server? + Font: Bold + Align: Center + Button@ABORT_BUTTON: + Key: escape + Y: 119 + Width: 140 + Height: 35 + Text: Abort + Button@SWITCH_BUTTON: + Key: return + X: 230 + Y: 119 + Width: 140 + Height: 35 + Text: Switch diff --git a/mods/ra/chrome/connection.yaml b/mods/ra/chrome/connection.yaml index ddebd34792..7fa93e7cb1 100644 --- a/mods/ra/chrome/connection.yaml +++ b/mods/ra/chrome/connection.yaml @@ -88,3 +88,71 @@ Background@CONNECTING_PANEL: Text: Abort Font: Bold Key: escape + +Background@CONNECTION_SWITCHMOD_PANEL: + Logic: ConnectionSwitchModLogic + X: (WINDOW_RIGHT - WIDTH)/2 + Y: (WINDOW_BOTTOM - HEIGHT)/2 + Width: 450 + Height: 191 + Children: + Label@TITLE: + X: 0 + Y: 20 + Width: 450 + Height: 25 + Align: Center + Font: Bold + Text: Switch Mod + Label@DESC: + Y: 45 + Width: PARENT_RIGHT + Height: 25 + Text: This server is running a different mod: + Font: Bold + Align: Center + Container@MOD_CONTAINER: + X: 0 + Y: 72 + Width: PARENT_RIGHT + Children: + RGBASprite@MOD_ICON: + Y: 4 + Width: 32 + Height: 32 + Label@MOD_TITLE: + X: 37 + Width: PARENT_RIGHT - 37 + Height: 25 + Font: Bold + Align: Left + Label@MOD_VERSION: + X: 37 + Y: 15 + Width: PARENT_RIGHT - 37 + Height: 25 + Font: Tiny + Align: Left + Label@DESC2: + Y: 110 + Width: PARENT_RIGHT + Height: 25 + Text: Switch mods and join server? + Font: Bold + Align: Center + Button@SWITCH_BUTTON: + X: PARENT_RIGHT - 430 + Y: PARENT_BOTTOM - 45 + Width: 160 + Height: 25 + Text: Switch + Font: Bold + Key: return + Button@ABORT_BUTTON: + X: PARENT_RIGHT - 180 + Y: PARENT_BOTTOM - 45 + Width: 160 + Height: 25 + Text: Cancel + Font: Bold + Key: escape