Refactor map loading & saving

This commit is contained in:
ElectroJr
2024-12-22 15:13:10 +13:00
parent 1abe9db99c
commit 6242567aff
33 changed files with 553 additions and 362 deletions

View File

@@ -11,6 +11,8 @@ using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Utility;
namespace Content.IntegrationTests.Tests.Body namespace Content.IntegrationTests.Tests.Body
{ {
@@ -57,7 +59,6 @@ namespace Content.IntegrationTests.Tests.Body
await server.WaitIdleAsync(); await server.WaitIdleAsync();
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var mapLoader = entityManager.System<MapLoaderSystem>(); var mapLoader = entityManager.System<MapLoaderSystem>();
var mapSys = entityManager.System<SharedMapSystem>(); var mapSys = entityManager.System<SharedMapSystem>();
@@ -69,17 +70,13 @@ namespace Content.IntegrationTests.Tests.Body
GridAtmosphereComponent relevantAtmos = default; GridAtmosphereComponent relevantAtmos = default;
var startingMoles = 0.0f; var startingMoles = 0.0f;
var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml"; var testMapName = new ResPath("Maps/Test/Breathing/3by3-20oxy-80nit.yml");
await server.WaitPost(() => await server.WaitPost(() =>
{ {
mapSys.CreateMap(out var mapId); mapSys.CreateMap(out var mapId);
Assert.That(mapLoader.TryLoad(mapId, testMapName, out var roots)); Assert.That(mapLoader.TryLoadGrid(mapId, testMapName, out var gridEnt));
grid = gridEnt!.Value.Owner;
var query = entityManager.GetEntityQuery<MapGridComponent>();
var grids = roots.Where(x => query.HasComponent(x));
Assert.That(grids, Is.Not.Empty);
grid = grids.First();
}); });
Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found."); Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found.");
@@ -148,18 +145,13 @@ namespace Content.IntegrationTests.Tests.Body
RespiratorComponent respirator = null; RespiratorComponent respirator = null;
EntityUid human = default; EntityUid human = default;
var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml"; var testMapName = new ResPath("Maps/Test/Breathing/3by3-20oxy-80nit.yml");
await server.WaitPost(() => await server.WaitPost(() =>
{ {
mapSys.CreateMap(out var mapId); mapSys.CreateMap(out var mapId);
Assert.That(mapLoader.TryLoadGrid(mapId, testMapName, out var gridEnt));
Assert.That(mapLoader.TryLoad(mapId, testMapName, out var ents), Is.True); grid = gridEnt!.Value.Owner;
var query = entityManager.GetEntityQuery<MapGridComponent>();
grid = ents
.Select<EntityUid, EntityUid?>(x => x)
.FirstOrDefault((uid) => uid.HasValue && query.HasComponent(uid.Value), null);
Assert.That(grid, Is.Not.Null);
}); });
Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found."); Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found.");

View File

@@ -4,8 +4,10 @@ using Content.Shared.Body.Components;
using Content.Shared.Body.Systems; using Content.Shared.Body.Systems;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Content.IntegrationTests.Tests.Body; namespace Content.IntegrationTests.Tests.Body;
@@ -111,13 +113,12 @@ public sealed class SaveLoadReparentTest
Is.Not.Empty Is.Not.Empty
); );
const string mapPath = $"/{nameof(SaveLoadReparentTest)}{nameof(Test)}map.yml"; var mapPath = new ResPath($"/{nameof(SaveLoadReparentTest)}{nameof(Test)}map.yml");
mapLoader.SaveMap(mapId, mapPath); mapLoader.SaveMap(mapId, mapPath);
maps.DeleteMap(mapId); mapSys.DeleteMap(mapId);
mapSys.CreateMap(out mapId); Assert.That(mapLoader.TryLoadMap(mapPath, out var map, out _), Is.True);
Assert.That(mapLoader.TryLoad(mapId, mapPath, out _), Is.True);
var query = EnumerateQueryEnumerator( var query = EnumerateQueryEnumerator(
entities.EntityQueryEnumerator<BodyComponent>() entities.EntityQueryEnumerator<BodyComponent>()
@@ -173,7 +174,7 @@ public sealed class SaveLoadReparentTest
}); });
} }
maps.DeleteMap(mapId); entities.DeleteEntity(map);
} }
}); });

View File

@@ -1,5 +1,6 @@
#nullable enable #nullable enable
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests.Minds; namespace Content.IntegrationTests.Tests.Minds;
@@ -37,8 +38,8 @@ public sealed partial class MindTests
Assert.That(pair.Client.EntMan.EntityCount, Is.EqualTo(0)); Assert.That(pair.Client.EntMan.EntityCount, Is.EqualTo(0));
// Create a new map. // Create a new map.
int mapId = 1; MapId mapId = default;
await pair.Server.WaitPost(() => conHost.ExecuteCommand($"addmap {mapId}")); await pair.Server.WaitPost(() => pair.Server.System<SharedMapSystem>().CreateMap(out mapId));
await pair.RunTicksSync(5); await pair.RunTicksSync(5);
// Client is not attached to anything // Client is not attached to anything
@@ -54,7 +55,7 @@ public sealed partial class MindTests
Assert.That(pair.Client.EntMan.EntityExists(pair.Client.AttachedEntity)); Assert.That(pair.Client.EntMan.EntityExists(pair.Client.AttachedEntity));
Assert.That(pair.Server.EntMan.EntityExists(pair.PlayerData?.Mind)); Assert.That(pair.Server.EntMan.EntityExists(pair.PlayerData?.Mind));
var xform = pair.Client.Transform(pair.Client.AttachedEntity!.Value); var xform = pair.Client.Transform(pair.Client.AttachedEntity!.Value);
Assert.That(xform.MapID, Is.EqualTo(new MapId(mapId))); Assert.That(xform.MapID, Is.EqualTo(mapId));
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();
} }

View File

