Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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"));
}
}
33 changes: 26 additions & 7 deletions Content.Server/Maps/GameMapManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
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;

Expand All @@ -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)]
Expand All @@ -40,18 +43,34 @@ public void Initialize()
if (TryLookupMap(value, out GameMapPrototype? map))
{
_configSelectedMap = map;
return;
}
else

if (string.IsNullOrEmpty(value))
{
if (string.IsNullOrEmpty(value))
{
_configSelectedMap = default!;
}
else
_configSelectedMap = default!;
return;
}

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))
{
_log.Error($"Unknown map prototype {value} was selected!");
_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 =>
Expand Down
14 changes: 14 additions & 0 deletions Content.Server/Maps/GameMapPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion Content.Server/Station/Systems/StationSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ public void AddGridToStation(EntityUid station, EntityUid mapGrid, MapGridCompon
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);

Expand Down
13 changes: 13 additions & 0 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,19 @@ public static readonly CVarDef<int>
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>
Expand Down
1 change: 1 addition & 0 deletions Resources/Locale/en-US/persistence/command.ftl
Original file line number Diff line number Diff line change
@@ -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.