]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Refactor map loading & saving
authorElectroJr <leonsfriedrich@gmail.com>
Sun, 22 Dec 2024 02:13:10 +0000 (15:13 +1300)
committerElectroJr <leonsfriedrich@gmail.com>
Sun, 22 Dec 2024 13:42:17 +0000 (02:42 +1300)
33 files changed:
Content.IntegrationTests/Tests/Body/LungTest.cs
Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs
Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs
Content.IntegrationTests/Tests/PostMapInitTest.cs
Content.IntegrationTests/Tests/SalvageTest.cs
Content.IntegrationTests/Tests/SaveLoadMapTest.cs
Content.IntegrationTests/Tests/SaveLoadSaveTest.cs
Content.IntegrationTests/Tests/Shuttle/DockTest.cs
Content.Server/Administration/Commands/LoadGameMapCommand.cs
Content.Server/Administration/Commands/PersistenceSaveCommand.cs
Content.Server/Administration/Systems/AdminTestArenaSystem.cs
Content.Server/GameTicking/GameTicker.RoundFlow.cs
Content.Server/GameTicking/GameTicker.cs
Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs
Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs
Content.Server/GridPreloader/GridPreloaderSystem.cs
Content.Server/Mapping/MappingCommand.cs
Content.Server/Mapping/MappingManager.cs
Content.Server/Mapping/MappingSystem.cs
Content.Server/Maps/GameMapPrototype.cs
Content.Server/Maps/MapMigrationSystem.cs
Content.Server/Maps/ResaveCommand.cs
Content.Server/Procedural/DungeonSystem.cs
Content.Server/Salvage/SalvageSystem.Magnet.cs
Content.Server/Salvage/SalvageSystem.cs
Content.Server/Shuttles/Systems/ArrivalsSystem.cs
Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs
Content.Server/Shuttles/Systems/ShuttleSystem.GridFill.cs
Content.Server/Shuttles/Systems/ShuttleSystem.cs
Content.Shared/Movement/Systems/SharedMoverController.Input.cs
Resources/Prototypes/GameRules/events.yml
Resources/Prototypes/Maps/centcomm.yml
RobustToolbox

index 9b5ee431f1fa9bb3dab2abb5e9d5c99606f75bda..8ac3a3021f1b50af31ca1b3fb2ddca5051711867 100644 (file)
@@ -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<IMapManager>();
             var entityManager = server.ResolveDependency<IEntityManager>();
             var mapLoader = entityManager.System<MapLoaderSystem>();
             var mapSys = entityManager.System<SharedMapSystem>();
@@ -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<MapGridComponent>();
-                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<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(mapLoader.TryLoadGrid(mapId, testMapName, out var gridEnt));
+                grid = gridEnt!.Value.Owner;
             });
 
             Assert.That(grid, Is.Not.Null, $"Test blueprint {testMapName} not found.");
index 01482ba8ee217b79cf685da6994dcf6aa410b76e..f56b5c28501823f558073bbb3dbb936d868e2cec 100644 (file)
@@ -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<BodyComponent>()
@@ -173,7 +174,7 @@ public sealed class SaveLoadReparentTest
                     });
                 }
 
-                maps.DeleteMap(mapId);
+                entities.DeleteEntity(map);
             }
         });
 
index 7bc62dfe2bc27eed952aecebf7798152064c0e3a..ad4ddc261256bae925d05be8ec47dbc3909a2399 100644 (file)
@@ -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<SharedMapSystem>().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();
     }
index 635a9dbe0f939e0c955fb0d2299c2892a1a7579a..f7562d934ddd7174966e59dce9953ff9ab662fcc 100644 (file)
@@ -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"
 
-           
+
         };
 
         /// <summary>
@@ -81,33 +81,23 @@ namespace Content.IntegrationTests.Tests
             var entManager = server.ResolveDependency<IEntityManager>();
             var mapLoader = entManager.System<MapLoaderSystem>();
             var mapSystem = entManager.System<SharedMapSystem>();
