--- /dev/null
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using Robust.Server.GameObjects;
+using Robust.Server.Maps;
+using Robust.Shared.ContentPack;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Mapping;
+using Robust.Shared.Serialization.Markdown.Value;
+using Robust.Shared.Utility;
+
+namespace Content.Server.Maps;
+
+/// <summary>
+/// Performs basic map migration operations by listening for engine <see cref="MapLoaderSystem"/> events.
+/// </summary>
+public sealed class MapMigrationSystem : EntitySystem
+{
+ [Dependency] private readonly IPrototypeManager _protoMan = default!;
+ [Dependency] private readonly IResourceManager _resMan = default!;
+
+ private const string MigrationFile = "/migration.yml";
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<BeforeEntityReadEvent>(OnBeforeReadEvent);
+
+#if DEBUG
+ if (!TryReadFile(out var mappings))
+ return;
+
+ foreach (var node in mappings.Values)
+ {
+ var newId = ((ValueDataNode) node).Value;
+ if (!string.IsNullOrEmpty(newId) && newId != "null")
+ DebugTools.Assert(_protoMan.HasIndex<EntityPrototype>(newId));
+ }
+#endif
+ }
+
+ private bool TryReadFile([NotNullWhen(true)] out MappingDataNode? mappings)
+ {
+ mappings = null;
+ var path = new ResPath(MigrationFile);
+ if (!_resMan.TryContentFileRead(path, out var stream))
+ return false;
+
+ using var reader = new StreamReader(stream, EncodingHelpers.UTF8);
+ var documents = DataNodeParser.ParseYamlStream(reader).FirstOrDefault();
+
+ if (documents == null)
+ return false;
+
+ mappings = (MappingDataNode) documents.Root;
+ return true;
+ }
+
+ private void OnBeforeReadEvent(BeforeEntityReadEvent ev)
+ {
+ if (!TryReadFile(out var mappings))
+ return;
+
+ foreach (var (key, value) in mappings)
+ {
+ if (key is not ValueDataNode keyNode || value is not ValueDataNode valueNode)
+ continue;
+
+ if (string.IsNullOrWhiteSpace(valueNode.Value) || valueNode.Value == "null")
+ ev.DeletedPrototypes.Add(keyNode.Value);
+ else
+ ev.RenamedPrototypes.Add(keyNode.Value, valueNode.Value);
+ }
+ }
+}