@@ -9,7 +9,6 @@ using Content.Server.Spawners.Components;
using Content.Server.Station.Components; using Content.Server.Station.Components;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Server.GameObjects;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -17,7 +16,8 @@ using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Content.Shared.Station.Components; using Content.Shared.Station.Components;
using FastAccessors; using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel; using YamlDotNet.RepresentationModel;
@@ -81,33 +81,23 @@ namespace Content.IntegrationTests.Tests
var entManager = server.ResolveDependency<IEntityManager>(); var entManager = server.ResolveDependency<IEntityManager>();
var mapLoader = entManager.System<MapLoaderSystem>(); var mapLoader = entManager.System<MapLoaderSystem>();
var mapSystem = entManager.System<SharedMapSystem>(); var mapSystem = entManager.System<SharedMapSystem>();
var mapManager = server.ResolveDependency<IMapManager>();
var cfg = server.ResolveDependency<IConfigurationManager>(); var cfg = server.ResolveDependency<IConfigurationManager>();
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
var path = new ResPath(mapFile);
await server.WaitPost(() => await server.WaitPost(() =>
{ {
mapSystem.CreateMap(out var mapId); mapSystem.CreateMap(out var mapId);
try try
{ {
#pragma warning disable NUnit2045 Assert.That(mapLoader.TryLoadGrid(mapId, path, out var grid));
Assert.That(mapLoader.TryLoad(mapId, mapFile, out var roots));
Assert.That(roots.Where(uid => entManager.HasComponent<MapGridComponent>(uid)), Is.Not.Empty);
#pragma warning restore NUnit2045
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new Exception($"Failed to load map {mapFile}, was it saved as a map instead of a grid?", ex); throw new Exception($"Failed to load map {mapFile}, was it saved as a map instead of a grid?", ex);
} }
try mapSystem.DeleteMap(mapId);
{
mapManager.DeleteMap(mapId);
}
catch (Exception ex)
{
throw new Exception($"Failed to delete map {mapFile}", ex);
}
}); });
await server.WaitRunTicks(1); await server.WaitRunTicks(1);
@@ -172,16 +162,16 @@ namespace Content.IntegrationTests.Tests
var protoManager = server.ResolveDependency<IPrototypeManager>(); var protoManager = server.ResolveDependency<IPrototypeManager>();
var ticker = entManager.EntitySysManager.GetEntitySystem<GameTicker>(); var ticker = entManager.EntitySysManager.GetEntitySystem<GameTicker>();
var shuttleSystem = entManager.EntitySysManager.GetEntitySystem<ShuttleSystem>(); var shuttleSystem = entManager.EntitySysManager.GetEntitySystem<ShuttleSystem>();
var xformQuery = entManager.GetEntityQuery<TransformComponent>();
var cfg = server.ResolveDependency<IConfigurationManager>(); var cfg = server.ResolveDependency<IConfigurationManager>();
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
await server.WaitPost(() => await server.WaitPost(() =>
{ {
mapSystem.CreateMap(out var mapId); MapId mapId;
try try
{ {
ticker.LoadGameMap(protoManager.Index<GameMapPrototype>(mapProto), mapId, null); var opts = DeserializationOptions.Default with {InitializeMaps = true};
ticker.LoadGameMap(protoManager.Index<GameMapPrototype>(mapProto), out mapId, opts);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -218,21 +208,17 @@ namespace Content.IntegrationTests.Tests
if (entManager.TryGetComponent<StationEmergencyShuttleComponent>(station, out var stationEvac)) if (entManager.TryGetComponent<StationEmergencyShuttleComponent>(station, out var stationEvac))
{ {
var shuttlePath = stationEvac.EmergencyShuttlePath; var shuttlePath = stationEvac.EmergencyShuttlePath;
#pragma warning disable NUnit2045 Assert.That(mapLoader.TryLoadGrid(shuttleMap, shuttlePath, out var shuttle),
Assert.That(mapLoader.TryLoad(shuttleMap, shuttlePath.ToString(), out var roots)); $"Failed to load {shuttlePath}");
EntityUid shuttle = default!;
Assert.DoesNotThrow(() =>
{
shuttle = roots.First(uid => entManager.HasComponent<MapGridComponent>(uid));
}, $"Failed to load {shuttlePath}");
Assert.That( Assert.That(
shuttleSystem.TryFTLDock(shuttle, shuttleSystem.TryFTLDock(shuttle!.Value.Owner,
entManager.GetComponent<ShuttleComponent>(shuttle), targetGrid.Value), entManager.GetComponent<ShuttleComponent>(shuttle!.Value.Owner),
targetGrid.Value),
$"Unable to dock {shuttlePath} to {mapProto}"); $"Unable to dock {shuttlePath} to {mapProto}");
#pragma warning restore NUnit2045
} }
mapManager.DeleteMap(shuttleMap); mapSystem.DeleteMap(shuttleMap);
if (entManager.HasComponent<StationJobsComponent>(station)) if (entManager.HasComponent<StationJobsComponent>(station))
{ {
@@ -269,7 +255,7 @@ namespace Content.IntegrationTests.Tests
try try
{ {
mapManager.DeleteMap(mapId); mapSystem.DeleteMap(mapId);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -333,11 +319,9 @@ namespace Content.IntegrationTests.Tests
var server = pair.Server; var server = pair.Server;
var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>(); var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
var mapManager = server.ResolveDependency<IMapManager>();
var resourceManager = server.ResolveDependency<IResourceManager>(); var resourceManager = server.ResolveDependency<IResourceManager>();
var protoManager = server.ResolveDependency<IPrototypeManager>(); var protoManager = server.ResolveDependency<IPrototypeManager>();
var cfg = server.ResolveDependency<IConfigurationManager>(); var cfg = server.ResolveDependency<IConfigurationManager>();
var mapSystem = server.System<SharedMapSystem>();
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
var gameMaps = protoManager.EnumeratePrototypes<GameMapPrototype>().Select(o => o.MapPath).ToHashSet(); var gameMaps = protoManager.EnumeratePrototypes<GameMapPrototype>().Select(o => o.MapPath).ToHashSet();
@@ -348,7 +332,7 @@ namespace Content.IntegrationTests.Tests
.Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal)) .Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal))
.ToArray(); .ToArray();
var mapNames = new List<string>(); var mapPaths = new List<ResPath>();
foreach (var map in maps) foreach (var map in maps)
{ {
if (gameMaps.Contains(map)) if (gameMaps.Contains(map))
@@ -359,32 +343,46 @@ namespace Content.IntegrationTests.Tests
{ {
continue; continue;
} }
mapNames.Add(rootedPath.ToString()); mapPaths.Add(rootedPath);
} }
await server.WaitPost(() => await server.WaitPost(() =>
{ {
Assert.Multiple(() => Assert.Multiple(() =>
{ {
foreach (var mapName in mapNames) // This bunch of files contains a random mixture of both map and grid files.
// TODO MAPPING organize files
var opts = MapLoadOptions.Default with
{
DeserializationOptions = DeserializationOptions.Default with
{
InitializeMaps = true,
LogOrphanedGrids = false
}
};
HashSet<Entity<MapComponent>> maps;
foreach (var path in mapPaths)
{ {
mapSystem.CreateMap(out var mapId);
try try
{ {
Assert.That(mapLoader.TryLoad(mapId, mapName, out _)); Assert.That(mapLoader.TryLoadEntities(path, out maps, out _, opts));
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new Exception($"Failed to load map {mapName}", ex); throw new Exception($"Failed to load map {path}", ex);
} }
try try
{ {
mapManager.DeleteMap(mapId); foreach (var map in maps)
{
server.EntMan.DeleteEntity(map);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new Exception($"Failed to delete map {mapName}", ex); throw new Exception($"Failed to delete map {path}", ex);
} }
} }
}); });

View File

@@ -1,11 +1,8 @@
using System.Linq; using Content.Shared.CCVar;
using Content.Shared.CCVar;
using Content.Shared.Salvage; using Content.Shared.Salvage;
using Robust.Server.GameObjects;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests; namespace Content.IntegrationTests.Tests;
@@ -24,7 +21,6 @@ public sealed class SalvageTest
var entManager = server.ResolveDependency<IEntityManager>(); var entManager = server.ResolveDependency<IEntityManager>();
var mapLoader = entManager.System<MapLoaderSystem>(); var mapLoader = entManager.System<MapLoaderSystem>();
var mapManager = server.ResolveDependency<IMapManager>();
var prototypeManager = server.ResolveDependency<IPrototypeManager>(); var prototypeManager = server.ResolveDependency<IPrototypeManager>();
var cfg = server.ResolveDependency<IConfigurationManager>(); var cfg = server.ResolveDependency<IConfigurationManager>();
var mapSystem = entManager.System<SharedMapSystem>(); var mapSystem = entManager.System<SharedMapSystem>();
@@ -34,13 +30,10 @@ public sealed class SalvageTest
{ {
foreach (var salvage in prototypeManager.EnumeratePrototypes<SalvageMapPrototype>()) foreach (var salvage in prototypeManager.EnumeratePrototypes<SalvageMapPrototype>())
{ {
var mapFile = salvage.MapPath;
mapSystem.CreateMap(out var mapId); mapSystem.CreateMap(out var mapId);
try try
{ {
Assert.That(mapLoader.TryLoad(mapId, mapFile.ToString(), out var roots)); Assert.That(mapLoader.TryLoadGrid(mapId, salvage.MapPath, out var grid));
Assert.That(roots.Where(uid => entManager.HasComponent<MapGridComponent>(uid)), Is.Not.Empty);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -49,7 +42,7 @@ public sealed class SalvageTest
try try
{ {
mapManager.DeleteMap(mapId); mapSystem.DeleteMap(mapId);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -3,6 +3,7 @@ using Content.Shared.CCVar;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
@@ -16,7 +17,7 @@ namespace Content.IntegrationTests.Tests
[Test] [Test]
public async Task SaveLoadMultiGridMap() public async Task SaveLoadMultiGridMap()
{ {
const string mapPath = @"/Maps/Test/TestMap.yml"; var mapPath = new ResPath("/Maps/Test/TestMap.yml");
await using var pair = await PoolManager.GetServerClient(); await using var pair = await PoolManager.GetServerClient();
var server = pair.Server; var server = pair.Server;
@@ -31,7 +32,7 @@ namespace Content.IntegrationTests.Tests
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
var dir = new ResPath(mapPath).Directory; var dir = mapPath.Directory;
resManager.UserData.CreateDir(dir); resManager.UserData.CreateDir(dir);
mapSystem.CreateMap(out var mapId); mapSystem.CreateMap(out var mapId);
@@ -48,14 +49,16 @@ namespace Content.IntegrationTests.Tests
} }
Assert.Multiple(() => mapLoader.SaveMap(mapId, mapPath)); Assert.Multiple(() => mapLoader.SaveMap(mapId, mapPath));
Assert.Multiple(() => mapManager.DeleteMap(mapId)); Assert.Multiple(() => mapSystem.DeleteMap(mapId));
}); });
await server.WaitIdleAsync(); await server.WaitIdleAsync();
MapId newMap = default;
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
Assert.That(mapLoader.TryLoad(new MapId(10), mapPath, out _)); Assert.That(mapLoader.TryLoadMap(mapPath, out var map, out _));
newMap = map!.Value.Comp.MapId;
}); });
await server.WaitIdleAsync(); await server.WaitIdleAsync();
@@ -63,7 +66,7 @@ namespace Content.IntegrationTests.Tests
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
{ {
if (!mapManager.TryFindGridAt(new MapId(10), new Vector2(10, 10), out var gridUid, out var mapGrid) || if (!mapManager.TryFindGridAt(newMap, new Vector2(10, 10), out var gridUid, out var mapGrid) ||
!sEntities.TryGetComponent<TransformComponent>(gridUid, out var gridXform)) !sEntities.TryGetComponent<TransformComponent>(gridUid, out var gridXform))
{ {
Assert.Fail(); Assert.Fail();
@@ -77,7 +80,7 @@ namespace Content.IntegrationTests.Tests
}); });
} }
{ {
if (!mapManager.TryFindGridAt(new MapId(10), new Vector2(-8, -8), out var gridUid, out var mapGrid) || if (!mapManager.TryFindGridAt(newMap, new Vector2(-8, -8), out var gridUid, out var mapGrid) ||
!sEntities.TryGetComponent<TransformComponent>(gridUid, out var gridXform)) !sEntities.TryGetComponent<TransformComponent>(gridUid, out var gridXform))
{ {
Assert.Fail(); Assert.Fail();

View File

@@ -1,25 +1,25 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Events;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.IntegrationTests.Tests namespace Content.IntegrationTests.Tests
{ {
/// <summary> /// <summary>
/// Tests that a map's yaml does not change when saved consecutively. /// Tests that a grid's yaml does not change when saved consecutively.
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public sealed class SaveLoadSaveTest public sealed class SaveLoadSaveTest
{ {
[Test] [Test]
public async Task SaveLoadSave() public async Task CreateSaveLoadSaveGrid()
{ {
await using var pair = await PoolManager.GetServerClient(); await using var pair = await PoolManager.GetServerClient();
var server = pair.Server; var server = pair.Server;
@@ -30,22 +30,21 @@ namespace Content.IntegrationTests.Tests
var cfg = server.ResolveDependency<IConfigurationManager>(); var cfg = server.ResolveDependency<IConfigurationManager>();
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
var testSystem = server.System<SaveLoadSaveTestSystem>();
testSystem.Enabled = true;
var rp1 = new ResPath("/save load save 1.yml");
var rp2 = new ResPath("/save load save 2.yml");
await server.WaitPost(() => await server.WaitPost(() =>
{ {
mapSystem.CreateMap(out var mapId0); mapSystem.CreateMap(out var mapId0);
// TODO: Properly find the "main" station grid.
var grid0 = mapManager.CreateGridEntity(mapId0); var grid0 = mapManager.CreateGridEntity(mapId0);
mapLoader.Save(grid0.Owner, "save load save 1.yml"); entManager.RunMapInit(grid0.Owner, entManager.GetComponent<MetaDataComponent>(grid0));
mapLoader.SaveGrid(grid0.Owner, rp1);
mapSystem.CreateMap(out var mapId1); mapSystem.CreateMap(out var mapId1);
EntityUid grid1 = default!; Assert.That(mapLoader.TryLoadGrid(mapId1, rp1, out var grid1));
#pragma warning disable NUnit2045 mapLoader.SaveGrid(grid1!.Value, rp2);
Assert.That(mapLoader.TryLoad(mapId1, "save load save 1.yml", out var roots, new MapLoadOptions() { LoadMap = false }), $"Failed to load test map {TestMap}");
Assert.DoesNotThrow(() =>
{
grid1 = roots.First(uid => entManager.HasComponent<MapGridComponent>(uid));
});
#pragma warning restore NUnit2045
mapLoader.Save(grid1, "save load save 2.yml");
}); });
await server.WaitIdleAsync(); await server.WaitIdleAsync();
@@ -54,14 +53,12 @@ namespace Content.IntegrationTests.Tests
string one; string one;
string two; string two;
var rp1 = new ResPath("/save load save 1.yml");
await using (var stream = userData.Open(rp1, FileMode.Open)) await using (var stream = userData.Open(rp1, FileMode.Open))
using (var reader = new StreamReader(stream)) using (var reader = new StreamReader(stream))
{ {
one = await reader.ReadToEndAsync(); one = await reader.ReadToEndAsync();
} }
var rp2 = new ResPath("/save load save 2.yml");
await using (var stream = userData.Open(rp2, FileMode.Open)) await using (var stream = userData.Open(rp2, FileMode.Open))
using (var reader = new StreamReader(stream)) using (var reader = new StreamReader(stream))
{ {
@@ -87,6 +84,7 @@ namespace Content.IntegrationTests.Tests
TestContext.Error.WriteLine(twoTmp); TestContext.Error.WriteLine(twoTmp);
} }
}); });
testSystem.Enabled = false;
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();
} }
@@ -101,8 +99,12 @@ namespace Content.IntegrationTests.Tests
await using var pair = await PoolManager.GetServerClient(); await using var pair = await PoolManager.GetServerClient();
var server = pair.Server; var server = pair.Server;
var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>(); var mapLoader = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
var mapManager = server.ResolveDependency<IMapManager>(); var mapSys = server.System<SharedMapSystem>();
var mapSystem = server.System<SharedMapSystem>(); var testSystem = server.System<SaveLoadSaveTestSystem>();
testSystem.Enabled = true;
var rp1 = new ResPath("/load save ticks save 1.yml");
var rp2 = new ResPath("/load save ticks save 2.yml");
MapId mapId = default; MapId mapId = default;
var cfg = server.ResolveDependency<IConfigurationManager>(); var cfg = server.ResolveDependency<IConfigurationManager>();
@@ -111,10 +113,10 @@ namespace Content.IntegrationTests.Tests
// Load bagel.yml as uninitialized map, and save it to ensure it's up to date. // Load bagel.yml as uninitialized map, and save it to ensure it's up to date.
server.Post(() => server.Post(() =>
{ {
mapSystem.CreateMap(out mapId, runMapInit: false); var path = new ResPath(TestMap);
mapManager.SetMapPaused(mapId, true); Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}");
Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}"); mapId = map!.Value.Comp.MapId;
mapLoader.SaveMap(mapId, "load save ticks save 1.yml"); mapLoader.SaveMap(mapId, rp1);
}); });
// Run 5 ticks. // Run 5 ticks.
@@ -122,7 +124,7 @@ namespace Content.IntegrationTests.Tests
await server.WaitPost(() => await server.WaitPost(() =>
{ {
mapLoader.SaveMap(mapId, "/load save ticks save 2.yml"); mapLoader.SaveMap(mapId, rp2);
}); });
await server.WaitIdleAsync(); await server.WaitIdleAsync();
@@ -131,13 +133,13 @@ namespace Content.IntegrationTests.Tests
string one; string one;
string two; string two;
await using (var stream = userData.Open(new ResPath("/load save ticks save 1.yml"), FileMode.Open)) await using (var stream = userData.Open(rp1, FileMode.Open))
using (var reader = new StreamReader(stream)) using (var reader = new StreamReader(stream))
{ {
one = await reader.ReadToEndAsync(); one = await reader.ReadToEndAsync();
} }
await using (var stream = userData.Open(new ResPath("/load save ticks save 2.yml"), FileMode.Open)) await using (var stream = userData.Open(rp2, FileMode.Open))
using (var reader = new StreamReader(stream)) using (var reader = new StreamReader(stream))
{ {
two = await reader.ReadToEndAsync(); two = await reader.ReadToEndAsync();
@@ -163,7 +165,8 @@ namespace Content.IntegrationTests.Tests
} }
}); });
await server.WaitPost(() => mapManager.DeleteMap(mapId)); testSystem.Enabled = false;
await server.WaitPost(() => mapSys.DeleteMap(mapId));
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();
} }
@@ -184,13 +187,15 @@ namespace Content.IntegrationTests.Tests
var server = pair.Server; var server = pair.Server;
var mapLoader = server.System<MapLoaderSystem>(); var mapLoader = server.System<MapLoaderSystem>();
var mapSystem = server.System<SharedMapSystem>(); var mapSys = server.System<SharedMapSystem>();
var mapManager = server.ResolveDependency<IMapManager>();
var userData = server.ResolveDependency<IResourceManager>().UserData; var userData = server.ResolveDependency<IResourceManager>().UserData;
var cfg = server.ResolveDependency<IConfigurationManager>(); var cfg = server.ResolveDependency<IConfigurationManager>();
Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
var testSystem = server.System<SaveLoadSaveTestSystem>();
testSystem.Enabled = true;
MapId mapId = default; MapId mapId1 = default;
MapId mapId2 = default;
const string fileA = "/load tick load a.yml"; const string fileA = "/load tick load a.yml";
const string fileB = "/load tick load b.yml"; const string fileB = "/load tick load b.yml";
string yamlA; string yamlA;
@@ -199,10 +204,10 @@ namespace Content.IntegrationTests.Tests
// Load & save the first map // Load & save the first map
server.Post(() => server.Post(() =>
{ {
mapSystem.CreateMap(out mapId, runMapInit: false); var path = new ResPath(TestMap);
mapManager.SetMapPaused(mapId, true); Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}");
Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}"); mapId1 = map!.Value.Comp.MapId;
mapLoader.SaveMap(mapId, fileA); mapLoader.SaveMap(mapId1, fileA);
}); });
await server.WaitIdleAsync(); await server.WaitIdleAsync();
@@ -217,11 +222,10 @@ namespace Content.IntegrationTests.Tests
// Load & save the second map // Load & save the second map
server.Post(() => server.Post(() =>
{ {
mapManager.DeleteMap(mapId); var path = new ResPath(TestMap);
mapSystem.CreateMap(out mapId, runMapInit: false); Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}");
mapManager.SetMapPaused(mapId, true); mapId2 = map!.Value.Comp.MapId;
Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}"); mapLoader.SaveMap(mapId2, fileB);
mapLoader.SaveMap(mapId, fileB);
}); });
await server.WaitIdleAsync(); await server.WaitIdleAsync();
@@ -234,8 +238,32 @@ namespace Content.IntegrationTests.Tests
Assert.That(yamlA, Is.EqualTo(yamlB)); Assert.That(yamlA, Is.EqualTo(yamlB));
await server.WaitPost(() => mapManager.DeleteMap(mapId)); testSystem.Enabled = false;
await server.WaitPost(() => mapSys.DeleteMap(mapId1));
await server.WaitPost(() => mapSys.DeleteMap(mapId2));
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();
} }
/// <summary>
/// Simple system that modifies the data saved to a yaml file by removing the timestamp.
/// Required by some tests that validate that re-saving a map does not modify it.
/// </summary>
private sealed class SaveLoadSaveTestSystem : EntitySystem
{
public bool Enabled;
public override void Initialize()
{
SubscribeLocalEvent<AfterSaveEvent>(OnAfterSave);
}
private void OnAfterSave(AfterSaveEvent ev)
{
if (!Enabled)
return;
// Remove timestamp.
((MappingDataNode)ev.Node["meta"]).Remove("time");
}
}
} }
} }