-            var mapManager = server.ResolveDependency<IMapManager>();
             var cfg = server.ResolveDependency<IConfigurationManager>();
             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<MapGridComponent>(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<IPrototypeManager>();
             var ticker = entManager.EntitySysManager.GetEntitySystem<GameTicker>();
             var shuttleSystem = entManager.EntitySysManager.GetEntitySystem<ShuttleSystem>();
-            var xformQuery = entManager.GetEntityQuery<TransformComponent>();
             var cfg = server.ResolveDependency<IConfigurationManager>();
             Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
 
             await server.WaitPost(() =>
             {
-                mapSystem.CreateMap(out var mapId);
+                MapId mapId;
                 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)
                 {
@@ -218,21 +208,17 @@ namespace Content.IntegrationTests.Tests
                 if (entManager.TryGetComponent<StationEmergencyShuttleComponent>(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<MapGridComponent>(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<ShuttleComponent>(shuttle), targetGrid.Value),
+                        shuttleSystem.TryFTLDock(shuttle!.Value.Owner,
+                            entManager.GetComponent<ShuttleComponent>(shuttle!.Value.Owner),
+                            targetGrid.Value),
                         $"Unable to dock {shuttlePath} to {mapProto}");
-#pragma warning restore NUnit2045
                 }
 
-                mapManager.DeleteMap(shuttleMap);
+                mapSystem.DeleteMap(shuttleMap);
 
                 if (entManager.HasComponent<StationJobsComponent>(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<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
-            var mapManager = server.ResolveDependency<IMapManager>();
             var resourceManager = server.ResolveDependency<IResourceManager>();
             var protoManager = server.ResolveDependency<IPrototypeManager>();
             var cfg = server.ResolveDependency<IConfigurationManager>();
-            var mapSystem = server.System<SharedMapSystem>();
             Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False);
 
             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))
                 .ToArray();
 
-            var mapNames = new List<string>();
+            var mapPaths = new List<ResPath>();
             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<Entity<MapComponent>> 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);
                         }
                     }
                 });
index 5dfba82308f03f130da890ea1d867c80670d0cad..0059db6292b5037dac26aa45f317b5d0bee21fda 100644 (file)
@@ -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<IEntityManager>();
         var mapLoader = entManager.System<MapLoaderSystem>();
-        var mapManager = server.ResolveDependency<IMapManager>();
         var prototypeManager = server.ResolveDependency<IPrototypeManager>();
         var cfg = server.ResolveDependency<IConfigurationManager>();
         var mapSystem = entManager.System<SharedMapSystem>();
