From 6242567aff9cf3c1281ac7cf785df30d135800a2 Mon Sep 17 00:00:00 2001 From: ElectroJr Date: Sun, 22 Dec 2024 15:13:10 +1300 Subject: [PATCH] Refactor map loading & saving --- .../Tests/Body/LungTest.cs | 24 +- .../Tests/Body/SaveLoadReparentTest.cs | 11 +- .../Minds/MindTest.DeleteAllThenGhost.cs | 7 +- .../Tests/PostMapInitTest.cs | 80 +++--- Content.IntegrationTests/Tests/SalvageTest.cs | 15 +- .../Tests/SaveLoadMapTest.cs | 15 +- .../Tests/SaveLoadSaveTest.cs | 110 +++++---- .../Tests/Shuttle/DockTest.cs | 7 +- .../Commands/LoadGameMapCommand.cs | 44 ++-- .../Commands/PersistenceSaveCommand.cs | 1 + .../Systems/AdminTestArenaSystem.cs | 40 ++- .../GameTicking/GameTicker.RoundFlow.cs | 230 ++++++++++++++---- Content.Server/GameTicking/GameTicker.cs | 4 +- .../Rules/Components/LoadMapRuleComponent.cs | 8 +- .../GameTicking/Rules/LoadMapRuleSystem.cs | 50 +++- .../GridPreloader/GridPreloaderSystem.cs | 20 +- Content.Server/Mapping/MappingCommand.cs | 10 +- Content.Server/Mapping/MappingManager.cs | 10 +- Content.Server/Mapping/MappingSystem.cs | 6 +- Content.Server/Maps/GameMapPrototype.cs | 5 + Content.Server/Maps/MapMigrationSystem.cs | 2 +- Content.Server/Maps/ResaveCommand.cs | 62 +++-- Content.Server/Procedural/DungeonSystem.cs | 21 +- .../Salvage/SalvageSystem.Magnet.cs | 12 +- Content.Server/Salvage/SalvageSystem.cs | 19 +- .../Shuttles/Systems/ArrivalsSystem.cs | 24 +- .../Systems/EmergencyShuttleSystem.cs | 24 +- .../Systems/ShuttleSystem.GridFill.cs | 45 ++-- .../Shuttles/Systems/ShuttleSystem.cs | 1 + .../Systems/SharedMoverController.Input.cs | 3 +- Resources/Prototypes/GameRules/events.yml | 2 +- Resources/Prototypes/Maps/centcomm.yml | 1 + RobustToolbox | 2 +- 33 files changed, 553 insertions(+), 362 deletions(-) diff --git a/Content.IntegrationTests/Tests/Body/LungTest.cs b/Content.IntegrationTests/Tests/Body/LungTest.cs index 9b5ee431f1..8ac3a3021f 100644 --- a/Content.IntegrationTests/Tests/Body/LungTest.cs +++ b/Content.IntegrationTests/Tests/Body/LungTest.cs @@ -11,6 +11,8 @@ using Robust.Shared.Map; using Robust.Shared.Map.Components; using System.Linq; using System.Numerics; +using Robust.Shared.EntitySerialization.Systems; +using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests.Body { @@ -57,7 +59,6 @@ namespace Content.IntegrationTests.Tests.Body await server.WaitIdleAsync(); - var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var mapLoader = entityManager.System(); var mapSys = entityManager.System(); @@ -69,17 +70,13 @@ namespace Content.IntegrationTests.Tests.Body GridAtmosphereComponent relevantAtmos = default; 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(() => { mapSys.CreateMap(out var mapId); - Assert.That(mapLoader.TryLoad(mapId, testMapName, out var roots)); - - var query = entityManager.GetEntityQuery(); - var grids = roots.Where(x => query.HasComponent(x)); - Assert.That(grids, Is.Not.Empty); - grid = grids.First(); + Assert.That(mapLoader.TryLoadGrid(mapId, testMapName, out var gridEnt)); + grid = gridEnt!.Value.Owner; }); Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found."); @@ -148,18 +145,13 @@ namespace Content.IntegrationTests.Tests.Body RespiratorComponent respirator = null; 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(() => { mapSys.CreateMap(out var mapId); - - Assert.That(mapLoader.TryLoad(mapId, testMapName, out var ents), Is.True); - var query = entityManager.GetEntityQuery(); - grid = ents - .Select(x => x) - .FirstOrDefault((uid) => uid.HasValue && query.HasComponent(uid.Value), null); - Assert.That(grid, Is.Not.Null); + Assert.That(mapLoader.TryLoadGrid(mapId, testMapName, out var gridEnt)); + grid = gridEnt!.Value.Owner; }); Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found."); diff --git a/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs b/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs index 01482ba8ee..f56b5c2850 100644 --- a/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs +++ b/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs @@ -4,8 +4,10 @@ using Content.Shared.Body.Components; using Content.Shared.Body.Systems; using Robust.Server.GameObjects; using Robust.Shared.Containers; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.GameObjects; using Robust.Shared.Map; +using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests.Body; @@ -111,13 +113,12 @@ public sealed class SaveLoadReparentTest 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); - maps.DeleteMap(mapId); + mapSys.DeleteMap(mapId); - mapSys.CreateMap(out mapId); - Assert.That(mapLoader.TryLoad(mapId, mapPath, out _), Is.True); + Assert.That(mapLoader.TryLoadMap(mapPath, out var map, out _), Is.True); var query = EnumerateQueryEnumerator( entities.EntityQueryEnumerator() @@ -173,7 +174,7 @@ public sealed class SaveLoadReparentTest }); } - maps.DeleteMap(mapId); + entities.DeleteEntity(map); } }); diff --git a/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs b/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs index 7bc62dfe2b..ad4ddc2612 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs @@ -1,5 +1,6 @@ #nullable enable using Robust.Shared.Console; +using Robust.Shared.GameObjects; using Robust.Shared.Map; namespace Content.IntegrationTests.Tests.Minds; @@ -37,8 +38,8 @@ public sealed partial class MindTests Assert.That(pair.Client.EntMan.EntityCount, Is.EqualTo(0)); // Create a new map. - int mapId = 1; - await pair.Server.WaitPost(() => conHost.ExecuteCommand($"addmap {mapId}")); + MapId mapId = default; + await pair.Server.WaitPost(() => pair.Server.System().CreateMap(out mapId)); await pair.RunTicksSync(5); // 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.Server.EntMan.EntityExists(pair.PlayerData?.Mind)); 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(); } diff --git a/Content.IntegrationTests/Tests/PostMapInitTest.cs b/Content.IntegrationTests/Tests/PostMapInitTest.cs index 635a9dbe0f..f7562d934d 100644 --- a/Content.IntegrationTests/Tests/PostMapInitTest.cs +++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs @@ -9,7 +9,6 @@ using Content.Server.Spawners.Components; using Content.Server.Station.Components; using Content.Shared.CCVar; using Content.Shared.Roles; -using Robust.Server.GameObjects; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; using Robust.Shared.GameObjects; @@ -17,7 +16,8 @@ using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; using Content.Shared.Station.Components; -using FastAccessors; +using Robust.Shared.EntitySerialization; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Utility; using YamlDotNet.RepresentationModel; @@ -66,7 +66,7 @@ namespace Content.IntegrationTests.Tests "Amber", "Loop" - + }; /// @@ -81,33 +81,23 @@ namespace Content.IntegrationTests.Tests var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); var mapSystem = entManager.System(); - var mapManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); + var path = new ResPath(mapFile); await server.WaitPost(() => { mapSystem.CreateMap(out var mapId); try { -#pragma warning disable NUnit2045 - Assert.That(mapLoader.TryLoad(mapId, mapFile, out var roots)); - Assert.That(roots.Where(uid => entManager.HasComponent(uid)), Is.Not.Empty); -#pragma warning restore NUnit2045 + Assert.That(mapLoader.TryLoadGrid(mapId, path, out var grid)); } catch (Exception ex) { throw new Exception($"Failed to load map {mapFile}, was it saved as a map instead of a grid?", ex); } - try - { - mapManager.DeleteMap(mapId); - } - catch (Exception ex) - { - throw new Exception($"Failed to delete map {mapFile}", ex); - } + mapSystem.DeleteMap(mapId); }); await server.WaitRunTicks(1); @@ -172,16 +162,16 @@ namespace Content.IntegrationTests.Tests var protoManager = server.ResolveDependency(); var ticker = entManager.EntitySysManager.GetEntitySystem(); var shuttleSystem = entManager.EntitySysManager.GetEntitySystem(); - var xformQuery = entManager.GetEntityQuery(); var cfg = server.ResolveDependency(); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitPost(() => { - mapSystem.CreateMap(out var mapId); + MapId mapId; try { - ticker.LoadGameMap(protoManager.Index(mapProto), mapId, null); + var opts = DeserializationOptions.Default with {InitializeMaps = true}; + ticker.LoadGameMap(protoManager.Index(mapProto), out mapId, opts); } catch (Exception ex) { @@ -218,21 +208,17 @@ namespace Content.IntegrationTests.Tests if (entManager.TryGetComponent(station, out var stationEvac)) { var shuttlePath = stationEvac.EmergencyShuttlePath; -#pragma warning disable NUnit2045 - Assert.That(mapLoader.TryLoad(shuttleMap, shuttlePath.ToString(), out var roots)); - EntityUid shuttle = default!; - Assert.DoesNotThrow(() => - { - shuttle = roots.First(uid => entManager.HasComponent(uid)); - }, $"Failed to load {shuttlePath}"); + Assert.That(mapLoader.TryLoadGrid(shuttleMap, shuttlePath, out var shuttle), + $"Failed to load {shuttlePath}"); + Assert.That( - shuttleSystem.TryFTLDock(shuttle, - entManager.GetComponent(shuttle), targetGrid.Value), + shuttleSystem.TryFTLDock(shuttle!.Value.Owner, + entManager.GetComponent(shuttle!.Value.Owner), + targetGrid.Value), $"Unable to dock {shuttlePath} to {mapProto}"); -#pragma warning restore NUnit2045 } - mapManager.DeleteMap(shuttleMap); + mapSystem.DeleteMap(shuttleMap); if (entManager.HasComponent(station)) { @@ -269,7 +255,7 @@ namespace Content.IntegrationTests.Tests try { - mapManager.DeleteMap(mapId); + mapSystem.DeleteMap(mapId); } catch (Exception ex) { @@ -333,11 +319,9 @@ namespace Content.IntegrationTests.Tests var server = pair.Server; var mapLoader = server.ResolveDependency().GetEntitySystem(); - var mapManager = server.ResolveDependency(); var resourceManager = server.ResolveDependency(); var protoManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); - var mapSystem = server.System(); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); var gameMaps = protoManager.EnumeratePrototypes().Select(o => o.MapPath).ToHashSet(); @@ -348,7 +332,7 @@ namespace Content.IntegrationTests.Tests .Where(filePath => filePath.Extension == "yml" && !filePath.Filename.StartsWith(".", StringComparison.Ordinal)) .ToArray(); - var mapNames = new List(); + var mapPaths = new List(); foreach (var map in maps) { if (gameMaps.Contains(map)) @@ -359,32 +343,46 @@ namespace Content.IntegrationTests.Tests { continue; } - mapNames.Add(rootedPath.ToString()); + mapPaths.Add(rootedPath); } await server.WaitPost(() => { 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> maps; + foreach (var path in mapPaths) { - mapSystem.CreateMap(out var mapId); try { - Assert.That(mapLoader.TryLoad(mapId, mapName, out _)); + Assert.That(mapLoader.TryLoadEntities(path, out maps, out _, opts)); } catch (Exception ex) { - throw new Exception($"Failed to load map {mapName}", ex); + throw new Exception($"Failed to load map {path}", ex); } try { - mapManager.DeleteMap(mapId); + foreach (var map in maps) + { + server.EntMan.DeleteEntity(map); + } } catch (Exception ex) { - throw new Exception($"Failed to delete map {mapName}", ex); + throw new Exception($"Failed to delete map {path}", ex); } } }); diff --git a/Content.IntegrationTests/Tests/SalvageTest.cs b/Content.IntegrationTests/Tests/SalvageTest.cs index 5dfba82308..0059db6292 100644 --- a/Content.IntegrationTests/Tests/SalvageTest.cs +++ b/Content.IntegrationTests/Tests/SalvageTest.cs @@ -1,11 +1,8 @@ -using System.Linq; -using Content.Shared.CCVar; +using Content.Shared.CCVar; using Content.Shared.Salvage; -using Robust.Server.GameObjects; using Robust.Shared.Configuration; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; @@ -24,7 +21,6 @@ public sealed class SalvageTest var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); - var mapManager = server.ResolveDependency(); var prototypeManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); var mapSystem = entManager.System(); @@ -34,13 +30,10 @@ public sealed class SalvageTest { foreach (var salvage in prototypeManager.EnumeratePrototypes()) { - var mapFile = salvage.MapPath; - mapSystem.CreateMap(out var mapId); try { - Assert.That(mapLoader.TryLoad(mapId, mapFile.ToString(), out var roots)); - Assert.That(roots.Where(uid => entManager.HasComponent(uid)), Is.Not.Empty); + Assert.That(mapLoader.TryLoadGrid(mapId, salvage.MapPath, out var grid)); } catch (Exception ex) { @@ -49,7 +42,7 @@ public sealed class SalvageTest try { - mapManager.DeleteMap(mapId); + mapSystem.DeleteMap(mapId); } catch (Exception ex) { diff --git a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs index 213da5d786..b1f6dd8433 100644 --- a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs @@ -3,6 +3,7 @@ using Content.Shared.CCVar; using Robust.Server.GameObjects; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Maths; @@ -16,7 +17,7 @@ namespace Content.IntegrationTests.Tests [Test] 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(); var server = pair.Server; @@ -31,7 +32,7 @@ namespace Content.IntegrationTests.Tests await server.WaitAssertion(() => { - var dir = new ResPath(mapPath).Directory; + var dir = mapPath.Directory; resManager.UserData.CreateDir(dir); mapSystem.CreateMap(out var mapId); @@ -48,14 +49,16 @@ namespace Content.IntegrationTests.Tests } Assert.Multiple(() => mapLoader.SaveMap(mapId, mapPath)); - Assert.Multiple(() => mapManager.DeleteMap(mapId)); + Assert.Multiple(() => mapSystem.DeleteMap(mapId)); }); await server.WaitIdleAsync(); + MapId newMap = default; 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(); @@ -63,7 +66,7 @@ namespace Content.IntegrationTests.Tests 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(gridUid, out var gridXform)) { 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(gridUid, out var gridXform)) { Assert.Fail(); diff --git a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs index 4facd5ee40..a398b55919 100644 --- a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs @@ -1,25 +1,25 @@ using System.IO; using System.Linq; using Content.Shared.CCVar; -using Robust.Server.GameObjects; -using Robust.Server.Maps; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.GameObjects; using Robust.Shared.Map; -using Robust.Shared.Map.Components; +using Robust.Shared.Map.Events; +using Robust.Shared.Serialization.Markdown.Mapping; using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests { /// - /// Tests that a map's yaml does not change when saved consecutively. + /// Tests that a grid's yaml does not change when saved consecutively. /// [TestFixture] public sealed class SaveLoadSaveTest { [Test] - public async Task SaveLoadSave() + public async Task CreateSaveLoadSaveGrid() { await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; @@ -30,22 +30,21 @@ namespace Content.IntegrationTests.Tests var cfg = server.ResolveDependency(); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); + var testSystem = server.System(); + testSystem.Enabled = true; + + var rp1 = new ResPath("/save load save 1.yml"); + var rp2 = new ResPath("/save load save 2.yml"); + await server.WaitPost(() => { mapSystem.CreateMap(out var mapId0); - // TODO: Properly find the "main" station grid. var grid0 = mapManager.CreateGridEntity(mapId0); - mapLoader.Save(grid0.Owner, "save load save 1.yml"); + entManager.RunMapInit(grid0.Owner, entManager.GetComponent(grid0)); + mapLoader.SaveGrid(grid0.Owner, rp1); mapSystem.CreateMap(out var mapId1); - EntityUid grid1 = default!; -#pragma warning disable NUnit2045 - 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(uid)); - }); -#pragma warning restore NUnit2045 - mapLoader.Save(grid1, "save load save 2.yml"); + Assert.That(mapLoader.TryLoadGrid(mapId1, rp1, out var grid1)); + mapLoader.SaveGrid(grid1!.Value, rp2); }); await server.WaitIdleAsync(); @@ -54,14 +53,12 @@ namespace Content.IntegrationTests.Tests string one; string two; - var rp1 = new ResPath("/save load save 1.yml"); await using (var stream = userData.Open(rp1, FileMode.Open)) using (var reader = new StreamReader(stream)) { one = await reader.ReadToEndAsync(); } - var rp2 = new ResPath("/save load save 2.yml"); await using (var stream = userData.Open(rp2, FileMode.Open)) using (var reader = new StreamReader(stream)) { @@ -87,6 +84,7 @@ namespace Content.IntegrationTests.Tests TestContext.Error.WriteLine(twoTmp); } }); + testSystem.Enabled = false; await pair.CleanReturnAsync(); } @@ -101,8 +99,12 @@ namespace Content.IntegrationTests.Tests await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; var mapLoader = server.ResolveDependency().GetEntitySystem(); - var mapManager = server.ResolveDependency(); - var mapSystem = server.System(); + var mapSys = server.System(); + var testSystem = server.System(); + 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; var cfg = server.ResolveDependency(); @@ -111,10 +113,10 @@ namespace Content.IntegrationTests.Tests // Load bagel.yml as uninitialized map, and save it to ensure it's up to date. server.Post(() => { - mapSystem.CreateMap(out mapId, runMapInit: false); - mapManager.SetMapPaused(mapId, true); - Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}"); - mapLoader.SaveMap(mapId, "load save ticks save 1.yml"); + var path = new ResPath(TestMap); + Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}"); + mapId = map!.Value.Comp.MapId; + mapLoader.SaveMap(mapId, rp1); }); // Run 5 ticks. @@ -122,7 +124,7 @@ namespace Content.IntegrationTests.Tests await server.WaitPost(() => { - mapLoader.SaveMap(mapId, "/load save ticks save 2.yml"); + mapLoader.SaveMap(mapId, rp2); }); await server.WaitIdleAsync(); @@ -131,13 +133,13 @@ namespace Content.IntegrationTests.Tests string one; 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)) { 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)) { 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(); } @@ -184,13 +187,15 @@ namespace Content.IntegrationTests.Tests var server = pair.Server; var mapLoader = server.System(); - var mapSystem = server.System(); - var mapManager = server.ResolveDependency(); + var mapSys = server.System(); var userData = server.ResolveDependency().UserData; var cfg = server.ResolveDependency(); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); + var testSystem = server.System(); + testSystem.Enabled = true; - MapId mapId = default; + MapId mapId1 = default; + MapId mapId2 = default; const string fileA = "/load tick load a.yml"; const string fileB = "/load tick load b.yml"; string yamlA; @@ -199,10 +204,10 @@ namespace Content.IntegrationTests.Tests // Load & save the first map server.Post(() => { - mapSystem.CreateMap(out mapId, runMapInit: false); - mapManager.SetMapPaused(mapId, true); - Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}"); - mapLoader.SaveMap(mapId, fileA); + var path = new ResPath(TestMap); + Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}"); + mapId1 = map!.Value.Comp.MapId; + mapLoader.SaveMap(mapId1, fileA); }); await server.WaitIdleAsync(); @@ -217,11 +222,10 @@ namespace Content.IntegrationTests.Tests // Load & save the second map server.Post(() => { - mapManager.DeleteMap(mapId); - mapSystem.CreateMap(out mapId, runMapInit: false); - mapManager.SetMapPaused(mapId, true); - Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}"); - mapLoader.SaveMap(mapId, fileB); + var path = new ResPath(TestMap); + Assert.That(mapLoader.TryLoadMap(path, out var map, out _), $"Failed to load test map {TestMap}"); + mapId2 = map!.Value.Comp.MapId; + mapLoader.SaveMap(mapId2, fileB); }); await server.WaitIdleAsync(); @@ -234,8 +238,32 @@ namespace Content.IntegrationTests.Tests 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(); } + + /// + /// 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. + /// + private sealed class SaveLoadSaveTestSystem : EntitySystem + { + public bool Enabled; + public override void Initialize() + { + SubscribeLocalEvent(OnAfterSave); + } + + private void OnAfterSave(AfterSaveEvent ev) + { + if (!Enabled) + return; + + // Remove timestamp. + ((MappingDataNode)ev.Node["meta"]).Remove("time"); + } + } } } diff --git a/Content.IntegrationTests/Tests/Shuttle/DockTest.cs b/Content.IntegrationTests/Tests/Shuttle/DockTest.cs index f6e99596e9..ab82a3d2f9 100644 --- a/Content.IntegrationTests/Tests/Shuttle/DockTest.cs +++ b/Content.IntegrationTests/Tests/Shuttle/DockTest.cs @@ -4,10 +4,12 @@ using System.Numerics; using Content.Server.Shuttles.Systems; using Content.Tests; using Robust.Server.GameObjects; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Maths; +using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests.Shuttle; @@ -106,8 +108,9 @@ public sealed class DockTest : ContentUnitTest { mapGrid = entManager.AddComponent(map.MapUid); entManager.DeleteEntity(map.Grid); - Assert.That(entManager.System().TryLoad(otherMap.MapId, "/Maps/Shuttles/emergency.yml", out var rootUids)); - shuttle = rootUids[0]; + var path = new ResPath("/Maps/Shuttles/emergency.yml"); + Assert.That(entManager.System().TryLoadGrid(otherMap.MapId, path, out var grid)); + shuttle = grid!.Value.Owner; var dockingConfig = dockingSystem.GetDockingConfig(shuttle, map.MapUid); Assert.That(dockingConfig, Is.EqualTo(null)); diff --git a/Content.Server/Administration/Commands/LoadGameMapCommand.cs b/Content.Server/Administration/Commands/LoadGameMapCommand.cs index ea31c1ecb1..034b50a9c5 100644 --- a/Content.Server/Administration/Commands/LoadGameMapCommand.cs +++ b/Content.Server/Administration/Commands/LoadGameMapCommand.cs @@ -1,11 +1,9 @@ -using System.Linq; using System.Numerics; using Content.Server.GameTicking; using Content.Server.Maps; using Content.Shared.Administration; -using Robust.Server.Maps; using Robust.Shared.Console; -using Robust.Shared.ContentPack; +using Robust.Shared.EntitySerialization; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -25,6 +23,7 @@ namespace Content.Server.Administration.Commands var prototypeManager = IoCManager.Resolve(); var entityManager = IoCManager.Resolve(); var gameTicker = entityManager.EntitySysManager.GetEntitySystem(); + var mapSys = entityManager.EntitySysManager.GetEntitySystem(); if (args.Length is not (2 or 4 or 5)) { @@ -32,29 +31,28 @@ namespace Content.Server.Administration.Commands return; } - if (prototypeManager.TryIndex(args[1], out var gameMap)) - { - if (!int.TryParse(args[0], out var mapId)) - return; - - var loadOptions = new MapLoadOptions() - { - LoadMap = false, - }; - - 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)) - { - loadOptions.Offset = new Vector2(x, y); - } - var grids = gameTicker.LoadGameMap(gameMap, new MapId(mapId), loadOptions, stationName); - shell.WriteLine($"Loaded {grids.Count} grids."); - } - else + if (!prototypeManager.TryIndex(args[1], out var gameMap)) { shell.WriteError($"The given map prototype {args[0]} is invalid."); + return; } + + if (!int.TryParse(args[0], out var mapId)) + return; + + var stationName = args.Length == 5 ? args[4] : null; + + Vector2? offset = null; + if (args.Length >= 4) + offset = new Vector2(int.Parse(args[2]), int.Parse(args[3])); + + 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."); } public CompletionResult GetCompletion(IConsoleShell shell, string[] args) diff --git a/Content.Server/Administration/Commands/PersistenceSaveCommand.cs b/Content.Server/Administration/Commands/PersistenceSaveCommand.cs index 7ef1932c56..dffe1eff7f 100644 --- a/Content.Server/Administration/Commands/PersistenceSaveCommand.cs +++ b/Content.Server/Administration/Commands/PersistenceSaveCommand.cs @@ -5,6 +5,7 @@ using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.Map; using System.Linq; +using Robust.Shared.EntitySerialization.Systems; namespace Content.Server.Administration.Commands; diff --git a/Content.Server/Administration/Systems/AdminTestArenaSystem.cs b/Content.Server/Administration/Systems/AdminTestArenaSystem.cs index e3671bcdfb..12bf0eba64 100644 --- a/Content.Server/Administration/Systems/AdminTestArenaSystem.cs +++ b/Content.Server/Administration/Systems/AdminTestArenaSystem.cs @@ -1,8 +1,7 @@ -using Robust.Server.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Network; using Robust.Shared.Player; +using Robust.Shared.Utility; namespace Content.Server.Administration.Systems; @@ -11,8 +10,7 @@ namespace Content.Server.Administration.Systems; /// public sealed class AdminTestArenaSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly MapLoaderSystem _map = default!; + [Dependency] private readonly MapLoaderSystem _loader = default!; [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; public const string ArenaMapPath = "/Maps/Test/admin_test_arena.yml"; @@ -28,26 +26,24 @@ public sealed class AdminTestArenaSystem : EntitySystem { return (arenaMap, arenaGrid); } - else - { - ArenaGrid[admin.UserId] = null; - return (arenaMap, null); - } - } - ArenaMap[admin.UserId] = _mapManager.GetMapEntityId(_mapManager.CreateMap()); - _metaDataSystem.SetEntityName(ArenaMap[admin.UserId], $"ATAM-{admin.Name}"); - var grids = _map.LoadMap(Comp(ArenaMap[admin.UserId]).MapId, ArenaMapPath); - if (grids.Count != 0) - { - _metaDataSystem.SetEntityName(grids[0], $"ATAG-{admin.Name}"); - ArenaGrid[admin.UserId] = grids[0]; - } - else - { + ArenaGrid[admin.UserId] = null; + return (arenaMap, null); } - return (ArenaMap[admin.UserId], ArenaGrid[admin.UserId]); + var path = new ResPath(ArenaMapPath); + if (!_loader.TryLoadMap(path, out var map, out var grids)) + throw new Exception($"Failed to load admin arena"); + + 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); } } diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index a7dd5d6ab6..37e7a4e1ce 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Numerics; using Content.Server.Announcements; using Content.Server.Discord; using Content.Server.GameTicking.Events; @@ -13,10 +14,12 @@ using Content.Shared.Players; using Content.Shared.Preferences; using JetBrains.Annotations; using Prometheus; -using Robust.Server.Maps; using Robust.Shared.Asynchronous; using Robust.Shared.Audio; +using Robust.Shared.EntitySerialization; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Random; @@ -92,9 +95,6 @@ namespace Content.Server.GameTicking AddGamePresetRules(); - DefaultMap = _mapManager.CreateMap(); - _mapManager.AddUninitializedMap(DefaultMap); - var maps = new List(); // 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. RaiseLocalEvent(new LoadingMapsEvent(maps)); - foreach (var map in maps) + if (maps.Count == 0) { - var toLoad = DefaultMap; - if (maps[0] != map) - { - // Create other maps for the others since we need to. - toLoad = _mapManager.CreateMap(); - _mapManager.AddUninitializedMap(toLoad); - } + _map.CreateMap(out var mapId, runMapInit: false); + DefaultMap = mapId; + return; + } - 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; + } /// /// 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. + /// This does not initialze maps, unles specified via the . /// - /// Game map prototype to load in. - /// Map to load into. - /// Map loading options, includes offset. + /// + /// This is basically a wrapper around a method that auto generate + /// some using information in a prototype, and raise some events to allow content + /// to modify the options and react to the map creation. + /// + /// Game map prototype to load in. + /// The id of the map that was loaded. + /// Entity loading options, including whether the maps should be initialized. /// Name to assign to the loaded station. /// All loaded entities and grids. - public IReadOnlyList LoadGameMap(GameMapPrototype map, MapId targetMapId, MapLoadOptions? loadOptions, string? stationName = null) + public IReadOnlyList 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. - // whereas the command can also be used on an existing map. - var loadOpts = loadOptions ?? new MapLoadOptions(); + var ev = RaisePreLoad(proto, options, offset, rot); - if (map.MaxRandomOffset != 0f) - loadOpts.Offset = _robustRandom.NextVector2(map.MaxRandomOffset); + if (ev.GameMap.IsGrid) + { + 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) - loadOpts.Rotation = _robustRandom.NextAngle(); + _metaData.SetEntityName(mapUid, proto.MapName); + var g = new List {grid.Value.Owner}; + RaiseLocalEvent(new PostGameMapLoad(proto, mapId, g, stationName)); + return g; + } - var ev = new PreGameMapLoad(targetMapId, map, loadOpts); - RaiseLocalEvent(ev); + if (!_loader.TryLoadMap(ev.GameMap.MapPath, + 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); + /// + /// Variant of that attempts to assign the provided to the + /// loaded map. + /// + public IReadOnlyList 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(); - RaiseLocalEvent(new PostGameMapLoad(map, targetMapId, gridUids, stationName)); + if (ev.GameMap.IsGrid) + { + 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 {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; + } + + /// + /// Variant of that loads and then merges a game map onto an existing map. + /// + public IReadOnlyList 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 {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; } @@ -274,7 +425,7 @@ namespace Content.Server.GameTicking } // MapInitialize *before* spawning players, our codebase is too shit to do it afterwards... - _mapManager.DoMapInitialize(DefaultMap); + _map.InitializeMap(DefaultMap); SpawnPlayers(readyPlayers, readyPlayerProfiles, force); @@ -714,21 +865,14 @@ namespace Content.Server.GameTicking /// You likely want to subscribe to this after StationSystem. /// [PublicAPI] - public sealed class PreGameMapLoad : EntityEventArgs + public sealed class PreGameMapLoad(GameMapPrototype gameMap, DeserializationOptions options, Vector2 offset, Angle rotation) : EntityEventArgs { - public readonly MapId Map; - public GameMapPrototype GameMap; - public MapLoadOptions Options; - - public PreGameMapLoad(MapId map, GameMapPrototype gameMap, MapLoadOptions options) - { - Map = map; - GameMap = gameMap; - Options = options; - } + public readonly GameMapPrototype GameMap = gameMap; + public DeserializationOptions Options = options; + public Vector2 Offset = offset; + public Angle Rotation = rotation; } - /// /// Event raised after the game loads a given map. /// diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index f8ba7e1d7d..d286fd641e 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -18,6 +18,7 @@ using Robust.Server.GameObjects; using Robust.Server.GameStates; using Robust.Shared.Audio.Systems; using Robust.Shared.Console; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -48,7 +49,8 @@ namespace Content.Server.GameTicking [Dependency] private readonly IServerPreferencesManager _prefsManager = default!; [Dependency] private readonly IServerDbManager _db = 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 SharedMindSystem _mind = default!; [Dependency] private readonly PlayTimeTrackingSystem _playTimeTrackings = default!; diff --git a/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs index 1f0505c60f..f09fff5eaf 100644 --- a/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs @@ -20,11 +20,17 @@ public sealed partial class LoadMapRuleComponent : Component public ProtoId? GameMap; /// - /// A map path to load on a new map. + /// A map to load. /// [DataField] public ResPath? MapPath; + /// + /// A grid to load on a new map. + /// + [DataField] + public ResPath? GridPath; + /// /// A to move to a new map. /// If there are no instances left nothing is done. diff --git a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs index f18115d3cf..003a74ef4b 100644 --- a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs @@ -1,10 +1,14 @@ +using System.Linq; using Content.Server.GameTicking.Rules.Components; using Content.Server.GridPreloader; using Content.Server.StationEvents.Events; using Content.Shared.GameTicking.Components; 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.Utility; namespace Content.Server.GameTicking.Rules; @@ -26,29 +30,50 @@ public sealed class LoadMapRuleSystem : StationEventSystem return; } - // grid preloading needs map to init after moving it - var mapUid = _map.CreateMap(out var mapId, runMapInit: comp.PreloadedGrid == null); - - Log.Info($"Created map {mapId} for {ToPrettyString(uid):rule}"); - + MapId mapId; IReadOnlyList grids; 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); - 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) { - var options = new MapLoadOptions { LoadMap = true }; - if (!_mapLoader.TryLoad(mapId, path.ToString(), out var roots, options)) + DebugTools.AssertNull(comp.GridPath); + 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}!"); - Del(mapUid); ForceEndSelf(uid, rule); 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 {grid.Value.Owner}; } else if (comp.PreloadedGrid is {} preloaded) { @@ -56,11 +81,11 @@ public sealed class LoadMapRuleSystem : StationEventSystem if (!_gridPreloader.TryGetPreloadedGrid(preloaded, out var loadedShuttle)) { Log.Error($"Failed to get a preloaded grid with {preloaded}!"); - Del(mapUid); ForceEndSelf(uid, rule); return; } + var mapUid = _map.CreateMap(out mapId, runMapInit: false); _transform.SetParent(loadedShuttle.Value, mapUid); grids = new List() { loadedShuttle.Value }; _map.InitializeMap(mapUid); @@ -68,7 +93,6 @@ public sealed class LoadMapRuleSystem : StationEventSystem else { Log.Error($"No valid map prototype or map path associated with the rule {ToPrettyString(uid)}"); - Del(mapUid); ForceEndSelf(uid, rule); return; } diff --git a/Content.Server/GridPreloader/GridPreloaderSystem.cs b/Content.Server/GridPreloader/GridPreloaderSystem.cs index e12ce41a31..d648acbb06 100644 --- a/Content.Server/GridPreloader/GridPreloaderSystem.cs +++ b/Content.Server/GridPreloader/GridPreloaderSystem.cs @@ -3,7 +3,6 @@ using Content.Shared.CCVar; using Content.Shared.GridPreloader.Prototypes; using Content.Shared.GridPreloader.Systems; using Robust.Server.GameObjects; -using Robust.Server.Maps; using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Map.Components; @@ -13,6 +12,7 @@ using System.Numerics; using Content.Server.GameTicking; using Content.Shared.GameTicking; using JetBrains.Annotations; +using Robust.Shared.EntitySerialization.Systems; namespace Content.Server.GridPreloader; public sealed class GridPreloaderSystem : SharedGridPreloaderSystem @@ -72,23 +72,13 @@ public sealed class GridPreloaderSystem : SharedGridPreloaderSystem { for (var i = 0; i < proto.Copies; i++) { - var options = new MapLoadOptions + if (!_mapLoader.TryLoadGrid(mapId, proto.Path, out var grid)) { - LoadMap = false, - }; - - if (!_mapLoader.TryLoad(mapId, proto.Path.ToString(), out var roots, options)) + Log.Error($"Failed to preload grid prototype {proto.ID}"); continue; + } - // only supports loading maps with one grid. - if (roots.Count != 1) - continue; - - var gridUid = roots[0]; - - // gets grid + also confirms that the root we loaded is actually a grid - if (!TryComp(gridUid, out var mapGrid)) - continue; + var (gridUid, mapGrid) = grid.Value; if (!TryComp(gridUid, out var physics)) continue; diff --git a/Content.Server/Mapping/MappingCommand.cs b/Content.Server/Mapping/MappingCommand.cs index 70647d3281..b85b0953dd 100644 --- a/Content.Server/Mapping/MappingCommand.cs +++ b/Content.Server/Mapping/MappingCommand.cs @@ -3,12 +3,13 @@ using Content.Server.Administration; using Content.Server.GameTicking; using Content.Shared.Administration; using Content.Shared.CCVar; -using Robust.Server.GameObjects; -using Robust.Server.Maps; using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.ContentPack; +using Robust.Shared.EntitySerialization; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; +using Robust.Shared.Utility; namespace Content.Server.Mapping { @@ -91,8 +92,9 @@ namespace Content.Server.Mapping } else { - var loadOptions = new MapLoadOptions {StoreMapUids = true}; - _entities.System().TryLoad(mapId, args[1], out _, loadOptions); + var path = new ResPath(args[1]); + var opts = new DeserializationOptions {StoreYamlUids = true}; + _entities.System().TryLoadMapWithId(mapId, path, out _, out _, opts); } // was the map actually created or did it fail somehow? diff --git a/Content.Server/Mapping/MappingManager.cs b/Content.Server/Mapping/MappingManager.cs index e8c6eca204..be6f503bf0 100644 --- a/Content.Server/Mapping/MappingManager.cs +++ b/Content.Server/Mapping/MappingManager.cs @@ -4,6 +4,8 @@ using Content.Shared.Administration; using Content.Shared.Mapping; using Robust.Server.GameObjects; using Robust.Server.Player; +using Robust.Shared.EntitySerialization; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Serialization; @@ -21,6 +23,7 @@ public sealed class MappingManager : IPostInjectInit [Dependency] private readonly IServerNetManager _net = default!; [Dependency] private readonly IPlayerManager _players = default!; [Dependency] private readonly IEntitySystemManager _systems = default!; + [Dependency] private readonly IEntityManager _ent = default!; private ISawmill _sawmill = default!; private ZStdCompressionContext _zstd = default!; @@ -45,14 +48,13 @@ public sealed class MappingManager : IPostInjectInit if (!_players.TryGetSessionByChannel(message.MsgChannel, out var session) || !_admin.IsAdmin(session, true) || !_admin.HasAdminFlag(session, AdminFlags.Host) || - session.AttachedEntity is not { } player) + !_ent.TryGetComponent(session.AttachedEntity, out TransformComponent? xform) || + xform.MapUid is not {} mapUid) { return; } - var mapId = _systems.GetEntitySystem().GetMapCoordinates(player).MapId; - var mapEntity = _map.GetMapEntityIdOrThrow(mapId); - var data = _systems.GetEntitySystem().GetSaveData(mapEntity); + var data = _systems.GetEntitySystem().SerializeEntityRecursive(mapUid).Node; var document = new YamlDocument(data.ToYaml()); var stream = new YamlStream { document }; var writer = new StringWriter(); diff --git a/Content.Server/Mapping/MappingSystem.cs b/Content.Server/Mapping/MappingSystem.cs index 28bb3afbe1..ecdbbe081c 100644 --- a/Content.Server/Mapping/MappingSystem.cs +++ b/Content.Server/Mapping/MappingSystem.cs @@ -3,10 +3,10 @@ using Content.Server.Administration; using Content.Shared.Administration; using Content.Shared.CCVar; using Robust.Server.GameObjects; -using Robust.Server.Maps; using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.ContentPack; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -23,7 +23,7 @@ public sealed class MappingSystem : EntitySystem [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IMapManager _mapManager = 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 /// @@ -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"); _currentlyAutosaving[map] = (CalculateNextTime(), name); Log.Info($"Autosaving map {name} ({map}) to {path}. Next save in {ReadableTimeLeft(map)} seconds."); - _map.SaveMap(map, path); + _loader.SaveMap(map, path); } } diff --git a/Content.Server/Maps/GameMapPrototype.cs b/Content.Server/Maps/GameMapPrototype.cs index 5942a9930e..c39ca348da 100644 --- a/Content.Server/Maps/GameMapPrototype.cs +++ b/Content.Server/Maps/GameMapPrototype.cs @@ -25,6 +25,11 @@ public sealed partial class GameMapPrototype : IPrototype [DataField] public float MaxRandomOffset = 1000f; + /// + /// Turns out some of the map files are actually secretly grids. Excellent. I love map loading code. + /// + [DataField] public bool IsGrid; + [DataField] public bool RandomRotation = true; diff --git a/Content.Server/Maps/MapMigrationSystem.cs b/Content.Server/Maps/MapMigrationSystem.cs index 83c2abc5e3..3341034a35 100644 --- a/Content.Server/Maps/MapMigrationSystem.cs +++ b/Content.Server/Maps/MapMigrationSystem.cs @@ -2,8 +2,8 @@ using System.IO; using System.Linq; using Robust.Server.GameObjects; -using Robust.Server.Maps; using Robust.Shared.ContentPack; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map.Events; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Markdown; diff --git a/Content.Server/Maps/ResaveCommand.cs b/Content.Server/Maps/ResaveCommand.cs index cc4d13dded..0d48f946be 100644 --- a/Content.Server/Maps/ResaveCommand.cs +++ b/Content.Server/Maps/ResaveCommand.cs @@ -1,11 +1,11 @@ using System.Linq; using Content.Server.Administration; using Content.Shared.Administration; -using Robust.Server.GameObjects; -using Robust.Server.Maps; using Robust.Shared.Console; 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; namespace Content.Server.Maps; @@ -17,8 +17,8 @@ namespace Content.Server.Maps; public sealed class ResaveCommand : LocalizedCommands { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IResourceManager _res = default!; + [Dependency] private readonly ILogManager _log = default!; public override string Command => "resave"; @@ -26,32 +26,56 @@ public sealed class ResaveCommand : LocalizedCommands { var loader = _entManager.System(); - foreach (var fn in _res.ContentFindFiles(new ResPath("/Maps/"))) + var opts = MapLoadOptions.Default with { - var mapId = _mapManager.CreateMap(); - _mapManager.AddUninitializedMap(mapId); - loader.Load(mapId, fn.ToString(), new MapLoadOptions() + + DeserializationOptions = DeserializationOptions.Default with { - StoreMapUids = true, - LoadMap = true, - }); + StoreYamlUids = 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. _entManager.CullRemovedComponents(); - var mapUid = _mapManager.GetMapEntityId(mapId); - var mapXform = _entManager.GetComponent(mapUid); - - if (_entManager.HasComponent(mapUid) || mapXform.ChildCount != 1) + if (_entManager.HasComponent(map)) { - 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"); } } diff --git a/Content.Server/Procedural/DungeonSystem.cs b/Content.Server/Procedural/DungeonSystem.cs index 706f63ffd7..75cdb69130 100644 --- a/Content.Server/Procedural/DungeonSystem.cs +++ b/Content.Server/Procedural/DungeonSystem.cs @@ -15,10 +15,13 @@ using Robust.Server.GameObjects; using Robust.Shared.Collections; using Robust.Shared.Configuration; using Robust.Shared.Console; +using Robust.Shared.EntitySerialization; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Server.Procedural; @@ -173,14 +176,18 @@ public sealed partial class DungeonSystem : SharedDungeonSystem return Transform(uid).MapID; } - var mapId = _mapManager.CreateMap(); - _mapManager.AddUninitializedMap(mapId); - _loader.Load(mapId, proto.AtlasPath.ToString()); - var mapUid = _mapManager.GetMapEntityId(mapId); - _mapManager.SetMapPaused(mapId, true); - comp = AddComp(mapUid); + var opts = new MapLoadOptions + { + DeserializationOptions = DeserializationOptions.Default with {PauseMaps = true}, + ExpectedCategory = FileCategory.Map + }; + + 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(map.Value.Owner); comp.Path = proto.AtlasPath; - return mapId; + return map.Value.Comp.MapId; } /// diff --git a/Content.Server/Salvage/SalvageSystem.Magnet.cs b/Content.Server/Salvage/SalvageSystem.Magnet.cs index 81db78fb20..f0520b6dc6 100644 --- a/Content.Server/Salvage/SalvageSystem.Magnet.cs +++ b/Content.Server/Salvage/SalvageSystem.Magnet.cs @@ -2,14 +2,11 @@ using System.Linq; using System.Numerics; using System.Threading.Tasks; using Content.Server.Salvage.Magnet; -using Content.Shared.Humanoid; using Content.Shared.Mobs.Components; using Content.Shared.Procedural; using Content.Shared.Radio; using Content.Shared.Salvage.Magnet; -using Robust.Server.Maps; using Robust.Shared.Map; -using Robust.Shared.Map.Components; namespace Content.Server.Salvage; @@ -278,15 +275,10 @@ public sealed partial class SalvageSystem case SalvageOffering wreck: var salvageProto = wreck.SalvageMap; - var opts = new MapLoadOptions - { - Offset = new Vector2(0, 0) - }; - - if (!_map.TryLoad(salvMapXform.MapID, salvageProto.MapPath.ToString(), out _, opts)) + if (!_loader.TryLoadGrid(salvMapXform.MapID, salvageProto.MapPath, out _)) { Report(magnet, MagnetChannel, "salvage-system-announcement-spawn-debris-disintegrated"); - _mapManager.DeleteMap(salvMapXform.MapID); + _mapSystem.DeleteMap(salvMapXform.MapID); return; } diff --git a/Content.Server/Salvage/SalvageSystem.cs b/Content.Server/Salvage/SalvageSystem.cs index eb5719c892..9115c60536 100644 --- a/Content.Server/Salvage/SalvageSystem.cs +++ b/Content.Server/Salvage/SalvageSystem.cs @@ -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.Shared.Examine; -using Content.Shared.Interaction; -using Content.Shared.Popups; using Content.Shared.Radio; using Content.Shared.Salvage; using Robust.Server.GameObjects; using Robust.Shared.Configuration; using Robust.Shared.Map; -using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Utility; using Content.Server.Chat.Managers; using Content.Server.Gravity; using Content.Server.Parallax; using Content.Server.Procedural; using Content.Server.Shuttles.Systems; using Content.Server.Station.Systems; -using Content.Shared.CCVar; 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.Map.Components; using Robust.Shared.Timing; using Content.Server.Labels; +using Robust.Shared.EntitySerialization.Systems; namespace Content.Server.Salvage { @@ -50,7 +35,7 @@ namespace Content.Server.Salvage [Dependency] private readonly DungeonSystem _dungeon = default!; [Dependency] private readonly GravitySystem _gravity = 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 RadioSystem _radioSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 1f972d9675..708f5a7a1c 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -30,11 +30,13 @@ using Robust.Server.GameObjects; using Robust.Shared.Collections; using Robust.Shared.Configuration; using Robust.Shared.Console; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; +using Robust.Shared.Utility; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; namespace Content.Server.Shuttles.Systems; @@ -512,15 +514,13 @@ public sealed class ArrivalsSystem : EntitySystem private void SetupArrivalsStation() { - var mapUid = _mapSystem.CreateMap(out var mapId, false); - _metaData.SetEntityName(mapUid, Loc.GetString("map-name-terminal")); - - if (!_loader.TryLoad(mapId, _cfgManager.GetCVar(CCVars.ArrivalsMap), out var uids)) - { + var path = new ResPath(_cfgManager.GetCVar(CCVars.ArrivalsMap)); + if (!_loader.TryLoadMap(path, out var map, out var grids)) return; - } - foreach (var id in uids) + _metaData.SetEntityName(map.Value, Loc.GetString("map-name-terminal")); + + foreach (var id in grids) { EnsureComp(id); EnsureComp(id); @@ -531,15 +531,15 @@ public sealed class ArrivalsSystem : EntitySystem if (_cfgManager.GetCVar(CCVars.ArrivalsPlanet)) { var template = _random.Pick(_arrivalsBiomeOptions); - _biomes.EnsurePlanet(mapUid, _protoManager.Index(template)); + _biomes.EnsurePlanet(map.Value, _protoManager.Index(template)); var restricted = new RestrictedRangeComponent { Range = 32f }; - AddComp(mapUid, restricted); + AddComp(map.Value, restricted); } - _mapSystem.InitializeMap(mapId); + _mapSystem.InitializeMap(map.Value.Comp.MapId); // Handle roundstart stations. var query = AllEntityQuery(); @@ -600,9 +600,9 @@ public sealed class ArrivalsSystem : EntitySystem var dummpMapEntity = _mapSystem.CreateMap(out var dummyMapId); 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(component.Shuttle); var arrivalsComp = EnsureComp(component.Shuttle); arrivalsComp.Station = uid; diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index 6c4bdc0814..afa77421bd 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -29,10 +29,9 @@ using Content.Shared.Shuttles.Events; using Content.Shared.Tag; using Content.Shared.Tiles; using Robust.Server.GameObjects; -using Robust.Server.Maps; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; -using Robust.Shared.Map; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map.Components; using Robust.Shared.Player; using Robust.Shared.Random; @@ -60,7 +59,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem [Dependency] private readonly DockingSystem _dock = default!; [Dependency] private readonly IdCardSystem _idSystem = 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 PopupSystem _popup = 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 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)) { @@ -608,15 +608,11 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem // Load escape shuttle 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. - 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) + offset: new Vector2(500f + ent.Comp2.ShuttleIndex, 0f))) { Log.Error($"Unable to spawn emergency shuttle {shuttlePath} for {ToPrettyString(ent)}"); return; diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs index de0593b26f..6123f348ec 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs @@ -72,17 +72,15 @@ public sealed partial class ShuttleSystem _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(ent[0])) - { - TryFTLProximity(ent[0], targetGrid.Value); - } + if (HasComp(ent)) + TryFTLProximity(ent.Value, targetGrid.Value); - _station.AddGridToStation(uid, ent[0]); + _station.AddGridToStation(uid, ent.Value); } - _mapManager.DeleteMap(mapId); + _mapSystem.DeleteMap(mapId); } private bool TryDungeonSpawn(Entity targetGrid, DungeonSpawnGroup group, out EntityUid spawned) @@ -143,20 +141,18 @@ public sealed partial class ShuttleSystem var path = paths[^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(ent[0])) - { - TryFTLProximity(ent[0], targetGrid); - } + if (HasComp(grid)) + TryFTLProximity(grid.Value, targetGrid); if (group.NameGrid) { var name = path.FilenameWithoutExtension; - _metadata.SetEntityName(ent[0], name); + _metadata.SetEntityName(grid.Value, name); } - spawned = ent[0]; + spawned = grid.Value; 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) @@ -246,23 +242,22 @@ public sealed partial class ShuttleSystem _mapSystem.CreateMap(out var mapId); var valid = false; - if (_loader.TryLoad(mapId, component.Path.ToString(), out var ent) && - ent.Count == 1 && - TryComp(ent[0], out TransformComponent? shuttleXform)) + if (_loader.TryLoadGrid(mapId, component.Path, out var grid)) { - var escape = GetSingleDock(ent[0]); + var escape = GetSingleDock(grid.Value); 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) { - FTLDock((ent[0], shuttleXform), config); + var shuttleXform = Transform(grid.Value); + FTLDock((grid.Value, shuttleXform), config); if (TryComp(xform.GridUid, out var stationMember)) { - _station.AddGridToStation(stationMember.Station, ent[0]); + _station.AddGridToStation(stationMember.Station, grid.Value); } valid = true; @@ -273,11 +268,11 @@ public sealed partial class ShuttleSystem { var compType = compReg.Component.GetType(); - if (HasComp(ent[0], compType)) + if (HasComp(grid.Value, compType)) continue; 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}"); } - _mapManager.DeleteMap(mapId); + _mapSystem.DeleteMap(mapId); } private (EntityUid Entity, DockingComponent Component)? GetSingleDock(EntityUid uid) diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.cs index 6e8c1a9e20..f2c58d103c 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.cs @@ -17,6 +17,7 @@ using Robust.Server.GameStates; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; +using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics; diff --git a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs index 6f508d9038..14c8036287 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.Input.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.Input.cs @@ -8,6 +8,7 @@ using Content.Shared.Movement.Events; using Robust.Shared.GameStates; using Robust.Shared.Input; using Robust.Shared.Input.Binding; +using Robust.Shared.Map.Components; using Robust.Shared.Player; using Robust.Shared.Prototypes; 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 (relative != null && _mapManager.IsMap(relative.Value)) + if (relative != null && HasComp(relative.Value)) { targetRotation = currentRotation.FlipPositive().Reduced(); } diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 2051c24be8..9a4ca97210 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -454,7 +454,7 @@ duration: 1 - type: RuleGrids - type: LoadMapRule - mapPath: /Maps/Shuttles/ShuttleEvent/striker.yml + gridPath: /Maps/Shuttles/ShuttleEvent/striker.yml - type: NukeopsRule roundEndBehavior: Nothing - type: AntagSelection diff --git a/Resources/Prototypes/Maps/centcomm.yml b/Resources/Prototypes/Maps/centcomm.yml index 47dc5ca1f3..007da851d0 100644 --- a/Resources/Prototypes/Maps/centcomm.yml +++ b/Resources/Prototypes/Maps/centcomm.yml @@ -1,5 +1,6 @@ - type: gameMap 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' mapPath: /Maps/centcomm.yml minPlayers: 10 diff --git a/RobustToolbox b/RobustToolbox index 5e97db435c..3cccf5be02 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 5e97db435c05b4c188184ef90e5d77b0500403d0 +Subproject commit 3cccf5be028be75242ffc86877b4e78a72b8cafe