diff --git a/OpenRA.Game/GameInformation.cs b/OpenRA.Game/GameInformation.cs index d30175121f..7eaa02b6db 100644 --- a/OpenRA.Game/GameInformation.cs +++ b/OpenRA.Game/GameInformation.cs @@ -18,8 +18,12 @@ namespace OpenRA { public class GameInformation { + public string Mod; + public string Version; + public string MapUid; public string MapTitle; + public DateTime StartTimeUtc; // Game end timestamp (when the recoding stopped). diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 974efb686b..5d6238ef2b 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -199,6 +199,9 @@ namespace OpenRA gameInfo = new GameInformation { + Mod = Game.ModData.Manifest.Mod.Id, + Version = Game.ModData.Manifest.Mod.Version, + MapUid = Map.Uid, MapTitle = Map.Title }; diff --git a/OpenRA.Mods.Common/LoadScreens/BlankLoadScreen.cs b/OpenRA.Mods.Common/LoadScreens/BlankLoadScreen.cs index ca83696c1e..5fe22f8d8d 100644 --- a/OpenRA.Mods.Common/LoadScreens/BlankLoadScreen.cs +++ b/OpenRA.Mods.Common/LoadScreens/BlankLoadScreen.cs @@ -8,8 +8,10 @@ */ #endregion +using System; using System.Collections.Generic; using System.Linq; +using OpenRA.FileFormats; using OpenRA.FileSystem; using OpenRA.Mods.Common.Widgets.Logic; using OpenRA.Widgets; @@ -78,7 +80,10 @@ namespace OpenRA.Mods.Common.LoadScreens var replay = args != null ? args.GetValue("Launch.Replay", null) : null; if (!string.IsNullOrEmpty(replay)) { - Game.JoinReplay(replay); + var replayMeta = ReplayMetadata.Read(replay); + if (ReplayUtils.CheckReplayCompatibility(replayMeta, Game.LoadShellMap)) + Game.JoinReplay(replay); + return; } diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index d0a8aefa84..6747ef62d6 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -571,6 +571,7 @@ + diff --git a/OpenRA.Mods.Common/Widgets/ConfirmationDialogs.cs b/OpenRA.Mods.Common/Widgets/ConfirmationDialogs.cs index 09d5ac232d..0ec0ffae83 100644 --- a/OpenRA.Mods.Common/Widgets/ConfirmationDialogs.cs +++ b/OpenRA.Mods.Common/Widgets/ConfirmationDialogs.cs @@ -39,6 +39,23 @@ namespace OpenRA.Mods.Common.Widgets }; } + public static void CancelPrompt(string title, string text, Action onCancel = null, string cancelText = null) + { + var prompt = Ui.OpenWindow("CANCEL_PROMPT"); + prompt.Get("PROMPT_TITLE").GetText = () => title; + prompt.Get("PROMPT_TEXT").GetText = () => text; + + if (!string.IsNullOrEmpty(cancelText)) + prompt.Get("CANCEL_BUTTON").GetText = () => cancelText; + + prompt.Get("CANCEL_BUTTON").OnClick = () => + { + Ui.CloseWindow(); + if (onCancel != null) + onCancel(); + }; + } + public static void TextInputPrompt( string title, string prompt, string initialText, Action onAccept, Action onCancel = null, diff --git a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs index 876158aa12..f88ec0bb55 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs @@ -32,11 +32,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic Dictionary selectedSpawns; ReplayMetadata selectedReplay; + Action onStart; + [ObjectCreator.UseCtor] public ReplayBrowserLogic(Widget widget, Action onExit, Action onStart) { panel = widget; + this.onStart = onStart; + playerList = panel.Get("PLAYER_LIST"); playerHeader = playerList.Get("HEADER"); playerTemplate = playerList.Get("TEMPLATE"); @@ -73,7 +77,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var watch = panel.Get("WATCH_BUTTON"); watch.IsDisabled = () => selectedReplay == null || selectedReplay.GameInfo.MapPreview.Status != MapStatus.Available; - watch.OnClick = () => { WatchReplay(); onStart(); }; + watch.OnClick = () => { WatchReplay(); }; panel.Get("REPLAY_INFO").IsVisible = () => selectedReplay != null; @@ -624,11 +628,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic void WatchReplay() { - if (selectedReplay != null && selectedReplay.GameInfo.MapPreview.Status == MapStatus.Available) + Action startReplay = () => { Game.JoinReplay(selectedReplay.FilePath); Ui.CloseWindow(); - } + onStart(); + }; + + if (selectedReplay != null && ReplayUtils.CheckReplayCompatibility(selectedReplay)) + startReplay(); } void AddReplay(ReplayMetadata replay, ScrollItemWidget template) diff --git a/OpenRA.Mods.Common/Widgets/Logic/ReplayUtils.cs b/OpenRA.Mods.Common/Widgets/Logic/ReplayUtils.cs new file mode 100644 index 0000000000..e4720127c2 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/ReplayUtils.cs @@ -0,0 +1,55 @@ +#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 OpenRA.FileFormats; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public static class ReplayUtils + { + static readonly Action DoNothing = () => { }; + + public static bool CheckReplayCompatibility(ReplayMetadata replayMeta, Action onCancel = null) + { + if (onCancel == null) + onCancel = DoNothing; + + var mod = replayMeta.GameInfo.Mod; + if (mod == null) + return IncompatibleReplayDialog("an unknown mod", mod, onCancel); + + var version = replayMeta.GameInfo.Version; + if (version == null) + return IncompatibleReplayDialog("an unknown version", version, onCancel); + + var allMods = ModMetadata.AllMods; + if (!allMods.ContainsKey(mod)) + return IncompatibleReplayDialog("an unavailable mod", mod, onCancel); + else if (allMods[mod].Version != version) + return IncompatibleReplayDialog("an incompatible version", version, onCancel); + + if (replayMeta.GameInfo.MapPreview.Status != MapStatus.Available) + return IncompatibleReplayDialog("an unavailable map", replayMeta.GameInfo.MapUid, onCancel); + else + return true; + } + + static bool IncompatibleReplayDialog(string type, string name, Action onCancel) + { + var error = "It was recorded with " + type; + error += string.IsNullOrEmpty(name) ? "." : ":\n{0}".F(name); + + ConfirmationDialogs.CancelPrompt("Incompatible Replay", error, onCancel); + + return false; + } + } +} \ No newline at end of file diff --git a/mods/cnc/chrome/dialogs.yaml b/mods/cnc/chrome/dialogs.yaml index 54c24f9260..c76c96b8dc 100644 --- a/mods/cnc/chrome/dialogs.yaml +++ b/mods/cnc/chrome/dialogs.yaml @@ -155,6 +155,35 @@ Container@CONFIRM_PROMPT: Height: 35 Text: Confirm +Container@CANCEL_PROMPT: + X: (WINDOW_RIGHT - WIDTH)/2 + Y: (WINDOW_BOTTOM - 90)/2 + Width: 370 + Height: 125 + Children: + Label@PROMPT_TITLE: + Width: PARENT_RIGHT + Y: 0-25 + Font: BigBold + Contrast: true + Align: Center + Background@bg: + Width: 370 + Height: 90 + Background: panel-black + Children: + Label@PROMPT_TEXT: + Y: (PARENT_BOTTOM-HEIGHT)/2 + Width: PARENT_RIGHT + Height: 25 + Font: Bold + Align: Center + Button@CANCEL_BUTTON: + Key: escape + Y: 89 + Width: 140 + Height: 35 + Text: Cancel Container@TEXT_INPUT_PROMPT: X: (WINDOW_RIGHT - WIDTH)/2 diff --git a/mods/ra/chrome/confirmation-dialogs.yaml b/mods/ra/chrome/confirmation-dialogs.yaml index 35522917d2..a7f59f4751 100644 --- a/mods/ra/chrome/confirmation-dialogs.yaml +++ b/mods/ra/chrome/confirmation-dialogs.yaml @@ -33,6 +33,33 @@ Background@CONFIRM_PROMPT: Font: Bold Key: escape +Background@CANCEL_PROMPT: + X: (WINDOW_RIGHT - WIDTH)/2 + Y: (WINDOW_BOTTOM - 90)/2 + Width: 370 + Height: 175 + Children: + Label@PROMPT_TITLE: + Width: PARENT_RIGHT + Y: 20 + Height: 25 + Font: Bold + Align: Center + Label@PROMPT_TEXT: + X: 15 + Y: 50 + Width: PARENT_RIGHT - 30 + Height: 65 + Align: Center + Button@CANCEL_BUTTON: + X: PARENT_RIGHT/2 - WIDTH/2 + Y: PARENT_BOTTOM - 45 + Width: 160 + Height: 25 + Text: Cancel + Font: Bold + Key: escape + Background@TEXT_INPUT_PROMPT: X: (WINDOW_RIGHT - WIDTH)/2 Y: (WINDOW_BOTTOM - HEIGHT)/2