Index: binary-improvements2/MapRendering/src/MapRenderer.cs
===================================================================
--- binary-improvements2/MapRendering/src/MapRenderer.cs	(revision 436)
+++ binary-improvements2/MapRendering/src/MapRenderer.cs	(revision 437)
@@ -317,11 +317,9 @@
 			writer.WriteBeginObject ();
 			
-			writer.WriteString ("blockSize");
-			writer.WriteNameSeparator ();
+			writer.WritePropertyName ("blockSize");
 			writer.WriteInt32 (Constants.MapBlockSize);
 			
 			writer.WriteValueSeparator ();
-			writer.WriteString ("maxZoom");
-			writer.WriteNameSeparator ();
+			writer.WritePropertyName ("maxZoom");
 			writer.WriteInt32 (Constants.Zoomlevels - 1);
 			
Index: binary-improvements2/WebServer/ModInfo.xml
===================================================================
--- binary-improvements2/WebServer/ModInfo.xml	(revision 436)
+++ binary-improvements2/WebServer/ModInfo.xml	(revision 437)
@@ -5,5 +5,5 @@
 	<Description value="Integrated Webserver for the Web Dashboard and server APIs" />
 	<Author value="The Fun Pimps LLC" />
-	<Version value="21.0.289.1" />
+	<Version value="21.0.289.2" />
 	<Website value="" />
 </xml>
Index: binary-improvements2/WebServer/WebServer.csproj
===================================================================
--- binary-improvements2/WebServer/WebServer.csproj	(revision 436)
+++ binary-improvements2/WebServer/WebServer.csproj	(revision 437)
@@ -120,5 +120,4 @@
     <Compile Include="src\WebAPI\AbsRestApi.cs" />
     <Compile Include="src\WebAPI\AbsWebAPI.cs" />
-    <Compile Include="src\WebAPI\APIs\Bloodmoon.cs" />
     <Compile Include="src\WebAPI\APIs\Command.cs" />
     <Compile Include="src\WebAPI\APIs\GameData\Item.cs" />
@@ -134,7 +133,10 @@
     <Compile Include="src\WebAPI\APIs\Permissions\WebUsers.cs" />
     <Compile Include="src\WebAPI\APIs\Permissions\Whitelist.cs" />
-    <Compile Include="src\WebAPI\APIs\ServerInfo.cs" />
-    <Compile Include="src\WebAPI\APIs\ServerStats.cs" />
+    <Compile Include="src\WebAPI\APIs\ServerState\GamePrefs.cs" />
+    <Compile Include="src\WebAPI\APIs\ServerState\GameStats.cs" />
+    <Compile Include="src\WebAPI\APIs\ServerState\ServerInfo.cs" />
+    <Compile Include="src\WebAPI\APIs\ServerState\ServerStats.cs" />
     <Compile Include="src\WebAPI\APIs\WorldState\Animal.cs" />
+    <Compile Include="src\WebAPI\APIs\WorldState\Bloodmoon.cs" />
     <Compile Include="src\WebAPI\APIs\WorldState\GetLandClaims.cs" />
     <Compile Include="src\WebAPI\APIs\WorldState\GetPlayerInventories.cs" />