@@ -34,13 +30,10 @@ public sealed class SalvageTest
         {
             foreach (var salvage in prototypeManager.EnumeratePrototypes<SalvageMapPrototype>())
             {
-                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<MapGridComponent>(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)
                 {
index 213da5d786265a398fb132555c688249d075fdf0..b1f6dd843301919cd99a2f6d0ccf4ede3d38e398 100644 (file)
@@ -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<TransformComponent>(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<TransformComponent>(gridUid, out var gridXform))
                     {
                         Assert.Fail();
index 4facd5ee40c98165ba95009b6e46f9721df0f483..a398b55919743a585dfaca5caed74179e3a553e4 100644 (file)
@@ -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
 {
     /// <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>
     [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<IConfigurationManager>();
             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(() =>
             {
                 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<MetaDataComponent>(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<MapGridComponent>(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<IEntitySystemManager>().GetEntitySystem<MapLoaderSystem>();
-            var mapManager = server.ResolveDependency<IMapManager>();
-            var mapSystem = server.System<SharedMapSystem>();
+            var mapSys = 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;
             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.
             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<MapLoaderSystem>();
-            var mapSystem = server.System<SharedMapSystem>();
-            var mapManager = server.ResolveDependency<IMapManager>();
+            var mapSys = server.System<SharedMapSystem>();
             var userData = server.ResolveDependency<IResourceManager>().UserData;
             var cfg = server.ResolveDependency<IConfigurationManager>();
             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 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();
         }
+
+        /// <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");
+            }
+        }
     }
 }
index f6e99596e90488778a55d1244c1f9abdddc68095..ab82a3d2f9184d6382fe859e815b5c3ca875947a 100644 (file)
@@ -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<MapGridComponent>(map.MapUid);
             entManager.DeleteEntity(map.Grid);
-            Assert.That(entManager.System<MapLoaderSystem>().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<MapLoaderSystem>().TryLoadGrid(otherMap.MapId, path, out var grid));
+            shuttle = grid!.Value.Owner;
 
             var dockingConfig = dockingSystem.GetDockingConfig(shuttle, map.MapUid);
             Assert.That(dockingConfig, Is.EqualTo(null));
index ea31c1ecb1712ad7c90121883c35ca4c9b3b53d2..034b50a9c5e4485b0947376cc66488f0fe0ced3d 100644 (file)
@@ -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<IPrototypeManager>();
             var entityManager = IoCManager.Resolve<IEntityManager>();
             var gameTicker = entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
+            var mapSys = entityManager.EntitySysManager.GetEntitySystem<SharedMapSystem>();
 
             if (args.Length is not (2 or 4 or 5))
             {
@@ -32,29 +31,28 @@ namespace Content.Server.Administration.Commands
                 return;
             }
 
-            if (prototypeManager.TryIndex<GameMapPrototype>(args[1], out var gameMap))
+            if (!prototypeManager.TryIndex<GameMapPrototype>(args[1], out var gameMap))
             {
-                if (!int.TryParse(args[0], out var mapId))
+                shell.WriteError($"The given map prototype {args[0]} is invalid.");
+                return;
+            }
+
+            if (!int.TryParse(args[0], out var mapId))
                     return;
 
-                var loadOptions = new MapLoadOptions()
-                {
-                    LoadMap = false,
-                };
+            var stationName = args.Length == 5 ? args[4] : null;
 
-                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]));
 
-                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
-            {
-                shell.WriteError($"The given map prototype {args[0]} is invalid.");
-            }
+            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)
index 7ef1932c56809db8e4d83debebae0f3a98550172..dffe1eff7f3ac6e5fb9e3876819a9bb5bcd65a86 100644 (file)
@@ -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;
 
index e3671bcdfb348fc259b51c966060774db5dd275c..12bf0eba6484de510fccdf3f66b72171d8df94c9 100644 (file)
@@ -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;
 /// </summary>
 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<MapComponent>(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);
     }
 }
index a7dd5d6ab6248d17747ca07622ecaeb9a37288a0..37e7a4e1ce598638f1424bcfe3643e03232508fa 100644 (file)
@@ -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<GameMapPrototype>();
 
             // 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;
+            }
+
+            for (var i = 0; i < maps.Count; i++)
+            {
+                LoadGameMap(maps[i], out var mapId);
+                DebugTools.Assert(!_map.IsInitialized(mapId));
 
-                LoadGameMap(map, toLoad, null);
+                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>
         ///     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 <see cref="DeserializationOptions"/>.
         /// </summary>
-        /// <param name="map">Game map prototype to load in.</param>
-        /// <param name="targetMapId">Map to load into.</param>
-        /// <param name="loadOptions">Map loading options, includes offset.</param>
+        /// <remarks>
+        /// This is basically a wrapper around a <see cref="MapLoaderSystem"/> method that auto generate
+        /// 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>
         /// <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.
-            // 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<EntityUid> {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}");
+            }
+
+            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;
+        }
+
+        /// <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);
+
+            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}");
+                }
 
-            var gridIds = _map.LoadMap(targetMapId, ev.GameMap.MapPath.ToString(), ev.Options);
+                _metaData.SetEntityName(mapUid, proto.MapName);
+                var g = new List<EntityUid> {grid.Value.Owner};
+                RaiseLocalEvent(new PostGameMapLoad(proto, mapId, g, stationName));
+                return g;
+            }
 
-            _metaData.SetEntityName(_mapManager.GetMapEntityId(targetMapId), map.MapName);
+            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");
+            }
 
-            var gridUids = gridIds.ToList();
-            RaiseLocalEvent(new PostGameMapLoad(map, targetMapId, gridUids, stationName));
+            _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;
         }
 
@@ -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.
     /// </remarks>
     [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;
     }
 
-
     /// <summary>
     ///     Event raised after the game loads a given map.
     /// </summary>
