From 1a9dfc089364e195e0c36f64e7933ca1fbeb4217 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Fri, 2 Apr 2021 14:11:45 +0200 Subject: [PATCH] Refactor GameSpeed setting *Remove internal GameSpeed defaults Enforce setting values explicitly all the time Require definition of a DefaultSpeed *Remove Global.Timestep default *Remove the hacky Timestep/OrderLatency setting via LobbyInfo *Fix shellmaps ignoring mod-defined gamespeeds *Make DateTimeGlobal use the MapOptions gamespeed --- OpenRA.Game/Game.cs | 21 ++++++-- OpenRA.Game/GameSpeed.cs | 33 ++++++++++-- OpenRA.Game/Network/ReplayConnection.cs | 10 +++- OpenRA.Game/Network/Session.cs | 2 - OpenRA.Game/Network/UnitOrders.cs | 11 ---- OpenRA.Game/Server/Server.cs | 4 -- OpenRA.Game/World.cs | 20 +++++++- .../Scripting/Global/DateTimeGlobal.cs | 6 ++- .../ServerTraits/LobbyCommands.cs | 14 ------ .../BotModuleLogic/BaseBuilderQueueManager.cs | 2 +- .../Traits/Player/PlayerStatistics.cs | 6 +-- .../Traits/Player/TimeLimitManager.cs | 7 +-- OpenRA.Mods.Common/Traits/World/MapOptions.cs | 20 +++----- .../Widgets/Logic/Ingame/GameTimerLogic.cs | 15 ++---- .../Logic/Ingame/ReplayControlBarLogic.cs | 24 ++++----- .../Widgets/ObserverProductionIconsWidget.cs | 5 +- .../ObserverSupportPowerIconsWidget.cs | 9 +--- .../Widgets/SupportPowerTimerWidget.cs | 10 +--- mods/cnc/mod.yaml | 50 ++++++++++--------- mods/d2k/mod.yaml | 50 ++++++++++--------- mods/ra/mod.yaml | 50 ++++++++++--------- mods/ts/mod.yaml | 50 ++++++++++--------- 22 files changed, 214 insertions(+), 205 deletions(-) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index caec6b6a7d..52151924a6 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -162,8 +162,12 @@ namespace OpenRA using (new PerfTimer("PrepareMap")) map = ModData.PrepareMap(mapUID); + using (new PerfTimer("NewWorld")) + { OrderManager.World = new World(ModData, map, OrderManager, type); + OrderManager.FramesAhead = OrderManager.World.OrderLatency; + } OrderManager.World.GameOver += FinishBenchmark; @@ -581,7 +585,11 @@ namespace OpenRA Cursor.Tick(); } - var worldTimestep = world == null ? Ui.Timestep : world.IsLoadingGameSave ? 1 : world.Timestep; + var worldTimestep = world == null ? Ui.Timestep : + world.IsLoadingGameSave ? 1 : + world.IsReplay ? world.ReplayTimestep : + world.Timestep; + var worldTickDelta = tick - orderManager.LastTickTime; if (worldTimestep != 0 && worldTickDelta >= worldTimestep) { @@ -776,9 +784,14 @@ namespace OpenRA while (state == RunStatus.Running) { - // Ideal time between logic updates. Timestep = 0 means the game is paused - // but we still call LogicTick() because it handles pausing internally. - var logicInterval = worldRenderer != null && worldRenderer.World.Timestep != 0 ? worldRenderer.World.Timestep : Ui.Timestep; + var logicInterval = Ui.Timestep; + var logicWorld = worldRenderer?.World; + + // ReplayTimestep = 0 means the replay is paused: we need to keep logicInterval as UI.Timestep to avoid breakage + if (logicWorld != null && !(logicWorld.IsReplay && logicWorld.ReplayTimestep == 0)) + logicInterval = logicWorld.IsLoadingGameSave ? 1 : + logicWorld.IsReplay ? logicWorld.ReplayTimestep : + logicWorld.Timestep; // Ideal time between screen updates var maxFramerate = Settings.Graphics.CapFramerate ? Settings.Graphics.MaxFramerate.Clamp(1, 1000) : 1000; diff --git a/OpenRA.Game/GameSpeed.cs b/OpenRA.Game/GameSpeed.cs index f10c071b6b..97ccc04630 100644 --- a/OpenRA.Game/GameSpeed.cs +++ b/OpenRA.Game/GameSpeed.cs @@ -10,26 +10,49 @@ #endregion using System.Collections.Generic; +using System.Linq; namespace OpenRA { public class GameSpeed { - public readonly string Name = "Default"; - public readonly int Timestep = 40; - public readonly int OrderLatency = 3; + [FieldLoader.Require] + public readonly string Name; + + [FieldLoader.Require] + public readonly int Timestep; + + [FieldLoader.Require] + public readonly int OrderLatency; } public class GameSpeeds : IGlobalModData { + [FieldLoader.Require] + public readonly string DefaultSpeed; + [FieldLoader.LoadUsing(nameof(LoadSpeeds))] public readonly Dictionary Speeds; static object LoadSpeeds(MiniYaml y) { var ret = new Dictionary(); - foreach (var node in y.Nodes) - ret.Add(node.Key, FieldLoader.Load(node.Value)); + var speedsNode = y.Nodes.FirstOrDefault(n => n.Key == "Speeds"); + if (speedsNode == null) + throw new YamlException("Error parsing GameSpeeds: Missing Speeds node!"); + + foreach (var node in speedsNode.Value.Nodes) + { + try + { + ret.Add(node.Key, FieldLoader.Load(node.Value)); + } + catch (FieldLoader.MissingFieldsException e) + { + var label = e.Missing.Length > 1 ? "Required properties missing" : "Required property missing"; + throw new YamlException("Error parsing GameSpeed {0}: {1}: {2}".F(node.Key, label, e.Missing.JoinWith(", "))); + } + } return ret; } diff --git a/OpenRA.Game/Network/ReplayConnection.cs b/OpenRA.Game/Network/ReplayConnection.cs index 81faa1a63a..b43a819c8a 100644 --- a/OpenRA.Game/Network/ReplayConnection.cs +++ b/OpenRA.Game/Network/ReplayConnection.cs @@ -27,7 +27,10 @@ namespace OpenRA.Network Queue chunks = new Queue(); List sync = new List(); + + readonly int orderLatency; int ordersFrame; + Dictionary lastClientsFrame = new Dictionary(); public int LocalClientId => -1; @@ -122,7 +125,10 @@ namespace OpenRA.Network } } - ordersFrame = LobbyInfo.GlobalSettings.OrderLatency; + var gameSpeeds = Game.ModData.Manifest.Get(); + var gameSpeedName = LobbyInfo.GlobalSettings.OptionOrDefault("gamespeed", gameSpeeds.DefaultSpeed); + orderLatency = gameSpeeds.Speeds[gameSpeedName].OrderLatency; + ordersFrame = orderLatency; } // Do nothing: ignore locally generated orders @@ -137,7 +143,7 @@ namespace OpenRA.Network sync.Add(ms.GetBuffer()); // Store the current frame so Receive() can return the next chunk of orders. - ordersFrame = frame + LobbyInfo.GlobalSettings.OrderLatency; + ordersFrame = frame + orderLatency; } public void Receive(Action packetFn) diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 22561f2cad..86face63dd 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -222,8 +222,6 @@ namespace OpenRA.Network { public string ServerName; public string Map; - public int Timestep = 40; - public int OrderLatency = 3; // net tick frames (x 120 = ms) public int RandomSeed = 0; public bool AllowSpectators = true; public string GameUid; diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 3a8ddca1b8..3a303f5925 100644 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -254,7 +254,6 @@ namespace OpenRA.Network case "SyncInfo": { orderManager.LobbyInfo = Session.Deserialize(order.TargetString); - SetOrderLag(orderManager); Game.SyncLobbyInfo(); break; } @@ -304,7 +303,6 @@ namespace OpenRA.Network orderManager.LobbyInfo.GlobalSettings = Session.Global.Deserialize(node.Value); } - SetOrderLag(orderManager); Game.SyncLobbyInfo(); break; } @@ -354,14 +352,5 @@ namespace OpenRA.Network if (world.OrderValidators.All(vo => vo.OrderValidation(orderManager, world, clientId, order))) order.Subject.ResolveOrder(order); } - - static void SetOrderLag(OrderManager o) - { - if (o.FramesAhead != o.LobbyInfo.GlobalSettings.OrderLatency && !o.GameStarted) - { - o.FramesAhead = o.LobbyInfo.GlobalSettings.OrderLatency; - Log.Write("server", "Order lag is now {0} frames.", o.LobbyInfo.GlobalSettings.OrderLatency); - } - } } } diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 964f39b92e..de55384d36 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -1200,10 +1200,6 @@ namespace OpenRA.Server DropClient(c); } - // HACK: Turn down the latency if there is only one real player - if (LobbyInfo.NonBotClients.Count() == 1) - LobbyInfo.GlobalSettings.OrderLatency = 1; - // Enable game saves for singleplayer missions only // TODO: Enable for multiplayer (non-dedicated servers only) once the lobby UI has been created LobbyInfo.GlobalSettings.GameSavesEnabled = Type != ServerType.Dedicated && LobbyInfo.NonBotClients.Count() == 1; diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 3afaa404e7..3fbc21609e 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -36,7 +36,12 @@ namespace OpenRA readonly Queue> frameEndActions = new Queue>(); - public int Timestep; + public readonly GameSpeed GameSpeed; + + public readonly int Timestep; + public readonly int OrderLatency; + + public int ReplayTimestep; internal readonly OrderManager OrderManager; public Session LobbyInfo => OrderManager.LobbyInfo; @@ -180,7 +185,18 @@ namespace OpenRA OrderManager = orderManager; orderGenerator = new UnitOrderGenerator(); Map = map; - Timestep = orderManager.LobbyInfo.GlobalSettings.Timestep; + + var gameSpeeds = modData.Manifest.Get(); + var gameSpeedName = orderManager.LobbyInfo.GlobalSettings.OptionOrDefault("gamespeed", gameSpeeds.DefaultSpeed); + GameSpeed = gameSpeeds.Speeds[gameSpeedName]; + + Timestep = ReplayTimestep = GameSpeed.Timestep; + OrderLatency = GameSpeed.OrderLatency; + + // HACK: Turn down the latency if there is only one real player/spectator + if (orderManager.LobbyInfo.NonBotClients.Count() == 1) + OrderLatency = 1; + SharedRandom = new MersenneTwister(orderManager.LobbyInfo.GlobalSettings.RandomSeed); LocalRandom = new MersenneTwister(); diff --git a/OpenRA.Mods.Common/Scripting/Global/DateTimeGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/DateTimeGlobal.cs index 696842a44e..56151c0354 100644 --- a/OpenRA.Mods.Common/Scripting/Global/DateTimeGlobal.cs +++ b/OpenRA.Mods.Common/Scripting/Global/DateTimeGlobal.cs @@ -20,11 +20,15 @@ namespace OpenRA.Mods.Common.Scripting public class DateGlobal : ScriptGlobal { readonly TimeLimitManager tlm; + readonly int ticksPerSecond; public DateGlobal(ScriptContext context) : base(context) { tlm = context.World.WorldActor.TraitOrDefault(); + var gameSpeeds = Game.ModData.Manifest.Get(); + var defaultGameSpeed = gameSpeeds.Speeds[gameSpeeds.DefaultSpeed]; + ticksPerSecond = 1000 / defaultGameSpeed.Timestep; } [Desc("True on the 31st of October.")] @@ -36,7 +40,7 @@ namespace OpenRA.Mods.Common.Scripting [Desc("Converts the number of seconds into game time (ticks).")] public int Seconds(int seconds) { - return seconds * 25; + return seconds * ticksPerSecond; } [Desc("Converts the number of minutes into game time (ticks).")] diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index dde2984eb9..d254c578ec 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -563,13 +563,6 @@ namespace OpenRA.Mods.Common.Server oo.Value = oo.PreferredValue = split[1]; - if (option.Id == "gamespeed") - { - var speed = server.ModData.Manifest.Get().Speeds[oo.Value]; - server.LobbyInfo.GlobalSettings.Timestep = speed.Timestep; - server.LobbyInfo.GlobalSettings.OrderLatency = speed.OrderLatency; - } - server.SyncLobbyGlobalSettings(); server.SendMessage(option.ValueChangedMessage(client.Name, split[1])); @@ -1080,13 +1073,6 @@ namespace OpenRA.Mods.Common.Server state.Value = value; state.PreferredValue = preferredValue; gs.LobbyOptions[o.Id] = state; - - if (o.Id == "gamespeed") - { - var speed = server.ModData.Manifest.Get().Speeds[value]; - gs.Timestep = speed.Timestep; - gs.OrderLatency = speed.OrderLatency; - } } } } diff --git a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs index f4635ce6b4..63cdb92f36 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs @@ -112,7 +112,7 @@ namespace OpenRA.Mods.Common.Traits var randomFactor = world.LocalRandom.Next(0, baseBuilder.Info.StructureProductionRandomBonusDelay); // Needs to be at least 4 * OrderLatency because otherwise the AI frequently duplicates build orders (i.e. makes the same build decision twice) - waitTicks = active ? 4 * world.LobbyInfo.GlobalSettings.OrderLatency + baseBuilder.Info.StructureProductionActiveDelay + randomFactor + waitTicks = active ? 4 * world.OrderLatency + baseBuilder.Info.StructureProductionActiveDelay + randomFactor : baseBuilder.Info.StructureProductionInactiveDelay + randomFactor; } diff --git a/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs b/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs index d140e6f9d4..fcb2f45a28 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs @@ -58,7 +58,6 @@ namespace OpenRA.Mods.Common.Traits int lastIncome; int lastIncomeTick; int ticks; - int replayTimestep; bool armyGraphDisabled; bool incomeGraphDisabled; @@ -81,7 +80,7 @@ namespace OpenRA.Mods.Common.Traits { ticks++; - var timestep = self.World.IsReplay ? replayTimestep : self.World.Timestep; + var timestep = self.World.Timestep; if (ticks * timestep >= 30000) { ticks = 0; @@ -124,9 +123,6 @@ namespace OpenRA.Mods.Common.Traits public void WorldLoaded(World w, WorldRenderer wr) { - if (w.IsReplay) - replayTimestep = w.WorldActor.Trait().GameSpeed.Timestep; - if (!armyGraphDisabled) ArmySamples.Add(ArmyValue); diff --git a/OpenRA.Mods.Common/Traits/Player/TimeLimitManager.cs b/OpenRA.Mods.Common/Traits/Player/TimeLimitManager.cs index e9c5096540..e42cdfd82c 100644 --- a/OpenRA.Mods.Common/Traits/Player/TimeLimitManager.cs +++ b/OpenRA.Mods.Common/Traits/Player/TimeLimitManager.cs @@ -93,6 +93,7 @@ namespace OpenRA.Mods.Common.Traits public class TimeLimitManager : INotifyTimeLimit, ITick, IWorldLoaded { readonly TimeLimitManagerInfo info; + readonly int ticksPerSecond; MapOptions mapOptions; LabelWidget countdownLabel; CachedTransform countdown; @@ -105,13 +106,14 @@ namespace OpenRA.Mods.Common.Traits { this.info = info; Notification = info.Notification; + ticksPerSecond = 1000 / self.World.Timestep; var tl = self.World.LobbyInfo.GlobalSettings.OptionOrDefault("timelimit", info.TimeLimitDefault.ToString()); if (!int.TryParse(tl, out TimeLimit)) TimeLimit = info.TimeLimitDefault; // Convert from minutes to ticks - TimeLimit *= 60 * (1000 / self.World.Timestep); + TimeLimit *= 60 * ticksPerSecond; } void IWorldLoaded.WorldLoaded(World w, OpenRA.Graphics.WorldRenderer wr) @@ -124,7 +126,7 @@ namespace OpenRA.Mods.Common.Traits if (countdownLabel != null) { countdown = new CachedTransform(t => - info.CountdownText.F(WidgetUtils.FormatTime(t, true, w.IsReplay ? mapOptions.GameSpeed.Timestep : w.Timestep))); + info.CountdownText.F(WidgetUtils.FormatTime(t, true, w.Timestep))); countdownLabel.GetText = () => countdown.Update(ticksRemaining); } } @@ -134,7 +136,6 @@ namespace OpenRA.Mods.Common.Traits if (TimeLimit <= 0) return; - var ticksPerSecond = 1000 / (self.World.IsReplay ? mapOptions.GameSpeed.Timestep : self.World.Timestep); ticksRemaining = TimeLimit - self.World.WorldTick; if (ticksRemaining == 0) diff --git a/OpenRA.Mods.Common/Traits/World/MapOptions.cs b/OpenRA.Mods.Common/Traits/World/MapOptions.cs index 7116c7eb7f..51eb449e66 100644 --- a/OpenRA.Mods.Common/Traits/World/MapOptions.cs +++ b/OpenRA.Mods.Common/Traits/World/MapOptions.cs @@ -60,8 +60,8 @@ namespace OpenRA.Mods.Common.Traits [Desc("Description of the game speed option in the lobby.")] public readonly string GameSpeedDropdownDescription = "Change the rate at which time passes"; - [Desc("Default game speed.")] - public readonly string GameSpeed = "default"; + [Desc("Default game speed (leave empty to use the default defined in mod.yaml).")] + public readonly string GameSpeed = null; [Desc("Prevent the game speed from being changed in the lobby.")] public readonly bool GameSpeedDropdownLocked = false; @@ -84,18 +84,18 @@ namespace OpenRA.Mods.Common.Traits yield return new LobbyOption("techlevel", TechLevelDropdownLabel, TechLevelDropdownDescription, TechLevelDropdownVisible, TechLevelDropdownDisplayOrder, techLevels, TechLevel, TechLevelDropdownLocked); - var gameSpeeds = Game.ModData.Manifest.Get().Speeds - .ToDictionary(s => s.Key, s => s.Value.Name); + var gameSpeeds = Game.ModData.Manifest.Get(); + var speeds = gameSpeeds.Speeds.ToDictionary(s => s.Key, s => s.Value.Name); - // NOTE: The server hardcodes special-case logic for this option id + // NOTE: This is just exposing the UI, the backend logic for this option is hardcoded in World yield return new LobbyOption("gamespeed", GameSpeedDropdownLabel, GameSpeedDropdownDescription, GameSpeedDropdownVisible, GameSpeedDropdownDisplayOrder, - gameSpeeds, GameSpeed, GameSpeedDropdownLocked); + speeds, GameSpeed ?? gameSpeeds.DefaultSpeed, GameSpeedDropdownLocked); } void IRulesetLoaded.RulesetLoaded(Ruleset rules, ActorInfo info) { var gameSpeeds = Game.ModData.Manifest.Get().Speeds; - if (!gameSpeeds.ContainsKey(GameSpeed)) + if (GameSpeed != null && !gameSpeeds.ContainsKey(GameSpeed)) throw new YamlException("Invalid default game speed '{0}'.".F(GameSpeed)); } @@ -108,7 +108,6 @@ namespace OpenRA.Mods.Common.Traits public bool ShortGame { get; private set; } public string TechLevel { get; private set; } - public GameSpeed GameSpeed { get; private set; } public MapOptions(MapOptionsInfo info) { @@ -122,11 +121,6 @@ namespace OpenRA.Mods.Common.Traits TechLevel = self.World.LobbyInfo.GlobalSettings .OptionOrDefault("techlevel", info.TechLevel); - - var speed = self.World.LobbyInfo.GlobalSettings - .OptionOrDefault("gamespeed", info.GameSpeed); - - GameSpeed = Game.ModData.Manifest.Get().Speeds[speed]; } } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs index ad83c2b72a..aa19337758 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameTimerLogic.cs @@ -26,27 +26,22 @@ namespace OpenRA.Mods.Common.Widgets.Logic var tlm = world.WorldActor.TraitOrDefault(); var startTick = Ui.LastTickTime; - Func shouldShowStatus = () => (world.Paused || world.Timestep != world.LobbyInfo.GlobalSettings.Timestep) + Func shouldShowStatus = () => (world.Paused || world.ReplayTimestep != world.Timestep) && (Ui.LastTickTime - startTick) / 1000 % 2 == 0; Func statusText = () => { - if (world.Paused || world.Timestep == 0) + if (world.Paused || world.ReplayTimestep == 0) return "Paused"; - if (world.Timestep == 1) + if (world.ReplayTimestep == 1) return "Max Speed"; - return "{0}% Speed".F(world.LobbyInfo.GlobalSettings.Timestep * 100 / world.Timestep); + return "{0}% Speed".F(world.Timestep * 100 / world.ReplayTimestep); }; if (timer != null) { - // Timers in replays should be synced to the effective game time, not the playback time. - var timestep = world.Timestep; - if (world.IsReplay) - timestep = world.WorldActor.Trait().GameSpeed.Timestep; - timer.GetText = () => { if (status == null && shouldShowStatus()) @@ -54,7 +49,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var timeLimit = tlm?.TimeLimit ?? 0; var displayTick = timeLimit > 0 ? timeLimit - world.WorldTick : world.WorldTick; - return WidgetUtils.FormatTime(Math.Max(0, displayTick), timestep); + return WidgetUtils.FormatTime(Math.Max(0, displayTick), world.Timestep); }; } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ReplayControlBarLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ReplayControlBarLogic.cs index 7f19db142a..7a81d13b1e 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ReplayControlBarLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ReplayControlBarLogic.cs @@ -46,12 +46,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic var originalTimestep = world.Timestep; var pauseButton = widget.Get("BUTTON_PAUSE"); - pauseButton.IsVisible = () => world.Timestep != 0 && orderManager.NetFrameNumber < replayNetTicks; - pauseButton.OnClick = () => world.Timestep = 0; + pauseButton.IsVisible = () => world.ReplayTimestep != 0 && orderManager.NetFrameNumber < replayNetTicks; + pauseButton.OnClick = () => world.ReplayTimestep = 0; var playButton = widget.Get("BUTTON_PLAY"); - playButton.IsVisible = () => world.Timestep == 0 || orderManager.NetFrameNumber >= replayNetTicks; - playButton.OnClick = () => world.Timestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); + playButton.IsVisible = () => world.ReplayTimestep == 0 || orderManager.NetFrameNumber >= replayNetTicks; + playButton.OnClick = () => world.ReplayTimestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); playButton.IsDisabled = () => orderManager.NetFrameNumber >= replayNetTicks; var slowButton = widget.Get("BUTTON_SLOW"); @@ -60,8 +60,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic slowButton.OnClick = () => { speed = PlaybackSpeed.Slow; - if (world.Timestep != 0) - world.Timestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); + if (world.ReplayTimestep != 0) + world.ReplayTimestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); }; var normalSpeedButton = widget.Get("BUTTON_REGULAR"); @@ -70,8 +70,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic normalSpeedButton.OnClick = () => { speed = PlaybackSpeed.Regular; - if (world.Timestep != 0) - world.Timestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); + if (world.ReplayTimestep != 0) + world.ReplayTimestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); }; var fastButton = widget.Get("BUTTON_FAST"); @@ -80,8 +80,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic fastButton.OnClick = () => { speed = PlaybackSpeed.Fast; - if (world.Timestep != 0) - world.Timestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); + if (world.ReplayTimestep != 0) + world.ReplayTimestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); }; var maximumButton = widget.Get("BUTTON_MAXIMUM"); @@ -90,8 +90,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic maximumButton.OnClick = () => { speed = PlaybackSpeed.Maximum; - if (world.Timestep != 0) - world.Timestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); + if (world.ReplayTimestep != 0) + world.ReplayTimestep = (int)Math.Ceiling(originalTimestep * multipliers[speed]); }; } } diff --git a/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs b/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs index 09c9018d63..b9b3bcef22 100644 --- a/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs @@ -27,7 +27,6 @@ namespace OpenRA.Mods.Common.Widgets public Func GetPlayer; readonly World world; readonly WorldRenderer worldRenderer; - readonly int timestep; public int IconWidth = 32; public int IconHeight = 24; @@ -56,7 +55,6 @@ namespace OpenRA.Mods.Common.Widgets this.world = world; this.worldRenderer = worldRenderer; clocks = new Dictionary(); - timestep = world.IsReplay ? world.WorldActor.Trait().GameSpeed.Timestep : world.Timestep; GetTooltipIcon = () => TooltipIcon; tooltipContainer = Exts.Lazy(() => Ui.Root.Get(TooltipContainer)); @@ -69,7 +67,6 @@ namespace OpenRA.Mods.Common.Widgets GetPlayer = other.GetPlayer; world = other.world; worldRenderer = other.worldRenderer; - timestep = other.timestep; clocks = other.clocks; IconWidth = other.IconWidth; @@ -198,7 +195,7 @@ namespace OpenRA.Mods.Common.Widgets foreach (var icon in productionIcons) { var current = icon.Queued.First(); - var text = GetOverlayForItem(current, timestep); + var text = GetOverlayForItem(current, world.Timestep); tiny.DrawTextWithContrast(text, icon.Pos + new float2(16, 12) - new float2(tiny.Measure(text).X / 2, 0), Color.White, Color.Black, 1); diff --git a/OpenRA.Mods.Common/Widgets/ObserverSupportPowerIconsWidget.cs b/OpenRA.Mods.Common/Widgets/ObserverSupportPowerIconsWidget.cs index daea82b545..f90471e8d6 100644 --- a/OpenRA.Mods.Common/Widgets/ObserverSupportPowerIconsWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ObserverSupportPowerIconsWidget.cs @@ -26,7 +26,6 @@ namespace OpenRA.Mods.Common.Widgets readonly World world; readonly WorldRenderer worldRenderer; readonly Dictionary clocks; - readonly int timestep; readonly Lazy tooltipContainer; @@ -55,11 +54,6 @@ namespace OpenRA.Mods.Common.Widgets this.worldRenderer = worldRenderer; clocks = new Dictionary(); - // Timers in replays should be synced to the effective game time, not the playback time. - timestep = world.Timestep; - if (world.IsReplay) - timestep = world.WorldActor.Trait().GameSpeed.Timestep; - tooltipContainer = Exts.Lazy(() => Ui.Root.Get(TooltipContainer)); } @@ -72,7 +66,6 @@ namespace OpenRA.Mods.Common.Widgets world = other.world; worldRenderer = other.worldRenderer; clocks = other.clocks; - timestep = other.timestep; IconWidth = other.IconWidth; IconHeight = other.IconHeight; @@ -146,7 +139,7 @@ namespace OpenRA.Mods.Common.Widgets var tiny = Game.Renderer.Fonts["Tiny"]; foreach (var icon in supportPowerIconsIcons) { - var text = GetOverlayForItem(icon.Power, timestep); + var text = GetOverlayForItem(icon.Power, world.Timestep); tiny.DrawTextWithContrast(text, icon.Pos + new float2(16, 12) - new float2(tiny.Measure(text).X / 2, 0), Color.White, Color.Black, 1); diff --git a/OpenRA.Mods.Common/Widgets/SupportPowerTimerWidget.cs b/OpenRA.Mods.Common/Widgets/SupportPowerTimerWidget.cs index 40c1b2f411..1dc6770be0 100644 --- a/OpenRA.Mods.Common/Widgets/SupportPowerTimerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/SupportPowerTimerWidget.cs @@ -25,7 +25,6 @@ namespace OpenRA.Mods.Common.Widgets public readonly TextAlign Align = TextAlign.Left; public readonly TimerOrder Order = TimerOrder.Descending; - readonly int timestep; readonly IEnumerable powers; readonly Color bgDark, bgLight; (string Text, Color Color)[] texts; @@ -38,11 +37,6 @@ namespace OpenRA.Mods.Common.Widgets .SelectMany(s => s.Trait.Powers.Values) .Where(p => p.Instances.Any() && p.Info.DisplayTimerRelationships != PlayerRelationship.None && !p.Disabled); - // Timers in replays should be synced to the effective game time, not the playback time. - timestep = world.Timestep; - if (world.IsReplay) - timestep = world.WorldActor.Trait().GameSpeed.Timestep; - bgDark = ChromeMetrics.Get("TextContrastColorDark"); bgLight = ChromeMetrics.Get("TextContrastColorLight"); } @@ -58,9 +52,9 @@ namespace OpenRA.Mods.Common.Widgets texts = displayedPowers.Select(p => { - var time = WidgetUtils.FormatTime(p.RemainingTicks, false, timestep); - var text = Format.F(p.Info.Description, time); var self = p.Instances[0].Self; + var time = WidgetUtils.FormatTime(p.RemainingTicks, false, self.World.Timestep); + var text = Format.F(p.Info.Description, time); var playerColor = self.Owner.Color; if (Game.Settings.Game.UsePlayerStanceColors) diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index 6973cda1b1..c8dbb076a7 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -236,30 +236,32 @@ AssetBrowser: SupportedExtensions: .shp, .tem, .des, .sno, .jun, .vqa, .wsa GameSpeeds: - slowest: - Name: Slowest - Timestep: 80 - OrderLatency: 2 - slower: - Name: Slower - Timestep: 50 - OrderLatency: 3 - default: - Name: Normal - Timestep: 40 - OrderLatency: 3 - fast: - Name: Fast - Timestep: 35 - OrderLatency: 4 - faster: - Name: Faster - Timestep: 30 - OrderLatency: 4 - fastest: - Name: Fastest - Timestep: 20 - OrderLatency: 6 + DefaultSpeed: default + Speeds: + slowest: + Name: Slowest + Timestep: 80 + OrderLatency: 2 + slower: + Name: Slower + Timestep: 50 + OrderLatency: 3 + default: + Name: Normal + Timestep: 40 + OrderLatency: 3 + fast: + Name: Fast + Timestep: 35 + OrderLatency: 4 + faster: + Name: Faster + Timestep: 30 + OrderLatency: 4 + fastest: + Name: Fastest + Timestep: 20 + OrderLatency: 6 ColorValidator: TeamColorPresets: f70606, ff7a22, f8d3b3, f8e947, 94b319, f335a0, a64d6c, ce08f9, f5b2db, 12b572, 502048, 1d06f7, 328dff, 78dbf8, cef6b1, 391d1d diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index 2d0ced9b05..541db6d3d5 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -211,30 +211,32 @@ AssetBrowser: SupportedExtensions: .shp, .r8, .vqa GameSpeeds: - slowest: - Name: Slowest - Timestep: 80 - OrderLatency: 2 - slower: - Name: Slower - Timestep: 50 - OrderLatency: 3 - default: - Name: Normal - Timestep: 40 - OrderLatency: 3 - fast: - Name: Fast - Timestep: 35 - OrderLatency: 4 - faster: - Name: Faster - Timestep: 30 - OrderLatency: 4 - fastest: - Name: Fastest - Timestep: 20 - OrderLatency: 6 + DefaultSpeed: default + Speeds: + slowest: + Name: Slowest + Timestep: 80 + OrderLatency: 2 + slower: + Name: Slower + Timestep: 50 + OrderLatency: 3 + default: + Name: Normal + Timestep: 40 + OrderLatency: 3 + fast: + Name: Fast + Timestep: 35 + OrderLatency: 4 + faster: + Name: Faster + Timestep: 30 + OrderLatency: 4 + fastest: + Name: Fastest + Timestep: 20 + OrderLatency: 6 ColorValidator: TeamColorPresets: 9023cd, f53333, ffae00, fff830, 87f506, f872ad, da06f3, ddb8ff, def7b2, 39c46f, 200738, 280df6, 2f86f2, 76d2f8, 498221, 392929 diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index bc74f670d6..16fc371d99 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -241,30 +241,32 @@ AssetBrowser: SupportedExtensions: .shp, .tmp, .tem, .des, .sno, .int, .vqa, .wsa GameSpeeds: - slowest: - Name: Slowest - Timestep: 80 - OrderLatency: 2 - slower: - Name: Slower - Timestep: 50 - OrderLatency: 3 - default: - Name: Normal - Timestep: 40 - OrderLatency: 3 - fast: - Name: Fast - Timestep: 35 - OrderLatency: 4 - faster: - Name: Faster - Timestep: 30 - OrderLatency: 4 - fastest: - Name: Fastest - Timestep: 20 - OrderLatency: 6 + DefaultSpeed: default + Speeds: + slowest: + Name: Slowest + Timestep: 80 + OrderLatency: 2 + slower: + Name: Slower + Timestep: 50 + OrderLatency: 3 + default: + Name: Normal + Timestep: 40 + OrderLatency: 3 + fast: + Name: Fast + Timestep: 35 + OrderLatency: 4 + faster: + Name: Faster + Timestep: 30 + OrderLatency: 4 + fastest: + Name: Fastest + Timestep: 20 + OrderLatency: 6 ColorValidator: TeamColorPresets: f7b3b3, f50606, 98331f, f57606, f7bb06, f861a4, da06f3, ddb8ff, 06f739, cef7b2, 200738, 280df6, 2f86f2, 76d2f8, 34ba93, 391d1d diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 6fd8593afd..c3770b3052 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -270,30 +270,32 @@ AssetBrowser: SupportedExtensions: .shp, .tem, .sno, .vqa, .vxl GameSpeeds: - slowest: - Name: Slowest - Timestep: 80 - OrderLatency: 2 - slower: - Name: Slower - Timestep: 50 - OrderLatency: 3 - default: - Name: Normal - Timestep: 40 - OrderLatency: 3 - fast: - Name: Fast - Timestep: 35 - OrderLatency: 4 - faster: - Name: Faster - Timestep: 30 - OrderLatency: 4 - fastest: - Name: Fastest - Timestep: 20 - OrderLatency: 6 + DefaultSpeed: default + Speeds: + slowest: + Name: Slowest + Timestep: 80 + OrderLatency: 2 + slower: + Name: Slower + Timestep: 50 + OrderLatency: 3 + default: + Name: Normal + Timestep: 40 + OrderLatency: 3 + fast: + Name: Fast + Timestep: 35 + OrderLatency: 4 + faster: + Name: Faster + Timestep: 30 + OrderLatency: 4 + fastest: + Name: Fastest + Timestep: 20 + OrderLatency: 6 ColorValidator: TeamColorPresets: f70606, ff7a22, f8d3b3, f8e947, 94b319, f335a0, a64d6c, ce08f9, f5b2db, 12b572, 4A1948, 1d06f7, 328dff, 78dbf8, cef6b1, 391d1d