Index: binary-improvements2/WebServer/src/WebAPI/APIs/Bloodmoon.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/Bloodmoon.cs	(revision 436)
+++ 	(revision )
@@ -1,39 +1,0 @@
-using JetBrains.Annotations;
-using Utf8Json;
-
-namespace Webserver.WebAPI.APIs {
-	[UsedImplicitly]
-	public class Bloodmoon : AbsRestApi {
-		private static readonly byte[] jsonKeyGameTime = JsonWriter.GetEncodedPropertyNameWithBeginObject ("gameTime");
-		private static readonly byte[] jsonKeyBloodmoonActive = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("bloodmoonActive");
-		private static readonly byte[] jsonKeyNextBloodmoon = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("nextBloodmoon");
-		private static readonly byte[] jsonKeyNextBloodmoonEnd = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("nextBloodmoonEnd");
-
-		protected override void HandleRestGet (RequestContext _context) {
-			ulong currentWorldTime = GameManager.Instance.World.worldTime;
-			(int currentDay, int currentHour, int currentMinut) = GameUtils.WorldTimeToElements (currentWorldTime);
-
-			int nextBloodmoonDay = GameStats.GetInt (EnumUtils.Parse<EnumGameStats> (nameof (EnumGameStats.BloodMoonDay)));
-
-			(int duskHour, int dawnHour) duskDawnHours = GameUtils.CalcDuskDawnHours (GamePrefs.GetInt (EnumUtils.Parse<EnumGamePrefs> (nameof (EnumGamePrefs.DayLightLength))));
-
-			PrepareEnvelopedResult (out JsonWriter writer);
-			
-			writer.WriteRaw (jsonKeyGameTime);
-			JsonCommons.WriteGameTimeObject (ref writer, currentDay, currentHour, currentMinut);
-
-			writer.WriteRaw (jsonKeyBloodmoonActive);
-			writer.WriteBoolean (GameUtils.IsBloodMoonTime (currentWorldTime, duskDawnHours, nextBloodmoonDay));
-			
-			writer.WriteRaw (jsonKeyNextBloodmoon);
-			JsonCommons.WriteGameTimeObject (ref writer, nextBloodmoonDay, duskDawnHours.duskHour, 0);
-			
-			writer.WriteRaw (jsonKeyNextBloodmoonEnd);
-			JsonCommons.WriteGameTimeObject (ref writer, nextBloodmoonDay + 1, duskDawnHours.dawnHour, 0);
-			
-			writer.WriteEndObject ();
-
-			SendEnvelopedResult (_context, ref writer);
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/GameData/Item.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/GameData/Item.cs	(revision 436)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/GameData/Item.cs	(revision 437)
@@ -57,7 +57,5 @@
 		}
 
-		public override int DefaultPermissionLevel () {
-			return AdminWebModules.PermissionLevelGuest;
-		}
+		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
 	}
 }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/ServerInfo.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/ServerInfo.cs	(revision 436)
