start of persistence support (#20770)

Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
deltanedas
2023-12-17 03:55:19 +00:00
committed by GitHub
parent 8c1eecffd4
commit cce7aa777c
7 changed files with 118 additions and 11 deletions

View File

@@ -0,0 +1,60 @@
using Content.Server.GameTicking;
using Content.Server.Ghost.Components;
using Content.Server.Players;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Ghost;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Map;
using System.Linq;
namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Server)]
public sealed class PersistenceSave : IConsoleCommand
{
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IEntitySystemManager _system = default!;
[Dependency] private readonly IMapManager _map = default!;
public string Command => "persistencesave";
public string Description => "Saves server data to a persistence file to be loaded later.";
public string Help => "persistencesave [mapId] [filePath - default: game.map (CCVar) ]";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length < 1 || args.Length > 2)
{
shell.WriteError(Loc.GetString("shell-wrong-arguments-number"));
return;
}
if (!int.TryParse(args[0], out var intMapId))
{
shell.WriteError(Loc.GetString("cmd-parse-failure-integer", ("arg", args[0])));
return;
}
var mapId = new MapId(intMapId);
if (!_map.MapExists(mapId))
{
shell.WriteError(Loc.GetString("cmd-savemap-not-exist"));
return;
}
var saveFilePath = (args.Length > 1 ? args[1] : null) ?? _config.GetCVar(CCVars.GameMap);
if (string.IsNullOrWhiteSpace(saveFilePath))
{
shell.WriteError(Loc.GetString("cmd-persistencesave-no-path", ("cvar", nameof(CCVars.GameMap))));
return;
}
var mapLoader = _system.GetEntitySystem<MapLoaderSystem>();
mapLoader.SaveMap(mapId, saveFilePath);
shell.WriteLine(Loc.GetString("cmd-savemap-success"));
}
}

View File

@@ -5,8 +5,10 @@ using Content.Server.GameTicking;
using Content.Shared.CCVar;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
namespace Content.Server.Maps;
@@ -16,6 +18,7 @@ public sealed class GameMapManager : IGameMapManager
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IResourceManager _resMan = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[ViewVariables(VVAccess.ReadOnly)]
@@ -40,18 +43,34 @@ public sealed class GameMapManager : IGameMapManager
if (TryLookupMap(value, out GameMapPrototype? map))
{
_configSelectedMap = map;
return;
}
else
{
if (string.IsNullOrEmpty(value))
{
_configSelectedMap = default!;
return;
}
else
if (_configurationManager.GetCVar<bool>(CCVars.UsePersistence))
{
var startMap = _configurationManager.GetCVar<string>(CCVars.PersistenceMap);
_configSelectedMap = _prototypeManager.Index<GameMapPrototype>(startMap);
var mapPath = new ResPath(value);
if (_resMan.UserData.Exists(mapPath))
{
_configSelectedMap = _configSelectedMap.Persistence(mapPath);
_log.Info($"Using persistence map from {value}");
return;
}
// persistence save path doesn't exist so we just use the start map
_log.Warning($"Using persistence start map {startMap} as {value} doesn't exist");
return;
}
_log.Error($"Unknown map prototype {value} was selected!");
}
}
}, true);
_configurationManager.OnValueChanged(CCVars.GameMapRotation, value => _mapRotationEnabled = value, true);
_configurationManager.OnValueChanged(CCVars.GameMapMemoryDepth, value =>

View File

@@ -40,4 +40,18 @@ public sealed partial class GameMapPrototype : IPrototype
/// The stations this map contains. The names should match with the BecomesStation components.
/// </summary>
public IReadOnlyDictionary<string, StationConfig> Stations => _stations;
/// <summary>
/// Performs a shallow clone of this map prototype, replacing <c>MapPath</c> with the argument.
/// </summary>
public GameMapPrototype Persistence(ResPath mapPath)
{
return new()
{
ID = ID,
MapName = MapName,
MapPath = mapPath,
_stations = _stations
};
}
}

View File

@@ -11,6 +11,6 @@ public sealed partial class StationMemberComponent : Component
/// <summary>
/// Station that this grid is a part of.
/// </summary>
[ViewVariables]
[DataField]
public EntityUid Station = EntityUid.Invalid;
}

View File

@@ -331,7 +331,7 @@ public sealed class StationSystem : EntitySystem
if (!string.IsNullOrEmpty(name))
_metaData.SetEntityName(mapGrid, name);
var stationMember = AddComp<StationMemberComponent>(mapGrid);
var stationMember = EnsureComp<StationMemberComponent>(mapGrid);
stationMember.Station = station;
stationData.Grids.Add(mapGrid);

View File

@@ -179,6 +179,19 @@ namespace Content.Shared.CCVar
public static readonly CVarDef<string>
GameMap = CVarDef.Create("game.map", string.Empty, CVar.SERVERONLY);
/// <summary>
/// Controls whether to use world persistence or not.
/// </summary>
public static readonly CVarDef<bool>
UsePersistence = CVarDef.Create("game.usepersistence", false, CVar.ARCHIVE);
/// <summary>
/// If world persistence is used, what map prototype should be initially loaded.
/// If the save file exists, it replaces MapPath but everything else stays the same (station name and such).
/// </summary>
public static readonly CVarDef<string>
PersistenceMap = CVarDef.Create("game.persistencemap", "Empty", CVar.ARCHIVE);
/// <summary>
/// Prototype to use for map pool.
/// </summary>

View File

@@ -0,0 +1 @@
cmd-persistencesave-no-path = filePath was not specified and CCVar {$cvar} is not set. Manually set the filePath param in order to save the map.