diff --git a/Robust.Client/ClientWarmup.cs b/Robust.Client/ClientWarmup.cs
new file mode 100644
index 0000000000..57ce11d91e
--- /dev/null
+++ b/Robust.Client/ClientWarmup.cs
@@ -0,0 +1,28 @@
+using System.Threading.Tasks;
+using Robust.Shared;
+using Robust.Shared.Resources;
+using SixLabors.ImageSharp;
+
+namespace Robust.Client;
+
+///
+/// Logic for "warming up" things like slow static constructors concurrently.
+///
+internal static class ClientWarmup
+{
+ public static void RunWarmup()
+ {
+ Task.Run(WarmupCore);
+ }
+
+ private static void WarmupCore()
+ {
+ // Get ImageSharp loaded.
+ _ = Configuration.Default;
+
+ SharedWarmup.WarmupCore();
+
+ // Preload the JSON loading code.
+ RsiLoading.Warmup();
+ }
+}
diff --git a/Robust.Client/GameController/GameController.Standalone.cs b/Robust.Client/GameController/GameController.Standalone.cs
index c89099a2a4..550e2ff49b 100644
--- a/Robust.Client/GameController/GameController.Standalone.cs
+++ b/Robust.Client/GameController/GameController.Standalone.cs
@@ -45,6 +45,8 @@ namespace Robust.Client
private static void ParsedMain(CommandLineArgs args, bool contentStart, IMainArgs? loaderArgs, GameControllerOptions options)
{
+ ClientWarmup.RunWarmup();
+
var deps = IoCManager.InitThread();
var mode = args.Headless ? DisplayMode.Headless : DisplayMode.Clyde;
diff --git a/Robust.Server/Program.cs b/Robust.Server/Program.cs
index 811d8c6cac..d711768f5a 100644
--- a/Robust.Server/Program.cs
+++ b/Robust.Server/Program.cs
@@ -48,6 +48,8 @@ namespace Robust.Server
private static void ParsedMain(CommandLineArgs args, bool contentStart, ServerOptions options)
{
+ ServerWarmup.RunWarmup();
+
Thread.CurrentThread.Name = "Main Thread";
var deps = IoCManager.InitThread();
ServerIoC.RegisterIoC(deps);
diff --git a/Robust.Server/ServerWarmup.cs b/Robust.Server/ServerWarmup.cs
new file mode 100644
index 0000000000..537ee33a74
--- /dev/null
+++ b/Robust.Server/ServerWarmup.cs
@@ -0,0 +1,20 @@
+using System.Threading.Tasks;
+using Robust.Shared;
+
+namespace Robust.Server;
+
+///
+/// Logic for "warming up" things like slow static constructors concurrently.
+///
+internal static class ServerWarmup
+{
+ public static void RunWarmup()
+ {
+ Task.Run(WarmupCore);
+ }
+
+ private static void WarmupCore()
+ {
+ SharedWarmup.WarmupCore();
+ }
+}
diff --git a/Robust.Shared/Resources/RsiLoading.cs b/Robust.Shared/Resources/RsiLoading.cs
index 86eca2942a..8a9853c7be 100644
--- a/Robust.Shared/Resources/RsiLoading.cs
+++ b/Robust.Shared/Resources/RsiLoading.cs
@@ -96,6 +96,12 @@ internal static class RsiLoading
return new RsiMetadata(size, states);
}
+ public static void Warmup()
+ {
+ // Just a random RSI I pulled from SS14.
+ const string warmupJson = @"{""version"":1,""license"":""CC-BY-SA-3.0"",""copyright"":""Space Wizards Federation"",""size"":{""x"":32,""y"":32},""states"":[{""name"":""mono""}]}";
+ JsonSerializer.Deserialize(warmupJson, SerializerOptions);
+ }
internal sealed class RsiMetadata
{
diff --git a/Robust.Shared/SharedWarmup.cs b/Robust.Shared/SharedWarmup.cs
new file mode 100644
index 0000000000..0b6e6ada58
--- /dev/null
+++ b/Robust.Shared/SharedWarmup.cs
@@ -0,0 +1,21 @@
+using System.Runtime.CompilerServices;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Maths;
+
+namespace Robust.Shared;
+
+///
+/// Logic for "warming up" things like slow static constructors concurrently.
+///
+internal static class SharedWarmup
+{
+ [MethodImpl(MethodImplOptions.NoOptimization)]
+ public static void WarmupCore()
+ {
+ // Color's cctor unironically takes ~12ms.
+ RuntimeHelpers.RunClassConstructor(typeof(Color).TypeHandle);
+
+ // This ends up initializing prometheus-net which takes quite a while.
+ RuntimeHelpers.RunClassConstructor(typeof(EntitySystemManager).TypeHandle);
+ }
+}