diff --git a/Content.MapRenderer/Content.MapRenderer.csproj b/Content.MapRenderer/Content.MapRenderer.csproj
new file mode 100644
index 0000000000..32f79a2821
--- /dev/null
+++ b/Content.MapRenderer/Content.MapRenderer.csproj
@@ -0,0 +1,20 @@
+
+
+
+ Exe
+ net6.0
+ ..\bin\Content.MapRenderer\
+ false
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.MapRenderer/Extensions/DirectoryExtensions.cs b/Content.MapRenderer/Extensions/DirectoryExtensions.cs
new file mode 100644
index 0000000000..55d4611a7e
--- /dev/null
+++ b/Content.MapRenderer/Extensions/DirectoryExtensions.cs
@@ -0,0 +1,32 @@
+using System.IO;
+using System.Reflection;
+
+namespace Content.MapRenderer.Extensions
+{
+ public static class DirectoryExtensions
+ {
+ public static DirectoryInfo RepositoryRoot()
+ {
+ // space-station-14/bin/Content.MapRenderer/Content.MapRenderer.dll
+ var currentLocation = Assembly.GetExecutingAssembly().Location;
+
+ // space-station-14
+ return Directory.GetParent(currentLocation)!.Parent!.Parent!;
+ }
+
+ public static DirectoryInfo Resources()
+ {
+ return new DirectoryInfo($"{RepositoryRoot()}{Path.DirectorySeparatorChar}Resources");
+ }
+
+ public static DirectoryInfo Maps()
+ {
+ return new DirectoryInfo($"{Resources()}{Path.DirectorySeparatorChar}Maps");
+ }
+
+ public static DirectoryInfo MapImages()
+ {
+ return new DirectoryInfo($"{Resources()}{Path.DirectorySeparatorChar}MapImages");
+ }
+ }
+}
diff --git a/Content.MapRenderer/Extensions/EnvironmentExtensions.cs b/Content.MapRenderer/Extensions/EnvironmentExtensions.cs
new file mode 100644
index 0000000000..34a379096f
--- /dev/null
+++ b/Content.MapRenderer/Extensions/EnvironmentExtensions.cs
@@ -0,0 +1,20 @@
+#nullable enable
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Content.MapRenderer.Extensions
+{
+ public static class EnvironmentExtensions
+ {
+ public static bool TryGetVariable(string key, [NotNullWhen(true)] out string? value)
+ {
+ return (value = Environment.GetEnvironmentVariable(key)) != null;
+ }
+
+ public static string GetVariableOrThrow(string key)
+ {
+ return Environment.GetEnvironmentVariable(key) ??
+ throw new ArgumentException($"No environment variable found with key {key}");
+ }
+ }
+}
diff --git a/Content.MapRenderer/Painters/EntityData.cs b/Content.MapRenderer/Painters/EntityData.cs
new file mode 100644
index 0000000000..95d8b3fd0f
--- /dev/null
+++ b/Content.MapRenderer/Painters/EntityData.cs
@@ -0,0 +1,20 @@
+using Robust.Client.GameObjects;
+
+namespace Content.MapRenderer.Painters
+{
+ public class EntityData
+ {
+ public EntityData(SpriteComponent sprite, float x, float y)
+ {
+ Sprite = sprite;
+ X = x;
+ Y = y;
+ }
+
+ public SpriteComponent Sprite { get; }
+
+ public float X { get; }
+
+ public float Y { get; }
+ }
+}
diff --git a/Content.MapRenderer/Painters/EntityPainter.cs b/Content.MapRenderer/Painters/EntityPainter.cs
new file mode 100644
index 0000000000..13dcd6bf73
--- /dev/null
+++ b/Content.MapRenderer/Painters/EntityPainter.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using Content.Shared.SubFloor;
+using Robust.Client.ResourceManagement;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Timing;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using static Robust.Client.Graphics.RSI.State;
+using static Robust.UnitTesting.RobustIntegrationTest;
+
+namespace Content.MapRenderer.Painters;
+
+public class EntityPainter
+{
+ private readonly IResourceCache _cResourceCache;
+
+ private readonly Dictionary<(string path, string state), Image> _images;
+ private readonly Image _errorImage;
+
+ private readonly IEntityManager _sEntityManager;
+
+ public EntityPainter(ClientIntegrationInstance client, ServerIntegrationInstance server)
+ {
+ _cResourceCache = client.ResolveDependency();
+
+ _sEntityManager = server.ResolveDependency();
+
+ _images = new Dictionary<(string path, string state), Image>();
+ _errorImage = Image.Load(_cResourceCache.ContentFileRead("/Textures/error.rsi/error.png"));
+ }
+
+ public void Run(Image canvas, List entities)
+ {
+ var stopwatch = new Stopwatch();
+ stopwatch.Start();
+
+ // TODO cache this shit what are we insane
+ entities.Sort(Comparer.Create((x, y) => x.Sprite.DrawDepth.CompareTo(y.Sprite.DrawDepth)));
+
+ foreach (var entity in entities)
+ {
+ Run(canvas, entity);
+ }
+
+ Console.WriteLine($"{nameof(GridPainter)} painted {entities.Count} entities in {(int) stopwatch.Elapsed.TotalMilliseconds} ms");
+ }
+
+ public void Run(Image canvas, EntityData entity)
+ {
+ if (_sEntityManager.HasComponent(entity.Sprite.Owner))
+ {
+ return;
+ }
+
+ if (!entity.Sprite.Visible || entity.Sprite.ContainerOccluded)
+ {
+ return;
+ }
+
+ var worldRotation = _sEntityManager.GetComponent(entity.Sprite.Owner).WorldRotation;
+ foreach (var layer in entity.Sprite.AllLayers)
+ {
+ if (!layer.Visible)
+ {
+ continue;
+ }
+
+ if (!layer.RsiState.IsValid)
+ {
+ continue;
+ }
+
+ var rsi = layer.ActualRsi;
+ Image image;
+
+ if (rsi == null || rsi.Path == null || !rsi.TryGetState(layer.RsiState, out var state))
+ {
+ image = _errorImage;
+ }
+ else
+ {
+ var key = (rsi.Path!.ToString(), state.StateId.Name!);
+
+ if (!_images.TryGetValue(key, out image!))
+ {
+ var stream = _cResourceCache.ContentFileRead($"{rsi.Path}/{state.StateId}.png");
+ image = Image.Load(stream);
+
+ _images[key] = image;
+ }
+ }
+
+ image = image.CloneAs();
+
+ var directions = entity.Sprite.GetLayerDirectionCount(layer);
+
+ // TODO add support for 8 directions and animations (delays)
+ if (directions != 1 && directions != 8)
+ {
+ double xStart, xEnd, yStart, yEnd;
+
+ switch (directions)
+ {
+ case 4:
+ {
+ var dir = layer.EffectiveDirection(worldRotation);
+
+ (xStart, xEnd, yStart, yEnd) = dir switch
+ {
+ // Only need the first tuple as doubles for the compiler to recognize it
+ Direction.South => (0d, 0.5d, 0d, 0.5d),
+ Direction.East => (0, 0.5, 0.5, 1),
+ Direction.North => (0.5, 1, 0, 0.5),
+ Direction.West => (0.5, 1, 0.5, 1),
+ _ => throw new ArgumentOutOfRangeException(nameof(dir))
+ };
+ break;
+ }
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ var x = (int) (image.Width * xStart);
+ var width = (int) (image.Width * xEnd) - x;
+
+ var y = (int) (image.Height * yStart);
+ var height = (int) (image.Height * yEnd) - y;
+
+ image.Mutate(o => o.Crop(new Rectangle(x, y, width, height)));
+ }
+
+ var colorMix = entity.Sprite.Color * layer.Color;
+ var imageColor = Color.FromRgba(colorMix.RByte, colorMix.GByte, colorMix.BByte, colorMix.AByte);
+ var coloredImage = new Image(image.Width, image.Height);
+ coloredImage.Mutate(o => o.BackgroundColor(imageColor));
+
+ image.Mutate(o => o
+ .DrawImage(coloredImage, PixelColorBlendingMode.Multiply, PixelAlphaCompositionMode.SrcAtop, 1)
+ .Resize(32, 32)
+ .Flip(FlipMode.Vertical));
+
+ var pointX = (int) entity.X;
+ var pointY = (int) entity.Y;
+ canvas.Mutate(o => o.DrawImage(image, new Point(pointX, pointY), 1));
+ }
+ }
+}
diff --git a/Content.MapRenderer/Painters/GridPainter.cs b/Content.MapRenderer/Painters/GridPainter.cs
new file mode 100644
index 0000000000..f009ce9660
--- /dev/null
+++ b/Content.MapRenderer/Painters/GridPainter.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using Robust.Client.GameObjects;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Map;
+using Robust.Shared.Timing;
+using SixLabors.ImageSharp;
+using static Robust.UnitTesting.RobustIntegrationTest;
+
+namespace Content.MapRenderer.Painters
+{
+ public class GridPainter
+ {
+ private readonly EntityPainter _entityPainter;
+
+ private readonly IEntityManager _cEntityManager;
+ private readonly IMapManager _cMapManager;
+
+ private readonly IEntityManager _sEntityManager;
+
+ private readonly ConcurrentDictionary> _entities;
+
+ public GridPainter(ClientIntegrationInstance client, ServerIntegrationInstance server)
+ {
+ _entityPainter = new EntityPainter(client, server);
+
+ _cEntityManager = client.ResolveDependency();
+ _cMapManager = client.ResolveDependency();
+
+ _sEntityManager = server.ResolveDependency();
+
+ _entities = GetEntities();
+ }
+
+ public void Run(Image gridCanvas, IMapGrid grid)
+ {
+ var stopwatch = new Stopwatch();
+ stopwatch.Start();
+
+ if (!_entities.TryGetValue(grid.Index, out var entities))
+ {
+ Console.WriteLine($"No entities found on grid {grid.Index}");
+ return;
+ }
+
+ _entityPainter.Run(gridCanvas, entities);
+ Console.WriteLine($"{nameof(GridPainter)} painted grid {grid.Index} in {(int) stopwatch.Elapsed.TotalMilliseconds} ms");
+ }
+
+ private ConcurrentDictionary> GetEntities()
+ {
+ var stopwatch = new Stopwatch();
+ stopwatch.Start();
+
+ var components = new ConcurrentDictionary>();
+
+ foreach (var entity in _sEntityManager.GetEntities())
+ {
+ if (!_sEntityManager.HasComponent(entity))
+ {
+ continue;
+ }
+
+ var prototype = _sEntityManager.GetComponent(entity).EntityPrototype;
+ if (prototype == null)
+ {
+ continue;
+ }
+
+ if (!_cEntityManager.TryGetComponent(entity, out SpriteComponent sprite))
+ {
+ throw new InvalidOperationException(
+ $"No sprite component found on an entity for which a server sprite component exists. Prototype id: {prototype.ID}");
+ }
+
+ var xOffset = 0;
+ var yOffset = 0;
+ var tileSize = 1;
+
+ var transform = _sEntityManager.GetComponent(entity);
+ if (_cMapManager.TryGetGrid(transform.GridID, out var grid))
+ {
+ xOffset = (int) Math.Abs(grid.LocalBounds.Left);
+ yOffset = (int) Math.Abs(grid.LocalBounds.Bottom);
+ tileSize = grid.TileSize;
+ }
+
+ var position = transform.LocalPosition;
+ var x = ((float) Math.Floor(position.X) + xOffset) * tileSize * TilePainter.TileImageSize;
+ var y = ((float) Math.Floor(position.Y) + yOffset) * tileSize * TilePainter.TileImageSize;
+ var data = new EntityData(sprite, x, y);
+
+ components.GetOrAdd(transform.GridID, _ => new List()).Add(data);
+ }
+
+ Console.WriteLine($"Found {components.Values.Sum(l => l.Count)} entities on {components.Count} grids in {(int) stopwatch.Elapsed.TotalMilliseconds} ms");
+
+ return components;
+ }
+ }
+}
diff --git a/Content.MapRenderer/Painters/MapPainter.cs b/Content.MapRenderer/Painters/MapPainter.cs
new file mode 100644
index 0000000000..cb2d1bd83b
--- /dev/null
+++ b/Content.MapRenderer/Painters/MapPainter.cs
@@ -0,0 +1,151 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Content.IntegrationTests;
+using Content.Shared.CCVar;
+using Robust.Client.GameObjects;
+using Robust.Server.GameObjects;
+using Robust.Server.Player;
+using Robust.Shared;
+using Robust.Shared.Log;
+using Robust.Shared.Map;
+using Robust.Shared.Maths;
+using Robust.Shared.Timing;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using SpriteComponent = Robust.Server.GameObjects.SpriteComponent;
+
+namespace Content.MapRenderer.Painters
+{
+ public class MapPainter : ContentIntegrationTest
+ {
+ public async IAsyncEnumerable Paint(string map)
+ {
+ var stopwatch = new Stopwatch();
+ stopwatch.Start();
+
+ var clientOptions = new ClientContentIntegrationOption
+ {
+ CVarOverrides =
+ {
+ [CVars.NetPVS.Name] = "false"
+ },
+ Pool = false,
+ FailureLogLevel = LogLevel.Fatal
+ };
+
+ var serverOptions = new ServerContentIntegrationOption
+ {
+ CVarOverrides =
+ {
+ [CCVars.GameMap.Name] = map,
+ [CVars.NetPVS.Name] = "false"
+ },
+ Pool = false,
+ FailureLogLevel = LogLevel.Fatal
+ };
+
+ var (client, server) = await StartConnectedServerClientPair(clientOptions, serverOptions);
+
+ await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
+ await RunTicksSync(client, server, 10);
+ await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
+
+ Console.WriteLine($"Loaded client and server in {(int) stopwatch.Elapsed.TotalMilliseconds} ms");
+
+ stopwatch.Restart();
+
+ var cEntityManager = client.ResolveDependency();
+ var cPlayerManager = client.ResolveDependency();
+
+ await client.WaitPost(() =>
+ {
+ if (cEntityManager.TryGetComponent(cPlayerManager.LocalPlayer!.ControlledEntity!, out Robust.Client.GameObjects.SpriteComponent? sprite))
+ {
+ sprite.Visible = false;
+ }
+ });
+
+ var sEntityManager = server.ResolveDependency();
+ var sPlayerManager = server.ResolveDependency();
+
+ await server.WaitPost(() =>
+ {
+ if (sEntityManager.TryGetComponent(sPlayerManager.ServerSessions.Single().AttachedEntity!, out SpriteComponent? sprite))
+ {
+ sprite.Visible = false;
+ }
+ });
+
+ await RunTicksSync(client, server, 10);
+ await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
+
+ var sMapManager = server.ResolveDependency();
+
+ var tilePainter = new TilePainter(client, server);
+ var entityPainter = new GridPainter(client, server);
+ IMapGrid[] grids = null!;
+
+ await server.WaitPost(() =>
+ {
+ var playerEntity = sPlayerManager.ServerSessions.Single().AttachedEntity;
+
+ if (playerEntity.HasValue)
+ {
+ sEntityManager.DeleteEntity(playerEntity.Value);
+ }
+
+ grids = sMapManager.GetAllMapGrids(new MapId(1)).ToArray();
+
+ foreach (var grid in grids)
+ {
+ grid.WorldRotation = Angle.Zero;
+ }
+ });
+
+ await RunTicksSync(client, server, 10);
+ await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
+
+ foreach (var grid in grids)
+ {
+ var tileXSize = grid.TileSize * TilePainter.TileImageSize;
+ var tileYSize = grid.TileSize * TilePainter.TileImageSize;
+
+ var bounds = grid.LocalBounds;
+
+ var left = bounds.Left;
+ var right = bounds.Right;
+ var top = bounds.Top;
+ var bottom = bounds.Bottom;
+
+ var w = (int) Math.Ceiling(right - left) * tileXSize;
+ var h = (int) Math.Ceiling(top - bottom) * tileYSize;
+
+ var gridCanvas = new Image(w, h);
+
+ await server.WaitPost(() =>
+ {
+ tilePainter.Run(gridCanvas, grid);
+ entityPainter.Run(gridCanvas, grid);
+
+ gridCanvas.Mutate(e => e.Flip(FlipMode.Vertical));
+ });
+
+ yield return gridCanvas;
+ }
+
+ // We don't care if it fails as we have already saved the images.
+ try
+ {
+ await OneTimeTearDown();
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+ }
+}
diff --git a/Content.MapRenderer/Painters/TilePainter.cs b/Content.MapRenderer/Painters/TilePainter.cs
new file mode 100644
index 0000000000..9f6cde3779
--- /dev/null
+++ b/Content.MapRenderer/Painters/TilePainter.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
+using Robust.Shared.Map;
+using Robust.Shared.Timing;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using static Robust.UnitTesting.RobustIntegrationTest;
+
+namespace Content.MapRenderer.Painters
+{
+ public class TilePainter
+ {
+ private const string TilesPath = "/Textures/Tiles/";
+ public const int TileImageSize = EyeManager.PixelsPerMeter;
+
+ private readonly ITileDefinitionManager _sTileDefinitionManager;
+ private readonly IResourceCache _cResourceCache;
+
+ public TilePainter(ClientIntegrationInstance client, ServerIntegrationInstance server)
+ {
+ _sTileDefinitionManager = server.ResolveDependency();
+ _cResourceCache = client.ResolveDependency();
+ }
+
+ public void Run(Image gridCanvas, IMapGrid grid)
+ {
+ var stopwatch = new Stopwatch();
+ stopwatch.Start();
+
+ var bounds = grid.LocalBounds;
+ var xOffset = Math.Abs(bounds.Left);
+ var yOffset = Math.Abs(bounds.Bottom);
+ var tileSize = grid.TileSize * TileImageSize;
+
+ var images = GetTileImages(_sTileDefinitionManager, _cResourceCache, tileSize);
+ var i = 0;
+
+ grid.GetAllTiles().AsParallel().ForAll(tile =>
+ {
+ var x = (int) (tile.X + xOffset);
+ var y = (int) (tile.Y + yOffset);
+ var sprite = _sTileDefinitionManager[tile.Tile.TypeId].SpriteName;
+ var image = images[sprite];
+
+ gridCanvas.Mutate(o => o.DrawImage(image, new Point(x * tileSize, y * tileSize), 1));
+
+ i++;
+ });
+
+ Console.WriteLine($"{nameof(TilePainter)} painted {i} tiles on grid {grid.Index} in {(int) stopwatch.Elapsed.TotalMilliseconds} ms");
+ }
+
+ private Dictionary GetTileImages(
+ ITileDefinitionManager tileDefinitionManager,
+ IResourceCache resourceCache,
+ int tileSize)
+ {
+ var stopwatch = new Stopwatch();
+ stopwatch.Start();
+
+ var images = new Dictionary();
+
+ foreach (var definition in tileDefinitionManager)
+ {
+ var sprite = definition.SpriteName;
+
+ if (string.IsNullOrEmpty(sprite))
+ {
+ continue;
+ }
+
+ using var stream = resourceCache.ContentFileRead($"{TilesPath}{sprite}.png");
+ Image tileImage = Image.Load(stream);
+
+ if (tileImage.Width != tileSize || tileImage.Height != tileSize)
+ {
+ throw new NotSupportedException($"Unable to use tiles with a dimension other than {tileSize}x{tileSize}.");
+ }
+
+ images[sprite] = tileImage;
+ }
+
+ Console.WriteLine($"Indexed all tile images in {(int) stopwatch.Elapsed.TotalMilliseconds} ms");
+
+ return images;
+ }
+ }
+}
diff --git a/Content.MapRenderer/Program.cs b/Content.MapRenderer/Program.cs
new file mode 100644
index 0000000000..e536f7e86c
--- /dev/null
+++ b/Content.MapRenderer/Program.cs
@@ -0,0 +1,102 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Content.MapRenderer.Extensions;
+using Content.MapRenderer.Painters;
+using SixLabors.ImageSharp;
+
+namespace Content.MapRenderer
+{
+ internal class Program
+ {
+ private const string MapsAddedEnvKey = "FILES_ADDED";
+ private const string MapsModifiedEnvKey = "FILES_MODIFIED";
+
+ private static readonly MapPainter MapPainter = new();
+
+#pragma warning disable CA1825
+ private static readonly string[] ForceRender = {"saltern"};
+#pragma warning restore CA1825
+
+ internal static async Task Main()
+ {
+ await Run();
+ }
+
+ private static async Task Run()
+ {
+ // var created = Environment.GetEnvironmentVariable(MapsAddedEnvKey);
+ // var modified = Environment.GetEnvironmentVariable(MapsModifiedEnvKey);
+ //
+ // var yamlStream = new YamlStream();
+ //
+ // if (created != null)
+ // {
+ // yamlStream.Load(new StringReader(created));
+ // }
+ //
+ // if (modified != null)
+ // {
+ // yamlStream.Load(new StringReader(modified));
+ // }
+ //
+ // var files = new YamlSequenceNode();
+ //
+ // foreach (var doc in yamlStream.Documents)
+ // {
+ // var filesModified = (YamlSequenceNode) doc.RootNode;
+ //
+ // foreach (var node in filesModified)
+ // {
+ // files.Add(node);
+ // }
+ // }
+
+ var maps = new List(ForceRender);
+
+ // foreach (var node in files)
+ // {
+ // var fileName = node.AsString();
+ //
+ // if (!fileName.StartsWith("Resources/Maps/") ||
+ // !fileName.EndsWith("yml"))
+ // {
+ // continue;
+ // }
+ //
+ // maps.Add(fileName);
+ // }
+
+ Console.WriteLine($"Creating images for {maps.Count} maps");
+
+ var mapNames = new List();
+ foreach (var map in maps)
+ {
+ Console.WriteLine($"Painting map {map}");
+
+ await foreach (var grid in MapPainter.Paint(map))
+ {
+ var directory = DirectoryExtensions.MapImages().FullName;
+ Directory.CreateDirectory(directory);
+
+ var fileName = Path.GetFileNameWithoutExtension(map);
+ var savePath = $"{directory}{Path.DirectorySeparatorChar}{fileName}.png";
+
+ Console.WriteLine($"Writing grid of size {grid.Width}x{grid.Height} to {savePath}");
+
+ await grid.SaveAsPngAsync(savePath);
+ grid.Dispose();
+
+ mapNames.Add(fileName);
+ }
+ }
+
+ var mapNamesString = $"[{string.Join(',', mapNames.Select(s => $"\"{s}\""))}]";
+ Console.WriteLine($@"::set-output name=map_names::{mapNamesString}");
+ Console.WriteLine($"Created {maps.Count} map images.");
+ }
+ }
+}
diff --git a/SpaceStation14.sln b/SpaceStation14.sln
index 23818d7276..d678ffce63 100644
--- a/SpaceStation14.sln
+++ b/SpaceStation14.sln
@@ -100,6 +100,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pow3r", "Pow3r\Pow3r.csproj
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Content.Shared.Database", "Content.Shared.Database\Content.Shared.Database.csproj", "{8842381D-3426-4BA8-93DA-599AB14D88E9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Content.MapRenderer", "Content.MapRenderer\Content.MapRenderer.csproj", "{199BBEA1-7627-434B-B6F6-0F52A7C0E1E0}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -233,6 +235,10 @@ Global
{8842381D-3426-4BA8-93DA-599AB14D88E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8842381D-3426-4BA8-93DA-599AB14D88E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8842381D-3426-4BA8-93DA-599AB14D88E9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {199BBEA1-7627-434B-B6F6-0F52A7C0E1E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {199BBEA1-7627-434B-B6F6-0F52A7C0E1E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {199BBEA1-7627-434B-B6F6-0F52A7C0E1E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {199BBEA1-7627-434B-B6F6-0F52A7C0E1E0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings
index 3287ea383e..da231d591f 100644
--- a/SpaceStation14.sln.DotSettings
+++ b/SpaceStation14.sln.DotSettings
@@ -257,6 +257,7 @@
True
True
True
+ True
True
True
True