View File

@@ -4,10 +4,12 @@ using System.Numerics;
using Content.Server.Shuttles.Systems; using Content.Server.Shuttles.Systems;
using Content.Tests; using Content.Tests;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Content.IntegrationTests.Tests.Shuttle; namespace Content.IntegrationTests.Tests.Shuttle;
@@ -106,8 +108,9 @@ public sealed class DockTest : ContentUnitTest
{ {
mapGrid = entManager.AddComponent<MapGridComponent>(map.MapUid); mapGrid = entManager.AddComponent<MapGridComponent>(map.MapUid);
entManager.DeleteEntity(map.Grid); entManager.DeleteEntity(map.Grid);
Assert.That(entManager.System<MapLoaderSystem>().TryLoad(otherMap.MapId, "/Maps/Shuttles/emergency.yml", out var rootUids)); var path = new ResPath("/Maps/Shuttles/emergency.yml");
shuttle = rootUids[0]; Assert.That(entManager.System<MapLoaderSystem>().TryLoadGrid(otherMap.MapId, path, out var grid));
shuttle = grid!.Value.Owner;
var dockingConfig = dockingSystem.GetDockingConfig(shuttle, map.MapUid); var dockingConfig = dockingSystem.GetDockingConfig(shuttle, map.MapUid);
Assert.That(dockingConfig, Is.EqualTo(null)); Assert.That(dockingConfig, Is.EqualTo(null));

View File

@@ -1,11 +1,9 @@
using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Maps; using Content.Server.Maps;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Server.Maps;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.ContentPack; using Robust.Shared.EntitySerialization;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -25,6 +23,7 @@ namespace Content.Server.Administration.Commands
var prototypeManager = IoCManager.Resolve<IPrototypeManager>(); var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
var entityManager = IoCManager.Resolve<IEntityManager>(); var entityManager = IoCManager.Resolve<IEntityManager>();
var gameTicker = entityManager.EntitySysManager.GetEntitySystem<GameTicker>(); var gameTicker = entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
var mapSys = entityManager.EntitySysManager.GetEntitySystem<SharedMapSystem>();
if (args.Length is not (2 or 4 or 5)) if (args.Length is not (2 or 4 or 5))
{ {
@@ -32,30 +31,29 @@ namespace Content.Server.Administration.Commands
return; return;
} }
if (prototypeManager.TryIndex<GameMapPrototype>(args[1], out var gameMap)) if (!prototypeManager.TryIndex<GameMapPrototype>(args[1], out var gameMap))
{ {
shell.WriteError($"The given map prototype {args[0]} is invalid.");
return;
}
if (!int.TryParse(args[0], out var mapId)) if (!int.TryParse(args[0], out var mapId))
return; return;
var loadOptions = new MapLoadOptions()
{
LoadMap = false,
};
var stationName = args.Length == 5 ? args[4] : null; var stationName = args.Length == 5 ? args[4] : null;
if (args.Length >= 4 && int.TryParse(args[2], out var x) && int.TryParse(args[3], out var y)) Vector2? offset = null;
{ if (args.Length >= 4)
loadOptions.Offset = new Vector2(x, y); offset = new Vector2(int.Parse(args[2]), int.Parse(args[3]));
}
var grids = gameTicker.LoadGameMap(gameMap, new MapId(mapId), loadOptions, stationName); var id = new MapId(mapId);
var grids = mapSys.MapExists(id)
? gameTicker.MergeGameMap(gameMap, id, stationName: stationName, offset: offset)
: gameTicker.LoadGameMapWithId(gameMap, id, stationName: stationName, offset: offset);
shell.WriteLine($"Loaded {grids.Count} grids."); shell.WriteLine($"Loaded {grids.Count} grids.");
} }
else
{
shell.WriteError($"The given map prototype {args[0]} is invalid.");
}
}
public CompletionResult GetCompletion(IConsoleShell shell, string[] args) public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{ {

View File

@@ -5,6 +5,7 @@ using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Map; using Robust.Shared.Map;
using System.Linq; using System.Linq;
using Robust.Shared.EntitySerialization.Systems;
namespace Content.Server.Administration.Commands; namespace Content.Server.Administration.Commands;

View File

@@ -1,8 +1,7 @@
using Robust.Server.GameObjects; using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Utility;
namespace Content.Server.Administration.Systems; namespace Content.Server.Administration.Systems;
@@ -11,8 +10,7 @@ namespace Content.Server.Administration.Systems;
/// </summary> /// </summary>
public sealed class AdminTestArenaSystem : EntitySystem public sealed class AdminTestArenaSystem : EntitySystem
{ {
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly MapLoaderSystem _loader = default!;
[Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly MetaDataSystem _metaDataSystem = default!; [Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
public const string ArenaMapPath = "/Maps/Test/admin_test_arena.yml"; public const string ArenaMapPath = "/Maps/Test/admin_test_arena.yml";
@@ -28,26 +26,24 @@ public sealed class AdminTestArenaSystem : EntitySystem
{ {
return (arenaMap, arenaGrid); return (arenaMap, arenaGrid);
} }
else
{
ArenaGrid[admin.UserId] = null; ArenaGrid[admin.UserId] = null;
return (arenaMap, null); return (arenaMap, null);
} }
}
ArenaMap[admin.UserId] = _mapManager.GetMapEntityId(_mapManager.CreateMap()); var path = new ResPath(ArenaMapPath);
_metaDataSystem.SetEntityName(ArenaMap[admin.UserId], $"ATAM-{admin.Name}"); if (!_loader.TryLoadMap(path, out var map, out var grids))
var grids = _map.LoadMap(Comp<MapComponent>(ArenaMap[admin.UserId]).MapId, ArenaMapPath); throw new Exception($"Failed to load admin arena");
if (grids.Count != 0)
{
_metaDataSystem.SetEntityName(grids[0], $"ATAG-{admin.Name}");
ArenaGrid[admin.UserId] = grids[0];
}
else
{
ArenaGrid[admin.UserId] = null;
}
return (ArenaMap[admin.UserId], ArenaGrid[admin.UserId]); ArenaMap[admin.UserId] = map.Value.Owner;
_metaDataSystem.SetEntityName(map.Value.Owner, $"ATAM-{admin.Name}");
var grid = grids.FirstOrNull();
ArenaGrid[admin.UserId] = grid?.Owner;
if (grid != null)
_metaDataSystem.SetEntityName(grid.Value.Owner, $"ATAG-{admin.Name}");
return (map.Value.Owner, grid?.Owner);
} }
} }