index f8ba7e1d7d91f6708c4eb2e5bb6e7d37408734b4..d286fd641eb876d361cddab0ac05e0eeae4ca746 100644 (file)
@@ -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!;
index 1f0505c60fc0ecd421c3e42047dba430fc573e17..f09fff5eafaad9f7ddd01ddcf599d96540b2a2e8 100644 (file)
@@ -20,11 +20,17 @@ public sealed partial class LoadMapRuleComponent : Component
     public ProtoId<GameMapPrototype>? GameMap;
 
     /// <summary>
-    /// A map path to load on a new map.
+    /// A map to load.
     /// </summary>
     [DataField]
     public ResPath? MapPath;
 
+    /// <summary>
+    /// A grid to load on a new map.
+    /// </summary>
+    [DataField]
+    public ResPath? GridPath;
+
     /// <summary>
     /// A <see cref="PreloadedGridPrototype"/> to move to a new map.
     /// If there are no instances left nothing is done.
index f18115d3cf6b3e42d88ac46e7259b9da81e255d3..003a74ef4bfb0d52cd2644ff7fbb8375a37d239a 100644 (file)
@@ -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<LoadMapRuleComponent>
             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<EntityUid> 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<EntityUid> {grid.Value.Owner};
         }
         else if (comp.PreloadedGrid is {} preloaded)
         {
@@ -56,11 +81,11 @@ public sealed class LoadMapRuleSystem : StationEventSystem<LoadMapRuleComponent>
             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<EntityUid>() { loadedShuttle.Value };
             _map.InitializeMap(mapUid);
@@ -68,7 +93,6 @@ public sealed class LoadMapRuleSystem : StationEventSystem<LoadMapRuleComponent>
         else
         {
             Log.Error($"No valid map prototype or map path associated with the rule {ToPrettyString(uid)}");
-            Del(mapUid);
             ForceEndSelf(uid, rule);
             return;
         }
index e12ce41a31e89bfa4a6e129cbdb06b4da48b7ad3..d648acbb06c0337036e1906ba02e6f8daf51ac9c 100644 (file)
@@ -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))
-                    continue;
-
-                // only supports loading maps with one grid.
-                if (roots.Count != 1)
+                    Log.Error($"Failed to preload grid prototype {proto.ID}");
                     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;
+                var (gridUid, mapGrid) = grid.Value;
 
                 if (!TryComp<PhysicsComponent>(gridUid, out var physics))
                     continue;
index 70647d3281456457305fb934bc0e0ede1684812e..b85b0953dddcff79b2fd7f61762924a20c86369f 100644 (file)
@@ -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<MapLoaderSystem>().TryLoad(mapId, args[1], out _, loadOptions);
+                    var path = new ResPath(args[1]);
+                    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?
index e8c6eca204caf44fe32f410efbc48ea85e81694b..be6f503bf0b553219978e7a3d5f9790d2e220b3e 100644 (file)
@@ -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<TransformSystem>().GetMapCoordinates(player).MapId;
-            var mapEntity = _map.GetMapEntityIdOrThrow(mapId);
-            var data = _systems.GetEntitySystem<MapLoaderSystem>().GetSaveData(mapEntity);
+            var data = _systems.GetEntitySystem<MapLoaderSystem>().SerializeEntityRecursive(mapUid).Node;
             var document = new YamlDocument(data.ToYaml());
             var stream = new YamlStream { document };
             var writer = new StringWriter();
index 28bb3afbe10f56da2b8a6668141726175948b2a8..ecdbbe081c15baf8b2d96c1631295d6510317b8c 100644 (file)
@@ -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
     /// <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");
             _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);
         }
     }
 
index 5942a9930eb06ff18a315b3178810ae6097eb1d5..c39ca348da0356df77caeb375dc7e6e539149d90 100644 (file)
@@ -25,6 +25,11 @@ public sealed partial class GameMapPrototype : IPrototype
     [DataField]
     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]
     public bool RandomRotation = true;
 
