start of persistence support (#20770)
Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
@@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,10 @@ using Content.Server.GameTicking;
|
|||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.ContentPack;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Maps;
|
namespace Content.Server.Maps;
|
||||||
|
|
||||||
@@ -16,6 +18,7 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
|
[Dependency] private readonly IResourceManager _resMan = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
@@ -40,18 +43,34 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
if (TryLookupMap(value, out GameMapPrototype? map))
|
if (TryLookupMap(value, out GameMapPrototype? map))
|
||||||
{
|
{
|
||||||
_configSelectedMap = map;
|
_configSelectedMap = map;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
if (string.IsNullOrEmpty(value))
|
||||||
{
|
{
|
||||||
_configSelectedMap = default!;
|
_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!");
|
_log.Error($"Unknown map prototype {value} was selected!");
|
||||||
}
|
|
||||||
}
|
|
||||||
}, true);
|
}, true);
|
||||||
_configurationManager.OnValueChanged(CCVars.GameMapRotation, value => _mapRotationEnabled = value, true);
|
_configurationManager.OnValueChanged(CCVars.GameMapRotation, value => _mapRotationEnabled = value, true);
|
||||||
_configurationManager.OnValueChanged(CCVars.GameMapMemoryDepth, value =>
|
_configurationManager.OnValueChanged(CCVars.GameMapMemoryDepth, value =>
|
||||||
|
|||||||
@@ -40,4 +40,18 @@ public sealed partial class GameMapPrototype : IPrototype
|
|||||||
/// The stations this map contains. The names should match with the BecomesStation components.
|
/// The stations this map contains. The names should match with the BecomesStation components.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyDictionary<string, StationConfig> Stations => _stations;
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ public sealed partial class StationMemberComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Station that this grid is a part of.
|
/// Station that this grid is a part of.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[DataField]
|
||||||
public EntityUid Station = EntityUid.Invalid;
|
public EntityUid Station = EntityUid.Invalid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ public sealed class StationSystem : EntitySystem
|
|||||||
if (!string.IsNullOrEmpty(name))
|
if (!string.IsNullOrEmpty(name))
|
||||||
_metaData.SetEntityName(mapGrid, name);
|
_metaData.SetEntityName(mapGrid, name);
|
||||||
|
|
||||||
var stationMember = AddComp<StationMemberComponent>(mapGrid);
|
var stationMember = EnsureComp<StationMemberComponent>(mapGrid);
|
||||||
stationMember.Station = station;
|
stationMember.Station = station;
|
||||||
stationData.Grids.Add(mapGrid);
|
stationData.Grids.Add(mapGrid);
|
||||||
|
|
||||||
|
|||||||
@@ -179,6 +179,19 @@ namespace Content.Shared.CCVar
|
|||||||
public static readonly CVarDef<string>
|
public static readonly CVarDef<string>
|
||||||
GameMap = CVarDef.Create("game.map", string.Empty, CVar.SERVERONLY);
|
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>
|
/// <summary>
|
||||||
/// Prototype to use for map pool.
|
/// Prototype to use for map pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
1
Resources/Locale/en-US/persistence/command.ftl
Normal file
1
Resources/Locale/en-US/persistence/command.ftl
Normal 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.
|
||||||
Reference in New Issue
Block a user