+++ 	(revision )
@@ -1,97 +1,0 @@
-using System.Collections.Generic;
-using JetBrains.Annotations;
-using Utf8Json;
-
-namespace Webserver.WebAPI.APIs {
-	[UsedImplicitly]
-	public class ServerInfo : AbsRestApi {
-		private static readonly UnityEngine.Profiling.CustomSampler buildSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_ServerInfo_BuildSampler");
-
-		private static readonly byte[] keyType = JsonWriter.GetEncodedPropertyNameWithBeginObject ("type");
-		private static readonly byte[] keyValue = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("value");
-
-		private int largestBuffer;
-
-		protected override void HandleRestGet (RequestContext _context) {
-			buildSampler.Begin ();
-
-			PrepareEnvelopedResult (out JsonWriter writer);
-			
-			writer.EnsureCapacity (largestBuffer);
-			writer.WriteBeginObject ();
-			
-			GameServerInfo gsi = ConnectionManager.Instance.LocalServerInfo;
-
-			IList<GameInfoString> list = EnumUtils.Values<GameInfoString> ();
-			for (int i = 0; i < list.Count; i++) {
-				GameInfoString stringGamePref = list [i];
-
-				if (i > 0) {
-					writer.WriteValueSeparator ();
-				}
-
-				writer.WriteString (stringGamePref.ToStringCached ());
-				writer.WriteNameSeparator ();
-
-				writer.WriteRaw (keyType);
-				writer.WriteString ("string");
-
-				writer.WriteRaw (keyValue);
-				writer.WriteString (gsi.GetValue (stringGamePref));
-
-				writer.WriteEndObject ();
-			}
-
-			IList<GameInfoInt> ints = EnumUtils.Values<GameInfoInt> ();
-			for (int i = 0; i < ints.Count; i++) {
-				GameInfoInt intGamePref = ints [i];
-
-				if (i > 0) {
-					writer.WriteValueSeparator ();
-				}
-
-				writer.WriteString (intGamePref.ToStringCached ());
-				writer.WriteNameSeparator ();
-
-				writer.WriteRaw (keyType);
-				writer.WriteString ("int");
-
-				writer.WriteRaw (keyValue);
-				writer.WriteInt32 (gsi.GetValue (intGamePref));
-
-				writer.WriteEndObject ();
-			}
-
-			IList<GameInfoBool> prefs = EnumUtils.Values<GameInfoBool> ();
-			for (int i = 0; i < prefs.Count; i++) {
-				GameInfoBool boolGamePref = prefs [i];
-
-				if (i > 0) {
-					writer.WriteValueSeparator ();
-				}
-
-				writer.WriteString (boolGamePref.ToStringCached ());
-				writer.WriteNameSeparator ();
-
-				writer.WriteRaw (keyType);
-				writer.WriteString ("bool");
-
-				writer.WriteRaw (keyValue);
-				writer.WriteBoolean (gsi.GetValue (boolGamePref));
-
-				writer.WriteEndObject ();
-			}
-
-			writer.WriteEndObject ();
-			
-			buildSampler.End ();
-
-			int bufferContentSize = writer.CurrentOffset + 128;
-			if (bufferContentSize > largestBuffer) {
-				largestBuffer = bufferContentSize;
-			}
-			
-			SendEnvelopedResult (_context, ref writer);
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/GamePrefs.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/GamePrefs.cs	(revision 437)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/GamePrefs.cs	(revision 437)
@@ -0,0 +1,112 @@
+using System;
+using JetBrains.Annotations;
+using Utf8Json;
+
+namespace Webserver.WebAPI.APIs.ServerState {
+	[UsedImplicitly]
+	public class GamePrefs : AbsRestApi {
+		private static readonly byte[] keyType = JsonWriter.GetEncodedPropertyNameWithBeginObject ("type");
+		private static readonly byte[] keyValue = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("value");
+		private static readonly byte[] keyDefault = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("default");
+
+		private int largestBuffer;
+
+		protected override void HandleRestGet (RequestContext _context) {
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.EnsureCapacity (largestBuffer);
+			writer.WriteBeginObject ();
+
+			bool first = true;
+			foreach (EnumGamePrefs stat in EnumUtils.Values<EnumGamePrefs> ()) {
+				string name = stat.ToStringCached ();
+				global::GamePrefs.EnumType? type = global::GamePrefs.GetPrefType (stat);
+
+				if (name.Contains ("Password", StringComparison.Ordinal)) {
+					continue; // TODO: Skip or not?
+				}
+
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+
+				first = false;
+				
+				writer.WritePropertyName (name);
+				
+				writer.WriteRaw (keyType);
+				writer.WriteString (type.HasValue ? type.Value.ToStringCached () : "noType");
+				
+				writer.WriteRaw (keyValue);
+				switch (type) {
+					case global::GamePrefs.EnumType.Int:
+						writer.WriteInt32 (global::GamePrefs.GetInt (stat));
+						break;
+					case global::GamePrefs.EnumType.Float:
+						writer.WriteSingle (global::GamePrefs.GetFloat (stat));
+						break;
+					case global::GamePrefs.EnumType.String:
+						writer.WriteString (global::GamePrefs.GetString (stat));
+						break;
+					case global::GamePrefs.EnumType.Bool:
+						writer.WriteBoolean (global::GamePrefs.GetBool (stat));
+						break;
+					default:
+						writer.WriteNull ();
+						break;
+				}
+				
+				writer.WriteRaw (keyDefault);
+				object defaultValue = global::GamePrefs.GetDefault (stat);
+				switch (type) {
+					case global::GamePrefs.EnumType.Int:
+						if (defaultValue is int defaultInt) {
+							writer.WriteInt32 (defaultInt);
+						} else {
+							writer.WriteNull ();
+						}
+
+						break;
+					case global::GamePrefs.EnumType.Float:
+						if (defaultValue is float defaultFloat) {
+							writer.WriteSingle (defaultFloat);
+						} else {
+							writer.WriteNull ();
+						}
+
+						break;
+					case global::GamePrefs.EnumType.String:
+						if (defaultValue is string defaultString) {
+							writer.WriteString (defaultString);
+						} else {
+							writer.WriteNull ();
+						}
+
+						break;
+					case global::GamePrefs.EnumType.Bool:
+						if (defaultValue is bool defaultBool) {
+							writer.WriteBoolean (defaultBool);
+						} else {
+							writer.WriteNull ();
+						}
+
+						break;
+					default:
+						writer.WriteNull ();
+						break;
+				}
+
+				writer.WriteEndObject ();
+			}
+			
+			writer.WriteEndObject ();
+
+			int bufferContentSize = writer.CurrentOffset + 128;
+			if (bufferContentSize > largestBuffer) {
+				largestBuffer = bufferContentSize;
+			}
+			
+			SendEnvelopedResult (_context, ref writer);
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/GameStats.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/GameStats.cs	(revision 437)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/GameStats.cs	(revision 437)
@@ -0,0 +1,66 @@
+using JetBrains.Annotations;
+using Utf8Json;
+
+namespace Webserver.WebAPI.APIs.ServerState {
+	[UsedImplicitly]
+	public class GameStats : AbsRestApi {
+		private static readonly byte[] keyType = JsonWriter.GetEncodedPropertyNameWithBeginObject ("type");
+		private static readonly byte[] keyValue = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("value");
+
+		private int largestBuffer;
+
+		protected override void HandleRestGet (RequestContext _context) {
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.EnsureCapacity (largestBuffer);
+			writer.WriteBeginObject ();
+
+			bool first = true;
+			foreach (EnumGameStats stat in EnumUtils.Values<EnumGameStats> ()) {
+				string name = stat.ToStringCached ();
+				global::GameStats.EnumType? type = global::GameStats.GetStatType (stat);
+
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+
+				first = false;
+				
+				writer.WritePropertyName (name);
+				
+				writer.WriteRaw (keyType);
+				writer.WriteString (type.HasValue ? type.Value.ToStringCached () : "noType");
+				
+				writer.WriteRaw (keyValue);
+				switch (type) {
+					case global::GameStats.EnumType.Int:
+						writer.WriteInt32 (global::GameStats.GetInt (stat));
+						break;
+					case global::GameStats.EnumType.Float:
+						writer.WriteSingle (global::GameStats.GetFloat (stat));
+						break;
+					case global::GameStats.EnumType.String:
+						writer.WriteString (global::GameStats.GetString (stat));
+						break;
+					case global::GameStats.EnumType.Bool:
+						writer.WriteBoolean (global::GameStats.GetBool (stat));
+						break;
+					default:
+						writer.WriteNull ();
+						break;
+				}
+				
+				writer.WriteEndObject ();
+			}
+			
+			writer.WriteEndObject ();
+
+			int bufferContentSize = writer.CurrentOffset + 128;
+			if (bufferContentSize > largestBuffer) {
+				largestBuffer = bufferContentSize;
+			}
+			
+			SendEnvelopedResult (_context, ref writer);
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/ServerInfo.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/ServerInfo.cs	(revision 437)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/ServerInfo.cs	(revision 437)
@@ -0,0 +1,105 @@
+using System.Collections.Generic;
+using JetBrains.Annotations;
+using Utf8Json;
+using Webserver.Permissions;
+
+namespace Webserver.WebAPI.APIs.ServerState {
+	[UsedImplicitly]
+	public class ServerInfo : AbsRestApi {
+		private static readonly UnityEngine.Profiling.CustomSampler buildSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_ServerInfo_BuildSampler");
+
+		private static readonly byte[] keyType = JsonWriter.GetEncodedPropertyNameWithBeginObject ("type");
+		private static readonly byte[] keyValue = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("value");
+
+		private int largestBuffer;
+
+		protected override void HandleRestGet (RequestContext _context) {
+			buildSampler.Begin ();
+
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.EnsureCapacity (largestBuffer);
+			writer.WriteBeginObject ();
+			
+			GameServerInfo gsi = ConnectionManager.Instance.LocalServerInfo;
+
+			bool first = true;
+
+			IList<GameInfoString> list = EnumUtils.Values<GameInfoString> ();
+			for (int i = 0; i < list.Count; i++) {
+				GameInfoString stringGamePref = list [i];
+
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+
+				first = false;
+
+				writer.WritePropertyName (stringGamePref.ToStringCached ());
+
+				writer.WriteRaw (keyType);
+				writer.WriteString ("string");
+
+				writer.WriteRaw (keyValue);
+				writer.WriteString (gsi.GetValue (stringGamePref));
+
+				writer.WriteEndObject ();
+			}
+
+			IList<GameInfoInt> ints = EnumUtils.Values<GameInfoInt> ();
+			for (int i = 0; i < ints.Count; i++) {
+				GameInfoInt intGamePref = ints [i];
+
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+
+				first = false;
+
+				writer.WritePropertyName (intGamePref.ToStringCached ());
+
+				writer.WriteRaw (keyType);
+				writer.WriteString ("int");
+
+				writer.WriteRaw (keyValue);
+				writer.WriteInt32 (gsi.GetValue (intGamePref));
+
+				writer.WriteEndObject ();
+			}
+
+			IList<GameInfoBool> prefs = EnumUtils.Values<GameInfoBool> ();
+			for (int i = 0; i < prefs.Count; i++) {
+				GameInfoBool boolGamePref = prefs [i];
+
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+
+				first = false;
+
+				writer.WritePropertyName (boolGamePref.ToStringCached ());
+
+				writer.WriteRaw (keyType);
+				writer.WriteString ("bool");
+
+				writer.WriteRaw (keyValue);
+				writer.WriteBoolean (gsi.GetValue (boolGamePref));
+
+				writer.WriteEndObject ();
+			}
+
+			writer.WriteEndObject ();
+			
+			buildSampler.End ();
+
+			int bufferContentSize = writer.CurrentOffset + 128;
+			if (bufferContentSize > largestBuffer) {
+				largestBuffer = bufferContentSize;
+			}
+			
+			SendEnvelopedResult (_context, ref writer);
+		}
+
+		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/ServerStats.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/ServerStats.cs	(revision 437)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/ServerState/ServerStats.cs	(revision 437)
@@ -0,0 +1,38 @@
+using JetBrains.Annotations;
+using Utf8Json;
+using Webserver.LiveData;
+using Webserver.Permissions;
+
+namespace Webserver.WebAPI.APIs.ServerState {
+	[UsedImplicitly]
+	public class ServerStats : AbsRestApi {
+		private static readonly byte[] jsonKeyGameTime = JsonWriter.GetEncodedPropertyNameWithBeginObject ("gameTime");
+		private static readonly byte[] jsonKeyPlayers = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("players");
+		private static readonly byte[] jsonKeyHostiles = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("hostiles");
+		private static readonly byte[] jsonKeyAnimals = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("animals");
+		
+
+		protected override void HandleRestGet (RequestContext _context) {
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.WriteRaw (jsonKeyGameTime);
+			(int days, int hours, int minutes) = GameUtils.WorldTimeToElements (GameManager.Instance.World.worldTime);
+			JsonCommons.WriteGameTimeObject (ref writer, days, hours, minutes);
+
+			writer.WriteRaw (jsonKeyPlayers);
+			writer.WriteInt32 (GameManager.Instance.World.Players.Count);
+			
+			writer.WriteRaw (jsonKeyHostiles);
+			writer.WriteInt32 (Hostiles.Instance.GetCount ());
+			
+			writer.WriteRaw (jsonKeyAnimals);
+			writer.WriteInt32 (Animals.Instance.GetCount ());
+			
+			writer.WriteEndObject ();
+
+			SendEnvelopedResult (_context, ref writer);
+		}
+
+		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/ServerStats.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/ServerStats.cs	(revision 436)
+++ 	(revision )
@@ -1,38 +1,0 @@
-using JetBrains.Annotations;
-using Utf8Json;
-using Webserver.LiveData;
-using Webserver.Permissions;
-
-namespace Webserver.WebAPI.APIs {
-	[UsedImplicitly]
-	public class ServerStats : AbsRestApi {
-		private static readonly byte[] jsonKeyGameTime = JsonWriter.GetEncodedPropertyNameWithBeginObject ("gameTime");
-		private static readonly byte[] jsonKeyPlayers = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("players");
-		private static readonly byte[] jsonKeyHostiles = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("hostiles");
-		private static readonly byte[] jsonKeyAnimals = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("animals");
-		
-
-		protected override void HandleRestGet (RequestContext _context) {
-			PrepareEnvelopedResult (out JsonWriter writer);
-			
-			writer.WriteRaw (jsonKeyGameTime);
-			(int days, int hours, int minutes) = GameUtils.WorldTimeToElements (GameManager.Instance.World.worldTime);
-			JsonCommons.WriteGameTimeObject (ref writer, days, hours, minutes);
-
-			writer.WriteRaw (jsonKeyPlayers);
-			writer.WriteInt32 (GameManager.Instance.World.Players.Count);
-			
-			writer.WriteRaw (jsonKeyHostiles);
-			writer.WriteInt32 (Hostiles.Instance.GetCount ());
-			
-			writer.WriteRaw (jsonKeyAnimals);
-			writer.WriteInt32 (Animals.Instance.GetCount ());
-			
-			writer.WriteEndObject ();
-
-			SendEnvelopedResult (_context, ref writer);
-		}
-
-		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/WorldState/Bloodmoon.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/WorldState/Bloodmoon.cs	(revision 437)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/WorldState/Bloodmoon.cs	(revision 437)
@@ -0,0 +1,39 @@
+using JetBrains.Annotations;
+using Utf8Json;
+
+namespace Webserver.WebAPI.APIs.WorldState {
+	[UsedImplicitly]
+	public class Bloodmoon : AbsRestApi {
+		private static readonly byte[] jsonKeyGameTime = JsonWriter.GetEncodedPropertyNameWithBeginObject ("gameTime");
+		private static readonly byte[] jsonKeyBloodmoonActive = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("bloodmoonActive");
+		private static readonly byte[] jsonKeyNextBloodmoon = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("nextBloodmoon");
+		private static readonly byte[] jsonKeyNextBloodmoonEnd = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("nextBloodmoonEnd");
+
+		protected override void HandleRestGet (RequestContext _context) {
+			ulong currentWorldTime = GameManager.Instance.World.worldTime;
+			(int currentDay, int currentHour, int currentMinut) = GameUtils.WorldTimeToElements (currentWorldTime);
+
+			int nextBloodmoonDay = GameStats.GetInt (EnumUtils.Parse<EnumGameStats> (nameof (EnumGameStats.BloodMoonDay)));
+
+			(int duskHour, int dawnHour) duskDawnHours = GameUtils.CalcDuskDawnHours (GamePrefs.GetInt (EnumUtils.Parse<EnumGamePrefs> (nameof (EnumGamePrefs.DayLightLength))));
+
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.WriteRaw (jsonKeyGameTime);
+			JsonCommons.WriteGameTimeObject (ref writer, currentDay, currentHour, currentMinut);
+
+			writer.WriteRaw (jsonKeyBloodmoonActive);
+			writer.WriteBoolean (GameUtils.IsBloodMoonTime (currentWorldTime, duskDawnHours, nextBloodmoonDay));
+			
+			writer.WriteRaw (jsonKeyNextBloodmoon);
+			JsonCommons.WriteGameTimeObject (ref writer, nextBloodmoonDay, duskDawnHours.duskHour, 0);
+			
+			writer.WriteRaw (jsonKeyNextBloodmoonEnd);
+			JsonCommons.WriteGameTimeObject (ref writer, nextBloodmoonDay + 1, duskDawnHours.dawnHour, 0);
+			
+			writer.WriteEndObject ();
+
+			SendEnvelopedResult (_context, ref writer);
+		}
+	}
+}
Index: binary-improvements2/bin/Mods/TFP_WebServer/ModInfo.xml
===================================================================
--- binary-improvements2/bin/Mods/TFP_WebServer/ModInfo.xml	(revision 436)
+++ binary-improvements2/bin/Mods/TFP_WebServer/ModInfo.xml	(revision 437)
@@ -5,5 +5,5 @@
 	<Description value="Integrated Webserver for the Web Dashboard and server APIs" />
 	<Author value="The Fun Pimps LLC" />
-	<Version value="21.0.289.1" />
+	<Version value="21.0.289.2" />
 	<Website value="" />
 </xml>