index 83c2abc5e3868e59d3e120a5fd77f63b86ec1979..3341034a35655b2d1ee28c0ac120287ca04342ae 100644 (file)
@@ -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;
index cc4d13ddedf38d59ebf8aa12780e4c0c362e38cd..0d48f946be8d1450cc81f12eecfb5e2253d6fe31 100644 (file)
@@ -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<MapLoaderSystem>();
 
-        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
+            {
+                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)
             {
-                StoreMapUids = true,
-                LoadMap = true,
-            });
+                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<TransformComponent>(mapUid);
-
-            if (_entManager.HasComponent<LoadedMapComponent>(mapUid) || mapXform.ChildCount != 1)
+            if (_entManager.HasComponent<LoadedMapComponent>(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");
     }
 }
index 706f63ffd7dc1b7f1d9f836f3414f7c62d354945..75cdb69130ea868835b20c62d94a48f6c158494d 100644 (file)
@@ -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<DungeonAtlasTemplateComponent>(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<DungeonAtlasTemplateComponent>(map.Value.Owner);
         comp.Path = proto.AtlasPath;
-        return mapId;
+        return map.Value.Comp.MapId;
     }
 
     /// <summary>
index 81db78fb20113a3ba6fc4a53c8016ab411354624..f0520b6dc6caeb369be40c09b0dd065059a3d302 100644 (file)
@@ -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;
                 }
 
index eb5719c892f837ecef3f091a177342a5f4788d76..9115c605366dbb53eb73aa31640cff2ba15a95f0 100644 (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.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!;
index 1f972d96756c4b6c5a561432d2fb68394c42a247..708f5a7a1c270c0ec77d583a6df3ff916b9f7e90 100644 (file)
@@ -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<ArrivalsSourceComponent>(id);
             EnsureComp<ProtectedGridComponent>(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<StationArrivalsComponent>();
@@ -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<ShuttleComponent>(component.Shuttle);
             var arrivalsComp = EnsureComp<ArrivalsShuttleComponent>(component.Shuttle);
             arrivalsComp.Station = uid;
index 6c4bdc08148995bff74bff56d41b9511d1eeb1d4..afa77421bd71590ad1d84fce10f3674238eb58d7 100644 (file)
@@ -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;
index de0593b26f0fd8538e83e9fe42caa47349735f29..6123f348ec83b1486aa5178d44a4ba614205af71 100644 (file)
@@ -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<ShuttleComponent>(ent[0]))
-            {
-                TryFTLProximity(ent[0], targetGrid.Value);
-            }
+            if (HasComp<ShuttleComponent>(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<MapGridComponent?> 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<ShuttleComponent>(ent[0]))
-            {
-                TryFTLProximity(ent[0], targetGrid);
-            }
+            if (HasComp<ShuttleComponent>(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<StationMemberComponent>(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)
index 6e8c1a9e204f2d691bd8de8792acdba5484c6744..f2c58d103c918aac51ff6ee90a9a0c6ba4053121 100644 (file)
@@ -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;
index 6f508d9038c0fea7e93e85b96f326ebf66f38f58..14c803628712917a084cfcea07ab49717f8b3366 100644 (file)
@@ -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<MapComponent>(relative.Value))
             {
                 targetRotation = currentRotation.FlipPositive().Reduced();
             }
index 2051c24be87aafd1d7d07e02391028e81e9d2dc0..9a4ca97210b4a0c142441300065c560a90e995f8 100644 (file)
     duration: 1
   - type: RuleGrids
   - type: LoadMapRule
-    mapPath: /Maps/Shuttles/ShuttleEvent/striker.yml
+    gridPath: /Maps/Shuttles/ShuttleEvent/striker.yml
   - type: NukeopsRule
     roundEndBehavior: Nothing
   - type: AntagSelection
index 47dc5ca1f3eaff3e7a5f176474a13fb976c776e1..007da851d010e507f6cea3e54534c086b678f37f 100644 (file)
@@ -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
index 5e97db435c05b4c188184ef90e5d77b0500403d0..3cccf5be028be75242ffc86877b4e78a72b8cafe 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 5e97db435c05b4c188184ef90e5d77b0500403d0
+Subproject commit 3cccf5be028be75242ffc86877b4e78a72b8cafe