View File

@@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using System.Numerics;
using Content.Server.Announcements; using Content.Server.Announcements;
using Content.Server.Discord; using Content.Server.Discord;
using Content.Server.GameTicking.Events; using Content.Server.GameTicking.Events;
@@ -13,10 +14,12 @@ using Content.Shared.Players;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using JetBrains.Annotations; using JetBrains.Annotations;
using Prometheus; using Prometheus;
using Robust.Server.Maps;
using Robust.Shared.Asynchronous; using Robust.Shared.Asynchronous;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -92,9 +95,6 @@ namespace Content.Server.GameTicking
AddGamePresetRules(); AddGamePresetRules();
DefaultMap = _mapManager.CreateMap();
_mapManager.AddUninitializedMap(DefaultMap);
var maps = new List<GameMapPrototype>(); var maps = new List<GameMapPrototype>();
// the map might have been force-set by something // the map might have been force-set by something
@@ -132,52 +132,203 @@ namespace Content.Server.GameTicking
// Let game rules dictate what maps we should load. // Let game rules dictate what maps we should load.
RaiseLocalEvent(new LoadingMapsEvent(maps)); RaiseLocalEvent(new LoadingMapsEvent(maps));
foreach (var map in maps) if (maps.Count == 0)
{ {
var toLoad = DefaultMap; _map.CreateMap(out var mapId, runMapInit: false);
if (maps[0] != map) DefaultMap = mapId;
{ return;
// Create other maps for the others since we need to.
toLoad = _mapManager.CreateMap();
_mapManager.AddUninitializedMap(toLoad);
} }
LoadGameMap(map, toLoad, null); for (var i = 0; i < maps.Count; i++)
{
LoadGameMap(maps[i], out var mapId);
DebugTools.Assert(!_map.IsInitialized(mapId));
if (i == 0)
DefaultMap = mapId;
} }
} }
public PreGameMapLoad RaisePreLoad(
GameMapPrototype proto,
DeserializationOptions? opts = null,
Vector2? offset = null,
Angle? rot = null)
{
offset ??= proto.MaxRandomOffset != 0f
? _robustRandom.NextVector2(proto.MaxRandomOffset)
: Vector2.Zero;
rot ??= proto.RandomRotation
? _robustRandom.NextAngle()
: Angle.Zero;
opts ??= DeserializationOptions.Default;
var ev = new PreGameMapLoad(proto, opts.Value, offset.Value, rot.Value);
RaiseLocalEvent(ev);
return ev;
}
/// <summary> /// <summary>
/// Loads a new map, allowing systems interested in it to handle loading events. /// Loads a new map, allowing systems interested in it to handle loading events.
/// In the base game, this is required to be used if you want to load a station. /// In the base game, this is required to be used if you want to load a station.
/// This does not initialze maps, unles specified via the <see cref="DeserializationOptions"/>.
/// </summary> /// </summary>
/// <param name="map">Game map prototype to load in.</param> /// <remarks>
/// <param name="targetMapId">Map to load into.</param> /// This is basically a wrapper around a <see cref="MapLoaderSystem"/> method that auto generate
/// <param name="loadOptions">Map loading options, includes offset.</param> /// some <see cref="MapLoadOptions"/> using information in a prototype, and raise some events to allow content
/// to modify the options and react to the map creation.
/// </remarks>
/// <param name="proto">Game map prototype to load in.</param>
/// <param name="mapId">The id of the map that was loaded.</param>
/// <param name="options">Entity loading options, including whether the maps should be initialized.</param>
/// <param name="stationName">Name to assign to the loaded station.</param> /// <param name="stationName">Name to assign to the loaded station.</param>
/// <returns>All loaded entities and grids.</returns> /// <returns>All loaded entities and grids.</returns>
public IReadOnlyList<EntityUid> LoadGameMap(GameMapPrototype map, MapId targetMapId, MapLoadOptions? loadOptions, string? stationName = null) public IReadOnlyList<EntityUid> LoadGameMap(
GameMapPrototype proto,
out MapId mapId,
DeserializationOptions? options = null,
string? stationName = null,
Vector2? offset = null,
Angle? rot = null)
{ {
// Okay I specifically didn't set LoadMap here because this is typically called onto a new map. var ev = RaisePreLoad(proto, options, offset, rot);
// whereas the command can also be used on an existing map.
var loadOpts = loadOptions ?? new MapLoadOptions();
if (map.MaxRandomOffset != 0f) if (ev.GameMap.IsGrid)
loadOpts.Offset = _robustRandom.NextVector2(map.MaxRandomOffset); {
var mapUid = _map.CreateMap(out mapId);
if (!_loader.TryLoadGrid(mapId,
ev.GameMap.MapPath,
out var grid,
ev.Options,
ev.Offset,
ev.Rotation))
{
throw new Exception($"Failed to load game-map grid {ev.GameMap.ID}");
}
if (map.RandomRotation) _metaData.SetEntityName(mapUid, proto.MapName);
loadOpts.Rotation = _robustRandom.NextAngle(); var g = new List<EntityUid> {grid.Value.Owner};
RaiseLocalEvent(new PostGameMapLoad(proto, mapId, g, stationName));
return g;
}
var ev = new PreGameMapLoad(targetMapId, map, loadOpts); if (!_loader.TryLoadMap(ev.GameMap.MapPath,
RaiseLocalEvent(ev); out var map,
out var grids,
ev.Options,
ev.Offset,
ev.Rotation))
{
throw new Exception($"Failed to load game map {ev.GameMap.ID}");
}
var gridIds = _map.LoadMap(targetMapId, ev.GameMap.MapPath.ToString(), ev.Options); mapId = map.Value.Comp.MapId;
_metaData.SetEntityName(map.Value.Owner, proto.MapName);
var gridUids = grids.Select(x => x.Owner).ToList();
RaiseLocalEvent(new PostGameMapLoad(proto, mapId, gridUids, stationName));
return gridUids;
}
_metaData.SetEntityName(_mapManager.GetMapEntityId(targetMapId), map.MapName); /// <summary>
/// Variant of <see cref="LoadGameMap"/> that attempts to assign the provided <see cref="MapId"/> to the
/// loaded map.
/// </summary>
public IReadOnlyList<EntityUid> LoadGameMapWithId(
GameMapPrototype proto,
MapId mapId,
DeserializationOptions? opts = null,
string? stationName = null,
Vector2? offset = null,
Angle? rot = null)
{
var ev = RaisePreLoad(proto, opts, offset, rot);
var gridUids = gridIds.ToList(); if (ev.GameMap.IsGrid)
RaiseLocalEvent(new PostGameMapLoad(map, targetMapId, gridUids, stationName)); {
var mapUid = _map.CreateMap(mapId);
if (!_loader.TryLoadGrid(mapId,
ev.GameMap.MapPath,
out var grid,
ev.Options,
ev.Offset,
ev.Rotation))
{
throw new Exception($"Failed to load game-map grid {ev.GameMap.ID}");
}
_metaData.SetEntityName(mapUid, proto.MapName);
var g = new List<EntityUid> {grid.Value.Owner};
RaiseLocalEvent(new PostGameMapLoad(proto, mapId, g, stationName));
return g;
}
if (!_loader.TryLoadMapWithId(
mapId,
ev.GameMap.MapPath,
out var map,
out var grids,
ev.Options,
ev.Offset,
ev.Rotation))
{
throw new Exception($"Failed to load map");
}
_metaData.SetEntityName(map.Value.Owner, proto.MapName);
var gridUids = grids.Select(x => x.Owner).ToList();
RaiseLocalEvent(new PostGameMapLoad(proto, mapId, gridUids, stationName));
return gridUids;
}
/// <summary>
/// Variant of <see cref="LoadGameMap"/> that loads and then merges a game map onto an existing map.
/// </summary>
public IReadOnlyList<EntityUid> MergeGameMap(
GameMapPrototype proto,
MapId targetMap,
DeserializationOptions? opts = null,
string? stationName = null,
Vector2? offset = null,
Angle? rot = null)
{
// TODO MAP LOADING use a new event?
// This is quite different from the other methods, which will actually create a **new** map.
var ev = RaisePreLoad(proto, opts, offset, rot);
if (ev.GameMap.IsGrid)
{
if (!_loader.TryLoadGrid(targetMap,
ev.GameMap.MapPath,
out var grid,
ev.Options,
ev.Offset,
ev.Rotation))
{
throw new Exception($"Failed to load game-map grid {ev.GameMap.ID}");
}
var g = new List<EntityUid> {grid.Value.Owner};
// TODO MAP LOADING use a new event?
RaiseLocalEvent(new PostGameMapLoad(proto, targetMap, g, stationName));
return g;
}
if (!_loader.TryMergeMap(targetMap,
ev.GameMap.MapPath,
out var map,
out var grids,
ev.Options,
ev.Offset,
ev.Rotation))
{
throw new Exception($"Failed to load map");
}
var gridUids = grids.Select(x => x.Owner).ToList();
// TODO MAP LOADING use a new event?
RaiseLocalEvent(new PostGameMapLoad(proto, targetMap, gridUids, stationName));
return gridUids; return gridUids;
} }
@@ -274,7 +425,7 @@ namespace Content.Server.GameTicking
} }
// MapInitialize *before* spawning players, our codebase is too shit to do it afterwards... // MapInitialize *before* spawning players, our codebase is too shit to do it afterwards...
_mapManager.DoMapInitialize(DefaultMap); _map.InitializeMap(DefaultMap);
SpawnPlayers(readyPlayers, readyPlayerProfiles, force); SpawnPlayers(readyPlayers, readyPlayerProfiles, force);
@@ -714,20 +865,13 @@ namespace Content.Server.GameTicking
/// You likely want to subscribe to this after StationSystem. /// You likely want to subscribe to this after StationSystem.
/// </remarks> /// </remarks>
[PublicAPI] [PublicAPI]
public sealed class PreGameMapLoad : EntityEventArgs public sealed class PreGameMapLoad(GameMapPrototype gameMap, DeserializationOptions options, Vector2 offset, Angle rotation) : EntityEventArgs
{ {
public readonly MapId Map; public readonly GameMapPrototype GameMap = gameMap;
public GameMapPrototype GameMap; public DeserializationOptions Options = options;
public MapLoadOptions Options; public Vector2 Offset = offset;
public Angle Rotation = rotation;
public PreGameMapLoad(MapId map, GameMapPrototype gameMap, MapLoadOptions options)
{
Map = map;
GameMap = gameMap;
Options = options;
} }
}
/// <summary> /// <summary>
/// Event raised after the game loads a given map. /// Event raised after the game loads a given map.

