Implement auto-save
This commit is contained in:
99
OpenRA.Mods.Common/Traits/World/AutoSave.cs
Normal file
99
OpenRA.Mods.Common/Traits/World/AutoSave.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[TraitLocation(SystemActors.World)]
|
||||
[Desc("Add this trait to the world actor to enable auto-save.")]
|
||||
public class AutoSaveInfo : TraitInfo
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new AutoSave(init.Self, this); }
|
||||
}
|
||||
|
||||
public class AutoSave : ITick
|
||||
{
|
||||
const string AutoSavePattern = "autosave-";
|
||||
const string SaveFileExtension = ".orasav";
|
||||
int ticksUntilAutoSave;
|
||||
int lastSaveInverval;
|
||||
readonly bool isDisabled;
|
||||
|
||||
public AutoSave(Actor self, AutoSaveInfo info)
|
||||
{
|
||||
ticksUntilAutoSave = GetTicksBetweenAutosaves(self);
|
||||
lastSaveInverval = Game.Settings.SinglePlayerSettings.AutoSaveInterval;
|
||||
|
||||
isDisabled = self.World.LobbyInfo.GlobalSettings.Dedicated || self.World.LobbyInfo.NonBotClients.Count() > 1;
|
||||
}
|
||||
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
if (isDisabled || self.World.IsReplay || self.World.IsLoadingGameSave)
|
||||
return;
|
||||
|
||||
var autoSaveValue = Game.Settings.SinglePlayerSettings.AutoSaveInterval;
|
||||
|
||||
if (autoSaveValue == 0)
|
||||
return;
|
||||
|
||||
var autoSaveFileLimit = Game.Settings.SinglePlayerSettings.AutoSaveMaxFileCount;
|
||||
|
||||
autoSaveFileLimit = autoSaveFileLimit < 3 ? 3 : autoSaveFileLimit;
|
||||
|
||||
if (lastSaveInverval != autoSaveValue)
|
||||
{
|
||||
lastSaveInverval = autoSaveValue;
|
||||
ticksUntilAutoSave = GetTicksBetweenAutosaves(self);
|
||||
}
|
||||
|
||||
if (--ticksUntilAutoSave > 0)
|
||||
return;
|
||||
|
||||
var oldAutoSaveFiles = GetAutoSaveFiles()
|
||||
.OrderByDescending(f => f.CreationTime)
|
||||
.Skip(autoSaveFileLimit - 1);
|
||||
|
||||
foreach (var oldAutoSaveFile in oldAutoSaveFiles)
|
||||
oldAutoSaveFile.Delete();
|
||||
|
||||
var dateTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHHmmssZ", CultureInfo.InvariantCulture);
|
||||
var fileName = $"{AutoSavePattern}{dateTime}{SaveFileExtension}";
|
||||
self.World.RequestGameSave(fileName);
|
||||
ticksUntilAutoSave = GetTicksBetweenAutosaves(self);
|
||||
}
|
||||
|
||||
static IEnumerable<FileSystemInfo> GetAutoSaveFiles()
|
||||
{
|
||||
var mod = Game.ModData.Manifest;
|
||||
|
||||
var saveFolderPath = Path.Combine(Platform.SupportDir, "Saves", mod.Id, mod.Metadata.Version);
|
||||
|
||||
var autoSaveDirectoryInfo = new DirectoryInfo(saveFolderPath);
|
||||
|
||||
if (!autoSaveDirectoryInfo.Exists)
|
||||
return Array.Empty<FileInfo>();
|
||||
|
||||
return autoSaveDirectoryInfo.EnumerateFiles($"{AutoSavePattern}*{SaveFileExtension}");
|
||||
}
|
||||
|
||||
static int GetTicksBetweenAutosaves(Actor self)
|
||||
{
|
||||
return 1000 / self.World.Timestep * Game.Settings.SinglePlayerSettings.AutoSaveInterval;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class GameplaySettingsLogic : ChromeLogic
|
||||
{
|
||||
[FluentReference]
|
||||
const string AutoSaveIntervalOptions = "auto-save-interval.options";
|
||||
|
||||
[FluentReference]
|
||||
const string AutoSaveIntervalDisabled = "auto-save-interval.disabled";
|
||||
|
||||
[FluentReference]
|
||||
const string AutoSaveIntervalMinuteOptions = "auto-save-interval.minute-options";
|
||||
|
||||
[FluentReference]
|
||||
const string AutoSaveMaxFileNumber = "auto-save-max-file-number";
|
||||
readonly int[] autoSaveSeconds = { 0, 10, 30, 45, 60, 120, 180, 300, 600 };
|
||||
|
||||
readonly int[] autoSaveFileNumbers = { 3, 5, 10, 20, 50, 100 };
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public GameplaySettingsLogic(Action<string, string, Func<Widget, Func<bool>>, Func<Widget, Action>> registerPanel, string panelID, string label)
|
||||
{
|
||||
registerPanel(panelID, label, InitPanel, ResetPanel);
|
||||
}
|
||||
|
||||
Func<bool> InitPanel(Widget panel)
|
||||
{
|
||||
var scrollPanel = panel.Get<ScrollPanelWidget>("SETTINGS_SCROLLPANEL");
|
||||
SettingsUtils.AdjustSettingsScrollPanelLayout(scrollPanel);
|
||||
|
||||
// Setup dropdown for auto-save interval
|
||||
var autoSaveIntervalDropDown = panel.Get<DropDownButtonWidget>("AUTO_SAVE_INTERVAL_DROP_DOWN");
|
||||
|
||||
autoSaveIntervalDropDown.OnClick = () =>
|
||||
ShowAutoSaveIntervalDropdown(autoSaveIntervalDropDown, autoSaveSeconds);
|
||||
|
||||
autoSaveIntervalDropDown.GetText = () => GetMessageForAutoSaveInterval(Game.Settings.SinglePlayerSettings.AutoSaveInterval);
|
||||
|
||||
// Setup dropdown for auto-save nr.
|
||||
var autoSaveNoDropDown = panel.Get<DropDownButtonWidget>("AUTO_SAVE_FILE_NUMBER_DROP_DOWN");
|
||||
|
||||
autoSaveNoDropDown.OnMouseDown = _ =>
|
||||
ShowAutoSaveFileNumberDropdown(autoSaveNoDropDown, autoSaveFileNumbers);
|
||||
|
||||
autoSaveNoDropDown.GetText = () => FluentProvider.GetMessage(AutoSaveMaxFileNumber, "saves", Game.Settings.SinglePlayerSettings.AutoSaveMaxFileCount);
|
||||
|
||||
autoSaveNoDropDown.IsDisabled = () => Game.Settings.SinglePlayerSettings.AutoSaveInterval <= 0;
|
||||
|
||||
return () => false;
|
||||
}
|
||||
|
||||
Action ResetPanel(Widget panel)
|
||||
{
|
||||
return () => { };
|
||||
}
|
||||
|
||||
void ShowAutoSaveIntervalDropdown(DropDownButtonWidget dropdown, IEnumerable<int> options)
|
||||
{
|
||||
var gsp = Game.Settings.SinglePlayerSettings;
|
||||
|
||||
ScrollItemWidget SetupItem(int o, ScrollItemWidget itemTemplate)
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(itemTemplate,
|
||||
() => gsp.AutoSaveInterval == o,
|
||||
() =>
|
||||
{
|
||||
gsp.AutoSaveInterval = o;
|
||||
Game.Settings.Save();
|
||||
});
|
||||
|
||||
var deviceLabel = item.Get<LabelWidget>("LABEL");
|
||||
deviceLabel.GetText = () => GetMessageForAutoSaveInterval(o);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options, SetupItem);
|
||||
}
|
||||
|
||||
void ShowAutoSaveFileNumberDropdown(DropDownButtonWidget dropdown, IEnumerable<int> options)
|
||||
{
|
||||
var gsp = Game.Settings.SinglePlayerSettings;
|
||||
|
||||
ScrollItemWidget SetupItem(int o, ScrollItemWidget itemTemplate)
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(itemTemplate,
|
||||
() => gsp.AutoSaveMaxFileCount == o,
|
||||
() =>
|
||||
{
|
||||
gsp.AutoSaveMaxFileCount = o;
|
||||
Game.Settings.Save();
|
||||
});
|
||||
|
||||
var deviceLabel = item.Get<LabelWidget>("LABEL");
|
||||
|
||||
deviceLabel.GetText = () => FluentProvider.GetMessage(AutoSaveMaxFileNumber, "saves", o);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options, SetupItem);
|
||||
}
|
||||
|
||||
static string GetMessageForAutoSaveInterval(int value) =>
|
||||
value switch
|
||||
{
|
||||
0 => FluentProvider.GetMessage(AutoSaveIntervalDisabled),
|
||||
< 60 => FluentProvider.GetMessage(AutoSaveIntervalOptions, "seconds", value),
|
||||
_ => FluentProvider.GetMessage(AutoSaveIntervalMinuteOptions, "minutes", value / 60)
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user