View File

@@ -18,6 +18,7 @@ using Robust.Server.GameObjects;
using Robust.Server.GameStates; using Robust.Server.GameStates;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -48,7 +49,8 @@ namespace Content.Server.GameTicking
[Dependency] private readonly IServerPreferencesManager _prefsManager = default!; [Dependency] private readonly IServerPreferencesManager _prefsManager = default!;
[Dependency] private readonly IServerDbManager _db = default!; [Dependency] private readonly IServerDbManager _db = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly MapLoaderSystem _loader = default!;
[Dependency] private readonly SharedMapSystem _map = default!;
[Dependency] private readonly GhostSystem _ghost = default!; [Dependency] private readonly GhostSystem _ghost = default!;
[Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly PlayTimeTrackingSystem _playTimeTrackings = default!; [Dependency] private readonly PlayTimeTrackingSystem _playTimeTrackings = default!;

View File

@@ -20,11 +20,17 @@ public sealed partial class LoadMapRuleComponent : Component
public ProtoId<GameMapPrototype>? GameMap; public ProtoId<GameMapPrototype>? GameMap;
/// <summary> /// <summary>
/// A map path to load on a new map. /// A map to load.
/// </summary> /// </summary>
[DataField] [DataField]
public ResPath? MapPath; public ResPath? MapPath;
/// <summary>
/// A grid to load on a new map.
/// </summary>
[DataField]
public ResPath? GridPath;
/// <summary> /// <summary>
/// A <see cref="PreloadedGridPrototype"/> to move to a new map. /// A <see cref="PreloadedGridPrototype"/> to move to a new map.
/// If there are no instances left nothing is done. /// If there are no instances left nothing is done.

View File

@@ -1,10 +1,14 @@
using System.Linq;
using Content.Server.GameTicking.Rules.Components; using Content.Server.GameTicking.Rules.Components;
using Content.Server.GridPreloader; using Content.Server.GridPreloader;
using Content.Server.StationEvents.Events; using Content.Server.StationEvents.Events;
using Content.Shared.GameTicking.Components; using Content.Shared.GameTicking.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Maps; using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server.GameTicking.Rules; namespace Content.Server.GameTicking.Rules;
@@ -26,29 +30,50 @@ public sealed class LoadMapRuleSystem : StationEventSystem<LoadMapRuleComponent>
return; return;
} }
// grid preloading needs map to init after moving it MapId mapId;
var mapUid = _map.CreateMap(out var mapId, runMapInit: comp.PreloadedGrid == null);
Log.Info($"Created map {mapId} for {ToPrettyString(uid):rule}");
IReadOnlyList<EntityUid> grids; IReadOnlyList<EntityUid> grids;
if (comp.GameMap != null) if (comp.GameMap != null)
{ {
// Component has one of three modes, only one of the three fields should ever be populated.
DebugTools.AssertNull(comp.MapPath);
DebugTools.AssertNull(comp.GridPath);
DebugTools.AssertNull(comp.PreloadedGrid);
var gameMap = _prototypeManager.Index(comp.GameMap.Value); var gameMap = _prototypeManager.Index(comp.GameMap.Value);
grids = GameTicker.LoadGameMap(gameMap, mapId, new MapLoadOptions()); grids = GameTicker.LoadGameMap(gameMap, out mapId, null);
Log.Info($"Created map {mapId} for {ToPrettyString(uid):rule}");
} }
else if (comp.MapPath is {} path) else if (comp.MapPath is {} path)
{ {
var options = new MapLoadOptions { LoadMap = true }; DebugTools.AssertNull(comp.GridPath);
if (!_mapLoader.TryLoad(mapId, path.ToString(), out var roots, options)) DebugTools.AssertNull(comp.PreloadedGrid);
var opts = DeserializationOptions.Default with {InitializeMaps = true};
if (!_mapLoader.TryLoadMap(path, out var map, out var gridSet, opts))
{ {
Log.Error($"Failed to load map from {path}!"); Log.Error($"Failed to load map from {path}!");
Del(mapUid);
ForceEndSelf(uid, rule); ForceEndSelf(uid, rule);
return; return;
} }
grids = roots; grids = gridSet.Select( x => x.Owner).ToList();
mapId = map.Value.Comp.MapId;
}
else if (comp.GridPath is { } gPath)
{
DebugTools.AssertNull(comp.PreloadedGrid);
// I fucking love it when "map paths" choses to ar
_map.CreateMap(out mapId);
var opts = DeserializationOptions.Default with {InitializeMaps = true};
if (!_mapLoader.TryLoadGrid(mapId, gPath, out var grid, opts))
{
Log.Error($"Failed to load grid from {gPath}!");
ForceEndSelf(uid, rule);
return;
}
grids = new List<EntityUid> {grid.Value.Owner};
} }
else if (comp.PreloadedGrid is {} preloaded) else if (comp.PreloadedGrid is {} preloaded)
{ {
@@ -56,11 +81,11 @@ public sealed class LoadMapRuleSystem : StationEventSystem<LoadMapRuleComponent>
if (!_gridPreloader.TryGetPreloadedGrid(preloaded, out var loadedShuttle)) if (!_gridPreloader.TryGetPreloadedGrid(preloaded, out var loadedShuttle))
{ {
Log.Error($"Failed to get a preloaded grid with {preloaded}!"); Log.Error($"Failed to get a preloaded grid with {preloaded}!");
Del(mapUid);
ForceEndSelf(uid, rule); ForceEndSelf(uid, rule);
return; return;
} }
var mapUid = _map.CreateMap(out mapId, runMapInit: false);
_transform.SetParent(loadedShuttle.Value, mapUid); _transform.SetParent(loadedShuttle.Value, mapUid);
grids = new List<EntityUid>() { loadedShuttle.Value }; grids = new List<EntityUid>() { loadedShuttle.Value };
_map.InitializeMap(mapUid); _map.InitializeMap(mapUid);
@@ -68,7 +93,6 @@ public sealed class LoadMapRuleSystem : StationEventSystem<LoadMapRuleComponent>
else else
{ {
Log.Error($"No valid map prototype or map path associated with the rule {ToPrettyString(uid)}"); Log.Error($"No valid map prototype or map path associated with the rule {ToPrettyString(uid)}");
Del(mapUid);
ForceEndSelf(uid, rule); ForceEndSelf(uid, rule);
return; return;
} }

View File

@@ -3,7 +3,6 @@ using Content.Shared.CCVar;
using Content.Shared.GridPreloader.Prototypes; using Content.Shared.GridPreloader.Prototypes;
using Content.Shared.GridPreloader.Systems; using Content.Shared.GridPreloader.Systems;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
@@ -13,6 +12,7 @@ using System.Numerics;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.EntitySerialization.Systems;
namespace Content.Server.GridPreloader; namespace Content.Server.GridPreloader;
public sealed class GridPreloaderSystem : SharedGridPreloaderSystem public sealed class GridPreloaderSystem : SharedGridPreloaderSystem
@@ -72,23 +72,13 @@ public sealed class GridPreloaderSystem : SharedGridPreloaderSystem
{ {
for (var i = 0; i < proto.Copies; i++) for (var i = 0; i < proto.Copies; i++)
{ {
var options = new MapLoadOptions if (!_mapLoader.TryLoadGrid(mapId, proto.Path, out var grid))
{ {
LoadMap = false, Log.Error($"Failed to preload grid prototype {proto.ID}");
};
if (!_mapLoader.TryLoad(mapId, proto.Path.ToString(), out var roots, options))
continue; continue;
}
// only supports loading maps with one grid. var (gridUid, mapGrid) = grid.Value;
if (roots.Count != 1)
continue;
var gridUid = roots[0];
// gets grid + also confirms that the root we loaded is actually a grid
if (!TryComp<MapGridComponent>(gridUid, out var mapGrid))
continue;
if (!TryComp<PhysicsComponent>(gridUid, out var physics)) if (!TryComp<PhysicsComponent>(gridUid, out var physics))
continue; continue;

View File

@@ -3,12 +3,13 @@ using Content.Server.Administration;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Content.Server.Mapping namespace Content.Server.Mapping
{ {
@@ -91,8 +92,9 @@ namespace Content.Server.Mapping
} }
else else
{ {
var loadOptions = new MapLoadOptions {StoreMapUids = true}; var path = new ResPath(args[1]);
_entities.System<MapLoaderSystem>().TryLoad(mapId, args[1], out _, loadOptions); var opts = new DeserializationOptions {StoreYamlUids = true};
_entities.System<MapLoaderSystem>().TryLoadMapWithId(mapId, path, out _, out _, opts);
} }
// was the map actually created or did it fail somehow? // was the map actually created or did it fail somehow?

View File

@@ -4,6 +4,8 @@ using Content.Shared.Administration;
using Content.Shared.Mapping; using Content.Shared.Mapping;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -21,6 +23,7 @@ public sealed class MappingManager : IPostInjectInit
[Dependency] private readonly IServerNetManager _net = default!; [Dependency] private readonly IServerNetManager _net = default!;
[Dependency] private readonly IPlayerManager _players = default!; [Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly IEntitySystemManager _systems = default!; [Dependency] private readonly IEntitySystemManager _systems = default!;
[Dependency] private readonly IEntityManager _ent = default!;
private ISawmill _sawmill = default!; private ISawmill _sawmill = default!;
private ZStdCompressionContext _zstd = default!; private ZStdCompressionContext _zstd = default!;
@@ -45,14 +48,13 @@ public sealed class MappingManager : IPostInjectInit
if (!_players.TryGetSessionByChannel(message.MsgChannel, out var session) || if (!_players.TryGetSessionByChannel(message.MsgChannel, out var session) ||
!_admin.IsAdmin(session, true) || !_admin.IsAdmin(session, true) ||
!_admin.HasAdminFlag(session, AdminFlags.Host) || !_admin.HasAdminFlag(session, AdminFlags.Host) ||
session.AttachedEntity is not { } player) !_ent.TryGetComponent(session.AttachedEntity, out TransformComponent? xform) ||
xform.MapUid is not {} mapUid)
{ {
return; return;
} }
var mapId = _systems.GetEntitySystem<TransformSystem>().GetMapCoordinates(player).MapId; var data = _systems.GetEntitySystem<MapLoaderSystem>().SerializeEntityRecursive(mapUid).Node;
var mapEntity = _map.GetMapEntityIdOrThrow(mapId);
var data = _systems.GetEntitySystem<MapLoaderSystem>().GetSaveData(mapEntity);
var document = new YamlDocument(data.ToYaml()); var document = new YamlDocument(data.ToYaml());
var stream = new YamlStream { document }; var stream = new YamlStream { document };
var writer = new StringWriter(); var writer = new StringWriter();

View File

@@ -3,10 +3,10 @@ using Content.Server.Administration;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -23,7 +23,7 @@ public sealed class MappingSystem : EntitySystem
[Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IResourceManager _resMan = default!; [Dependency] private readonly IResourceManager _resMan = default!;
[Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly MapLoaderSystem _loader = default!;
// Not a comp because I don't want to deal with this getting saved onto maps ever // Not a comp because I don't want to deal with this getting saved onto maps ever
/// <summary> /// <summary>
@@ -78,7 +78,7 @@ public sealed class MappingSystem : EntitySystem
var path = Path.Combine(saveDir, $"{DateTime.Now.ToString("yyyy-M-dd_HH.mm.ss")}-AUTO.yml"); var path = Path.Combine(saveDir, $"{DateTime.Now.ToString("yyyy-M-dd_HH.mm.ss")}-AUTO.yml");
_currentlyAutosaving[map] = (CalculateNextTime(), name); _currentlyAutosaving[map] = (CalculateNextTime(), name);
Log.Info($"Autosaving map {name} ({map}) to {path}. Next save in {ReadableTimeLeft(map)} seconds."); Log.Info($"Autosaving map {name} ({map}) to {path}. Next save in {ReadableTimeLeft(map)} seconds.");
_map.SaveMap(map, path); _loader.SaveMap(map, path);
} }
} }

View File

@@ -25,6 +25,11 @@ public sealed partial class GameMapPrototype : IPrototype
[DataField] [DataField]
public float MaxRandomOffset = 1000f; public float MaxRandomOffset = 1000f;
/// <summary>
/// Turns out some of the map files are actually secretly grids. Excellent. I love map loading code.
/// </summary>
[DataField] public bool IsGrid;
[DataField] [DataField]
public bool RandomRotation = true; public bool RandomRotation = true;

View File

@@ -2,8 +2,8 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map.Events; using Robust.Shared.Map.Events;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Markdown; using Robust.Shared.Serialization.Markdown;

View File

@@ -1,11 +1,11 @@
using System.Linq; using System.Linq;
using Content.Server.Administration; using Content.Server.Administration;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.Map; using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Components;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Maps; namespace Content.Server.Maps;
@@ -17,8 +17,8 @@ namespace Content.Server.Maps;
public sealed class ResaveCommand : LocalizedCommands public sealed class ResaveCommand : LocalizedCommands
{ {
[Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IResourceManager _res = default!; [Dependency] private readonly IResourceManager _res = default!;
[Dependency] private readonly ILogManager _log = default!;
public override string Command => "resave"; public override string Command => "resave";
@@ -26,32 +26,56 @@ public sealed class ResaveCommand : LocalizedCommands
{ {
var loader = _entManager.System<MapLoaderSystem>(); var loader = _entManager.System<MapLoaderSystem>();
foreach (var fn in _res.ContentFindFiles(new ResPath("/Maps/"))) var opts = MapLoadOptions.Default with
{ {
var mapId = _mapManager.CreateMap();
_mapManager.AddUninitializedMap(mapId); DeserializationOptions = DeserializationOptions.Default with
loader.Load(mapId, fn.ToString(), new MapLoadOptions()
{ {
StoreMapUids = true, StoreYamlUids = true,
LoadMap = true, LogOrphanedGrids = false
}); }
};
var log = _log.GetSawmill(Command);
var files = _res.ContentFindFiles(new ResPath("/Maps/")).ToList();
for (var i = 0; i < files.Count; i++)
{
var fn = files[i];
log.Info($"Re-saving file {i}/{files.Count} : {fn}");
if (!loader.TryLoadEntities(fn, out var result, opts))
continue;
if (result.Maps.Count != 1)
{
shell.WriteError(
$"Multi-map or multi-grid files like {fn} are not yet supported by the {Command} command");
loader.Delete(result);
continue;
}
var map = result.Maps.First();
// Process deferred component removals. // Process deferred component removals.
_entManager.CullRemovedComponents(); _entManager.CullRemovedComponents();
var mapUid = _mapManager.GetMapEntityId(mapId); if (_entManager.HasComponent<LoadedMapComponent>(map))
var mapXform = _entManager.GetComponent<TransformComponent>(mapUid);
if (_entManager.HasComponent<LoadedMapComponent>(mapUid) || mapXform.ChildCount != 1)
{ {
loader.SaveMap(mapId, fn.ToString()); loader.SaveMap(map.Comp.MapId, fn);
} }
else if (mapXform.ChildEnumerator.MoveNext(out var child)) else if (result.Grids.Count == 1)
{ {
loader.Save(child, fn.ToString()); loader.SaveGrid(result.Grids.First(), fn);
}
else
{
shell.WriteError($"Failed to resave {fn}");
} }
_mapManager.DeleteMap(mapId); loader.Delete(result);
} }
shell.WriteLine($"Resaved all maps");
} }
} }

View File

@@ -15,10 +15,13 @@ using Robust.Server.GameObjects;
using Robust.Shared.Collections; using Robust.Shared.Collections;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility;
namespace Content.Server.Procedural; namespace Content.Server.Procedural;
@@ -173,14 +176,18 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
return Transform(uid).MapID; return Transform(uid).MapID;
} }
var mapId = _mapManager.CreateMap(); var opts = new MapLoadOptions
_mapManager.AddUninitializedMap(mapId); {
_loader.Load(mapId, proto.AtlasPath.ToString()); DeserializationOptions = DeserializationOptions.Default with {PauseMaps = true},
var mapUid = _mapManager.GetMapEntityId(mapId); ExpectedCategory = FileCategory.Map
_mapManager.SetMapPaused(mapId, true); };
comp = AddComp<DungeonAtlasTemplateComponent>(mapUid);
if (!_loader.TryLoadEntities(proto.AtlasPath, out var res, opts) || !res.Maps.TryFirstOrNull(out var map))
throw new Exception($"Failed to load dungeon template.");
comp = AddComp<DungeonAtlasTemplateComponent>(map.Value.Owner);
comp.Path = proto.AtlasPath; comp.Path = proto.AtlasPath;
return mapId; return map.Value.Comp.MapId;
} }
/// <summary> /// <summary>

View File

@@ -2,14 +2,11 @@ using System.Linq;
using System.Numerics; using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.Salvage.Magnet; using Content.Server.Salvage.Magnet;
using Content.Shared.Humanoid;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Procedural; using Content.Shared.Procedural;
using Content.Shared.Radio; using Content.Shared.Radio;
using Content.Shared.Salvage.Magnet; using Content.Shared.Salvage.Magnet;
using Robust.Server.Maps;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components;
namespace Content.Server.Salvage; namespace Content.Server.Salvage;
@@ -278,15 +275,10 @@ public sealed partial class SalvageSystem
case SalvageOffering wreck: case SalvageOffering wreck:
var salvageProto = wreck.SalvageMap; var salvageProto = wreck.SalvageMap;
var opts = new MapLoadOptions if (!_loader.TryLoadGrid(salvMapXform.MapID, salvageProto.MapPath, out _))
{
Offset = new Vector2(0, 0)
};
if (!_map.TryLoad(salvMapXform.MapID, salvageProto.MapPath.ToString(), out _, opts))
{ {
Report(magnet, MagnetChannel, "salvage-system-announcement-spawn-debris-disintegrated"); Report(magnet, MagnetChannel, "salvage-system-announcement-spawn-debris-disintegrated");
_mapManager.DeleteMap(salvMapXform.MapID); _mapSystem.DeleteMap(salvMapXform.MapID);
return; return;
} }

View File

@@ -1,38 +1,23 @@
using System.Linq;
using System.Numerics;
using Content.Server.Cargo.Systems;
using Content.Server.Construction;
using Content.Server.GameTicking;
using Content.Server.Radio.EntitySystems; using Content.Server.Radio.EntitySystems;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Radio; using Content.Shared.Radio;
using Content.Shared.Salvage; using Content.Shared.Salvage;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.Gravity; using Content.Server.Gravity;
using Content.Server.Parallax; using Content.Server.Parallax;
using Content.Server.Procedural; using Content.Server.Procedural;
using Content.Server.Shuttles.Systems; using Content.Server.Shuttles.Systems;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Shared.CCVar;
using Content.Shared.Construction.EntitySystems; using Content.Shared.Construction.EntitySystems;
using Content.Shared.Random;
using Content.Shared.Random.Helpers;
using Content.Shared.Tools.Components;
using Robust.Server.Maps;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Content.Server.Labels; using Content.Server.Labels;
using Robust.Shared.EntitySerialization.Systems;
namespace Content.Server.Salvage namespace Content.Server.Salvage
{ {
@@ -50,7 +35,7 @@ namespace Content.Server.Salvage
[Dependency] private readonly DungeonSystem _dungeon = default!; [Dependency] private readonly DungeonSystem _dungeon = default!;
[Dependency] private readonly GravitySystem _gravity = default!; [Dependency] private readonly GravitySystem _gravity = default!;
[Dependency] private readonly LabelSystem _labelSystem = default!; [Dependency] private readonly LabelSystem _labelSystem = default!;
[Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly MapLoaderSystem _loader = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly RadioSystem _radioSystem = default!; [Dependency] private readonly RadioSystem _radioSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;

View File

@@ -30,11 +30,13 @@ using Robust.Server.GameObjects;
using Robust.Shared.Collections; using Robust.Shared.Collections;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility;
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
namespace Content.Server.Shuttles.Systems; namespace Content.Server.Shuttles.Systems;
@@ -512,15 +514,13 @@ public sealed class ArrivalsSystem : EntitySystem
private void SetupArrivalsStation() private void SetupArrivalsStation()
{ {
var mapUid = _mapSystem.CreateMap(out var mapId, false); var path = new ResPath(_cfgManager.GetCVar(CCVars.ArrivalsMap));
_metaData.SetEntityName(mapUid, Loc.GetString("map-name-terminal")); if (!_loader.TryLoadMap(path, out var map, out var grids))
if (!_loader.TryLoad(mapId, _cfgManager.GetCVar(CCVars.ArrivalsMap), out var uids))
{
return; return;
}
foreach (var id in uids) _metaData.SetEntityName(map.Value, Loc.GetString("map-name-terminal"));
foreach (var id in grids)
{ {
EnsureComp<ArrivalsSourceComponent>(id); EnsureComp<ArrivalsSourceComponent>(id);
EnsureComp<ProtectedGridComponent>(id); EnsureComp<ProtectedGridComponent>(id);
@@ -531,15 +531,15 @@ public sealed class ArrivalsSystem : EntitySystem
if (_cfgManager.GetCVar(CCVars.ArrivalsPlanet)) if (_cfgManager.GetCVar(CCVars.ArrivalsPlanet))
{ {
var template = _random.Pick(_arrivalsBiomeOptions); var template = _random.Pick(_arrivalsBiomeOptions);
_biomes.EnsurePlanet(mapUid, _protoManager.Index(template)); _biomes.EnsurePlanet(map.Value, _protoManager.Index(template));
var restricted = new RestrictedRangeComponent var restricted = new RestrictedRangeComponent
{ {
Range = 32f Range = 32f
}; };
AddComp(mapUid, restricted); AddComp(map.Value, restricted);
} }
_mapSystem.InitializeMap(mapId); _mapSystem.InitializeMap(map.Value.Comp.MapId);
// Handle roundstart stations. // Handle roundstart stations.
var query = AllEntityQuery<StationArrivalsComponent>(); var query = AllEntityQuery<StationArrivalsComponent>();
@@ -600,9 +600,9 @@ public sealed class ArrivalsSystem : EntitySystem
var dummpMapEntity = _mapSystem.CreateMap(out var dummyMapId); var dummpMapEntity = _mapSystem.CreateMap(out var dummyMapId);
if (TryGetArrivals(out var arrivals) && if (TryGetArrivals(out var arrivals) &&
_loader.TryLoad(dummyMapId, component.ShuttlePath.ToString(), out var shuttleUids)) _loader.TryLoadGrid(dummyMapId, component.ShuttlePath, out var shuttle))
{ {
component.Shuttle = shuttleUids[0]; component.Shuttle = shuttle.Value;
var shuttleComp = Comp<ShuttleComponent>(component.Shuttle); var shuttleComp = Comp<ShuttleComponent>(component.Shuttle);
var arrivalsComp = EnsureComp<ArrivalsShuttleComponent>(component.Shuttle); var arrivalsComp = EnsureComp<ArrivalsShuttleComponent>(component.Shuttle);
arrivalsComp.Station = uid; arrivalsComp.Station = uid;

View File

@@ -29,10 +29,9 @@ using Content.Shared.Shuttles.Events;
using Content.Shared.Tag; using Content.Shared.Tag;
using Content.Shared.Tiles; using Content.Shared.Tiles;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Map; using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -60,7 +59,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
[Dependency] private readonly DockingSystem _dock = default!; [Dependency] private readonly DockingSystem _dock = default!;
[Dependency] private readonly IdCardSystem _idSystem = default!; [Dependency] private readonly IdCardSystem _idSystem = default!;
[Dependency] private readonly NavMapSystem _navMap = default!; [Dependency] private readonly NavMapSystem _navMap = default!;
[Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly MapLoaderSystem _loader = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly RoundEndSystem _roundEnd = default!; [Dependency] private readonly RoundEndSystem _roundEnd = default!;
@@ -531,10 +530,11 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
} }
var map = _mapSystem.CreateMap(out var mapId); var map = _mapSystem.CreateMap(out var mapId);
var grid = _map.LoadGrid(mapId, component.Map.ToString(), new MapLoadOptions() if (!_loader.TryLoadGrid(mapId, component.Map, out var grid))
{ {
LoadMap = false, Log.Error($"Failed to set up centcomm grid!");
}); return;
}
if (!Exists(map)) if (!Exists(map))
{ {
@@ -608,15 +608,11 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
// Load escape shuttle // Load escape shuttle
var shuttlePath = ent.Comp1.EmergencyShuttlePath; var shuttlePath = ent.Comp1.EmergencyShuttlePath;
var shuttle = _map.LoadGrid(map.MapId, shuttlePath.ToString(), new MapLoadOptions() if (!_loader.TryLoadGrid(map.MapId,
{ shuttlePath,
out var shuttle,
// Should be far enough... right? I'm too lazy to bounds check CentCom rn. // Should be far enough... right? I'm too lazy to bounds check CentCom rn.
Offset = new Vector2(500f + ent.Comp2.ShuttleIndex, 0f), offset: new Vector2(500f + ent.Comp2.ShuttleIndex, 0f)))
// fun fact: if you just fucking yeet centcomm into nullspace anytime you try to spawn the shuttle, then any distance is far enough. so lets not do that
LoadMap = false,
});
if (shuttle == null)
{ {
Log.Error($"Unable to spawn emergency shuttle {shuttlePath} for {ToPrettyString(ent)}"); Log.Error($"Unable to spawn emergency shuttle {shuttlePath} for {ToPrettyString(ent)}");
return; return;

View File

@@ -72,17 +72,15 @@ public sealed partial class ShuttleSystem
_mapSystem.CreateMap(out var mapId); _mapSystem.CreateMap(out var mapId);
if (_loader.TryLoad(mapId, component.Path.ToString(), out var ent) && ent.Count > 0) if (_loader.TryLoadGrid(mapId, component.Path, out var ent))
{ {
if (HasComp<ShuttleComponent>(ent[0])) if (HasComp<ShuttleComponent>(ent))
{ TryFTLProximity(ent.Value, targetGrid.Value);
TryFTLProximity(ent[0], targetGrid.Value);
_station.AddGridToStation(uid, ent.Value);
} }
_station.AddGridToStation(uid, ent[0]); _mapSystem.DeleteMap(mapId);
}
_mapManager.DeleteMap(mapId);
} }
private bool TryDungeonSpawn(Entity<MapGridComponent?> targetGrid, DungeonSpawnGroup group, out EntityUid spawned) private bool TryDungeonSpawn(Entity<MapGridComponent?> targetGrid, DungeonSpawnGroup group, out EntityUid spawned)
@@ -143,20 +141,18 @@ public sealed partial class ShuttleSystem
var path = paths[^1]; var path = paths[^1];
paths.RemoveAt(paths.Count - 1); paths.RemoveAt(paths.Count - 1);
if (_loader.TryLoad(mapId, path.ToString(), out var ent) && ent.Count == 1) if (_loader.TryLoadGrid(mapId, path, out var grid))
{ {
if (HasComp<ShuttleComponent>(ent[0])) if (HasComp<ShuttleComponent>(grid))
{ TryFTLProximity(grid.Value, targetGrid);
TryFTLProximity(ent[0], targetGrid);
}
if (group.NameGrid) if (group.NameGrid)
{ {
var name = path.FilenameWithoutExtension; var name = path.FilenameWithoutExtension;
_metadata.SetEntityName(ent[0], name); _metadata.SetEntityName(grid.Value, name);
} }
spawned = ent[0]; spawned = grid.Value;
return true; return true;
} }
@@ -227,7 +223,7 @@ public sealed partial class ShuttleSystem
} }
} }
_mapManager.DeleteMap(mapId); _mapSystem.DeleteMap(mapId);
} }
private void OnGridFillMapInit(EntityUid uid, GridFillComponent component, MapInitEvent args) private void OnGridFillMapInit(EntityUid uid, GridFillComponent component, MapInitEvent args)
@@ -246,23 +242,22 @@ public sealed partial class ShuttleSystem
_mapSystem.CreateMap(out var mapId); _mapSystem.CreateMap(out var mapId);
var valid = false; var valid = false;
if (_loader.TryLoad(mapId, component.Path.ToString(), out var ent) && if (_loader.TryLoadGrid(mapId, component.Path, out var grid))
ent.Count == 1 &&
TryComp(ent[0], out TransformComponent? shuttleXform))
{ {
var escape = GetSingleDock(ent[0]); var escape = GetSingleDock(grid.Value);
if (escape != null) if (escape != null)
{ {
var config = _dockSystem.GetDockingConfig(ent[0], xform.GridUid.Value, escape.Value.Entity, escape.Value.Component, uid, dock); var config = _dockSystem.GetDockingConfig(grid.Value, xform.GridUid.Value, escape.Value.Entity, escape.Value.Component, uid, dock);
if (config != null) if (config != null)
{ {
FTLDock((ent[0], shuttleXform), config); var shuttleXform = Transform(grid.Value);
FTLDock((grid.Value, shuttleXform), config);
if (TryComp<StationMemberComponent>(xform.GridUid, out var stationMember)) if (TryComp<StationMemberComponent>(xform.GridUid, out var stationMember))
{ {
_station.AddGridToStation(stationMember.Station, ent[0]); _station.AddGridToStation(stationMember.Station, grid.Value);
} }
valid = true; valid = true;
@@ -273,11 +268,11 @@ public sealed partial class ShuttleSystem
{ {
var compType = compReg.Component.GetType(); var compType = compReg.Component.GetType();
if (HasComp(ent[0], compType)) if (HasComp(grid.Value, compType))
continue; continue;
var comp = _factory.GetComponent(compType); var comp = _factory.GetComponent(compType);
AddComp(ent[0], comp, true); AddComp(grid.Value, comp, true);
} }
} }
@@ -286,7 +281,7 @@ public sealed partial class ShuttleSystem
Log.Error($"Error loading gridfill dock for {ToPrettyString(uid)} / {component.Path}"); Log.Error($"Error loading gridfill dock for {ToPrettyString(uid)} / {component.Path}");
} }
_mapManager.DeleteMap(mapId); _mapSystem.DeleteMap(mapId);
} }
private (EntityUid Entity, DockingComponent Component)? GetSingleDock(EntityUid uid) private (EntityUid Entity, DockingComponent Component)? GetSingleDock(EntityUid uid)

View File

@@ -17,6 +17,7 @@ using Robust.Server.GameStates;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Physics; using Robust.Shared.Physics;

View File

@@ -8,6 +8,7 @@ using Content.Shared.Movement.Events;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Input.Binding; using Robust.Shared.Input.Binding;
using Robust.Shared.Map.Components;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -207,7 +208,7 @@ namespace Content.Shared.Movement.Systems
} }
// If we went from grid -> map we'll preserve our worldrotation // If we went from grid -> map we'll preserve our worldrotation
if (relative != null && _mapManager.IsMap(relative.Value)) if (relative != null && HasComp<MapComponent>(relative.Value))
{ {
targetRotation = currentRotation.FlipPositive().Reduced(); targetRotation = currentRotation.FlipPositive().Reduced();
} }

View File

@@ -454,7 +454,7 @@
duration: 1 duration: 1
- type: RuleGrids - type: RuleGrids
- type: LoadMapRule - type: LoadMapRule
mapPath: /Maps/Shuttles/ShuttleEvent/striker.yml gridPath: /Maps/Shuttles/ShuttleEvent/striker.yml
- type: NukeopsRule - type: NukeopsRule
roundEndBehavior: Nothing roundEndBehavior: Nothing
- type: AntagSelection - type: AntagSelection

View File

@@ -1,5 +1,6 @@
- type: gameMap - type: gameMap
id: CentComm id: CentComm
isGrid: true # Did you know that centcomm is the only "game map" that isn't actually a map? Send help.
mapName: 'Central Command' mapName: 'Central Command'
mapPath: /Maps/centcomm.yml mapPath: /Maps/centcomm.yml
minPlayers: 10 minPlayers: 10