Index: /binary-improvements/7dtd-server-fixes/7dtd-server-fixes.csproj
===================================================================
--- /binary-improvements/7dtd-server-fixes/7dtd-server-fixes.csproj	(revision 454)
+++ /binary-improvements/7dtd-server-fixes/7dtd-server-fixes.csproj	(revision 455)
@@ -83,5 +83,4 @@
   <ItemGroup>
     <Compile Include="src\AssemblyInfo.cs" />
-    <Compile Include="src\JSON\JsonManualBuilder.cs" />
     <Compile Include="src\LiveData\Animals.cs" />
     <Compile Include="src\LiveData\Hostiles.cs" />
@@ -91,19 +90,8 @@
     <Compile Include="src\PersistentData\Players.cs" />
     <Compile Include="src\PersistentData\Player.cs" />
-    <Compile Include="src\JSON\JSONNode.cs" />
-    <Compile Include="src\JSON\JSONArray.cs" />
-    <Compile Include="src\JSON\JSONObject.cs" />
-    <Compile Include="src\JSON\JSONNumber.cs" />
-    <Compile Include="src\JSON\JSONString.cs" />
-    <Compile Include="src\JSON\JSONBoolean.cs" />
-    <Compile Include="src\BlockingQueue.cs" />
-    <Compile Include="src\JSON\Parser.cs" />
-    <Compile Include="src\JSON\JSONNull.cs" />
-    <Compile Include="src\JSON\MalformedJSONException.cs" />
     <Compile Include="src\API.cs" />
     <Compile Include="src\AllocsUtils.cs" />
     <Compile Include="src\LandClaimList.cs" />
     <Compile Include="src\PersistentData\Attributes.cs" />
-    <Compile Include="src\JSON\JSONValue.cs" />
     <Compile Include="src\LiveData\EntityFilterList.cs" />
   </ItemGroup>
@@ -112,5 +100,4 @@
     <Folder Include="src\" />
     <Folder Include="src\PersistentData\" />
-    <Folder Include="src\JSON\Parser\" />
   </ItemGroup>
   <ItemGroup>
Index: /binary-improvements/7dtd-server-fixes/ModInfo.xml
===================================================================
--- /binary-improvements/7dtd-server-fixes/ModInfo.xml	(revision 454)
+++ /binary-improvements/7dtd-server-fixes/ModInfo.xml	(revision 455)
@@ -5,6 +5,6 @@
 		<Description value="Common functions" />
 		<Author value="Christian 'Alloc' Illy" />
-		<Version value="29" />
-		<Website value="http://7dtd.illy.bz" />
+		<Version value="30" />
+		<Website value="https://7dtd.illy.bz" />
 	</ModInfo>
 </xml>
Index: /binary-improvements/7dtd-server-fixes/src/API.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/API.cs	(revision 454)
+++ /binary-improvements/7dtd-server-fixes/src/API.cs	(revision 455)
@@ -1,11 +1,12 @@
 using System.Collections.Generic;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
 using Platform.Steam;
 
 namespace AllocsFixes {
+	[UsedImplicitly]
 	public class API : IModApi {
 		public void InitMod (Mod _modInstance) {
 			ModEvents.GameStartDone.RegisterHandler (GameAwake);
-			ModEvents.GameShutdown.RegisterHandler (GameShutdown);
 			ModEvents.SavePlayerData.RegisterHandler (SavePlayerData);
 			ModEvents.PlayerSpawning.RegisterHandler (PlayerSpawning);
@@ -15,16 +16,13 @@
 		}
 
-		public void GameAwake () {
+		private static void GameAwake () {
 			PersistentContainer.Load ();
 		}
 
-		public void GameShutdown () {
-		}
-
-		public void SavePlayerData (ClientInfo _cInfo, PlayerDataFile _playerDataFile) {
+		private static void SavePlayerData (ClientInfo _cInfo, PlayerDataFile _playerDataFile) {
 			PersistentContainer.Instance.Players.GetOrCreate (_cInfo.InternalId, _cInfo.PlatformId, _cInfo.CrossplatformId).Update (_cInfo, _playerDataFile);
 		}
 
-		public void PlayerSpawning (ClientInfo _cInfo, int _chunkViewDim, PlayerProfile _playerProfile) {
+		private static void PlayerSpawning (ClientInfo _cInfo, int _chunkViewDim, PlayerProfile _playerProfile) {
 			string owner = null;
 			if (_cInfo.PlatformId is UserIdentifierSteam identifierSteam) {
@@ -32,15 +30,10 @@
 			}
 
-			Log.Out ("Player connected" +
-			         ", entityid=" + _cInfo.entityId +
-			         ", name=" + _cInfo.playerName +
-			         ", pltfmid=" + (_cInfo.PlatformId?.CombinedString ?? "<unknown>") +
-			         ", crossid=" + (_cInfo.CrossplatformId?.CombinedString ?? "<unknown/none>") +
-			         ", steamOwner=" + (owner ?? "<unknown/none>") +
-			         ", ip=" + _cInfo.ip
+			Log.Out (
+				$"Player connected, entityid={_cInfo.entityId}, name={_cInfo.playerName}, pltfmid={_cInfo.PlatformId?.CombinedString ?? "<unknown>"}, crossid={_cInfo.CrossplatformId?.CombinedString ?? "<unknown/none>"}, steamOwner={owner ?? "<unknown/none>"}, ip={_cInfo.ip}"
 			);
 		}
 
-		public void PlayerDisconnected (ClientInfo _cInfo, bool _bShutdown) {
+		private static void PlayerDisconnected (ClientInfo _cInfo, bool _bShutdown) {
 			Player p = PersistentContainer.Instance.Players.GetByInternalId (_cInfo.InternalId);
 			if (p != null) {
@@ -53,13 +46,12 @@
 		}
 
-		public void PlayerSpawned (ClientInfo _cInfo, RespawnType _respawnReason, Vector3i _spawnPos) {
+		private static void PlayerSpawned (ClientInfo _cInfo, RespawnType _respawnReason, Vector3i _spawnPos) {
 			PersistentContainer.Instance.Players.GetOrCreate (_cInfo.InternalId, _cInfo.PlatformId, _cInfo.CrossplatformId).SetOnline (_cInfo);
 			PersistentContainer.Instance.Save ();
 		}
 
-		private const string ANSWER =
-			"     [ff0000]I[-] [ff7f00]W[-][ffff00]A[-][80ff00]S[-] [00ffff]H[-][0080ff]E[-][0000ff]R[-][8b00ff]E[-]";
+		private const string ANSWER = "     [ff0000]I[-] [ff7f00]W[-][ffff00]A[-][80ff00]S[-] [00ffff]H[-][0080ff]E[-][0000ff]R[-][8b00ff]E[-]";
 
-		public bool ChatMessage (ClientInfo _cInfo, EChatType _type, int _senderId, string _msg, string _mainName,
+		private static bool ChatMessage (ClientInfo _cInfo, EChatType _type, int _senderId, string _msg, string _mainName,
 			bool _localizeMain, List<int> _recipientEntityIds) {
 			if (string.IsNullOrEmpty (_msg) || !_msg.EqualsCaseInsensitive ("/alloc")) {
@@ -68,8 +60,8 @@
 
 			if (_cInfo != null) {
-				Log.Out ("Sent chat hook reply to {0}", _cInfo.InternalId);
+				Log.Out ($"Sent chat hook reply to {_cInfo.InternalId}");
 				_cInfo.SendPackage (NetPackageManager.GetPackage<NetPackageChat> ().Setup (EChatType.Whisper, -1, ANSWER, "", false, null));
 			} else {
-				Log.Error ("ChatHookExample: Argument _cInfo null on message: {0}", _msg);
+				Log.Error ($"ChatHookExample: Argument _cInfo null on message: {_msg}");
 			}
 
Index: /binary-improvements/7dtd-server-fixes/src/AllocsUtils.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/AllocsUtils.cs	(revision 454)
+++ /binary-improvements/7dtd-server-fixes/src/AllocsUtils.cs	(revision 455)
@@ -4,6 +4,5 @@
 	public static class AllocsUtils {
 		public static string ColorToHex (Color _color) {
-			return string.Format ("{0:X02}{1:X02}{2:X02}", (int) (_color.r * 255), (int) (_color.g * 255),
-				(int) (_color.b * 255));
+			return $"{(int)(_color.r * 255):X02}{(int)(_color.g * 255):X02}{(int)(_color.b * 255):X02}";
 		}
 	}
Index: nary-improvements/7dtd-server-fixes/src/BlockingQueue.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/BlockingQueue.cs	(revision 454)
+++ 	(revision )
@@ -1,37 +1,0 @@
-using System.Collections.Generic;
-using System.Threading;
-
-namespace AllocsFixes {
-	public class BlockingQueue<T> {
-		private readonly Queue<T> queue = new Queue<T> ();
-		private bool closing;
-
-		public void Enqueue (T _item) {
-			lock (queue) {
-				queue.Enqueue (_item);
-				Monitor.PulseAll (queue);
-			}
-		}
-
-		public T Dequeue () {
-			lock (queue) {
-				while (queue.Count == 0) {
-					if (closing) {
-						return default (T);
-					}
-
-					Monitor.Wait (queue);
-				}
-
-				return queue.Dequeue ();
-			}
-		}
-
-		public void Close () {
-			lock (queue) {
-				closing = true;
-				Monitor.PulseAll (queue);
-			}
-		}
-	}
-}
Index: /binary-improvements/7dtd-server-fixes/src/PersistentData/Attributes.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/PersistentData/Attributes.cs	(revision 454)
+++ /binary-improvements/7dtd-server-fixes/src/PersistentData/Attributes.cs	(revision 455)
@@ -8,17 +8,11 @@
 
 		public bool HideChatCommands {
-			get { return hideChatCommands; }
-			set { hideChatCommands = value; }
+			get => hideChatCommands;
+			set => hideChatCommands = value;
 		}
 
 		public string HideChatCommandPrefix {
-			get {
-				if (hideChatCommandPrefix == null) {
-					hideChatCommandPrefix = "";
-				}
-
-				return hideChatCommandPrefix;
-			}
-			set { hideChatCommandPrefix = value; }
+			get => hideChatCommandPrefix ??= "";
+			set => hideChatCommandPrefix = value;
 		}
 	}
Index: /binary-improvements/7dtd-server-fixes/src/PersistentData/Inventory.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/PersistentData/Inventory.cs	(revision 454)
+++ /binary-improvements/7dtd-server-fixes/src/PersistentData/Inventory.cs	(revision 455)
@@ -24,7 +24,7 @@
 		}
 
-		private void ProcessInv (List<InvItem> _target, ItemStack[] _sourceFields, int _id) {
+		private void ProcessInv (ICollection<InvItem> _target, IReadOnlyList<ItemStack> _sourceFields, int _id) {
 			_target.Clear ();
-			for (int i = 0; i < _sourceFields.Length; i++) {
+			for (int i = 0; i < _sourceFields.Count; i++) {
 				InvItem item = CreateInvItem (_sourceFields [i].itemValue, _sourceFields [i].count, _id);
 				if (item != null && _sourceFields [i].itemValue.Modifications != null) {
@@ -43,7 +43,7 @@
 		}
 
-		private void ProcessParts (ItemValue[] _parts, InvItem _item, int _playerId) {
-			InvItem[] itemParts = new InvItem[_parts.Length];
-			for (int i = 0; i < _parts.Length; i++) {
+		private void ProcessParts (IReadOnlyList<ItemValue> _parts, InvItem _item, int _playerId) {
+			InvItem[] itemParts = new InvItem[_parts.Count];
+			for (int i = 0; i < _parts.Count; i++) {
 				InvItem partItem = CreateInvItem (_parts [i], 1, _playerId);
 				if (partItem != null && _parts [i].Modifications != null) {
@@ -67,6 +67,5 @@
 
 			if (_count > maxAllowed) {
-				Log.Out ("Player with ID " + _playerId + " has stack for \"" + name + "\" greater than allowed (" +
-				         _count + " > " + maxAllowed + ")");
+				Log.Out ($"Player with ID {_playerId} has stack for \"{name}\" greater than allowed ({_count} > {maxAllowed})");
 			}
 
Index: /binary-improvements/7dtd-server-fixes/src/PersistentData/PersistentContainer.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/PersistentData/PersistentContainer.cs	(revision 454)
+++ /binary-improvements/7dtd-server-fixes/src/PersistentData/PersistentContainer.cs	(revision 455)
@@ -12,35 +12,11 @@
 		[OptionalField] private Attributes attributes;
 
-		public Players Players {
-			get {
-				if (players == null) {
-					players = new Players ();
-				}
+		public Players Players => players ??= new Players ();
 
-				return players;
-			}
-		}
-
-		public Attributes Attributes {
-			get {
-				if (attributes == null) {
-					attributes = new Attributes ();
-				}
-
-				return attributes;
-			}
-		}
+		public Attributes Attributes => attributes ??= new Attributes ();
 
 		private static PersistentContainer instance;
 
-		public static PersistentContainer Instance {
-			get {
-				if (instance == null) {
-					instance = new PersistentContainer ();
-				}
-
-				return instance;
-			}
-		}
+		public static PersistentContainer Instance => instance ??= new PersistentContainer ();
 
 		private PersistentContainer () {
@@ -49,5 +25,5 @@
 		public void Save () {
 			lock (fileLockObject) {
-				using Stream stream = File.Open (GameIO.GetSaveGameDir () + persistentDataFileName, FileMode.Create);
+				using Stream stream = File.Open ($"{GameIO.GetSaveGameDir ()}{persistentDataFileName}", FileMode.Create);
 				BinaryFormatter bFormatter = new BinaryFormatter ();
 				bFormatter.Serialize (stream, this);
@@ -56,5 +32,7 @@
 
 		public static bool Load () {
-			if (!File.Exists (GameIO.GetSaveGameDir () + persistentDataFileName)) {
+			var filePath = $"{GameIO.GetSaveGameDir ()}{persistentDataFileName}";
+			
+			if (!File.Exists (filePath)) {
 				return false;
 			}
@@ -63,5 +41,5 @@
 				PersistentContainer obj;
 				lock (fileLockObject) {
-					using Stream stream = File.Open (GameIO.GetSaveGameDir () + persistentDataFileName, FileMode.Open);
+					using Stream stream = File.Open (filePath, FileMode.Open);
 					BinaryFormatter bFormatter = new BinaryFormatter ();
 					obj = (PersistentContainer)bFormatter.Deserialize (stream);
Index: /binary-improvements/7dtd-server-fixes/src/PersistentData/Player.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/PersistentData/Player.cs	(revision 454)
+++ /binary-improvements/7dtd-server-fixes/src/PersistentData/Player.cs	(revision 455)
@@ -124,5 +124,5 @@
 			}
 
-			Log.Out ("Player set to offline: " + InternalId);
+			Log.Out ($"Player set to offline: {InternalId}");
 			lastOnline = DateTime.Now;
 			try {
@@ -140,5 +140,5 @@
 
 		public void SetOnline (ClientInfo _ci) {
-			Log.Out ("Player set to online: " + InternalId);
+			Log.Out ($"Player set to online: {InternalId}");
 			clientInfo = _ci;
             entityId = _ci.entityId;
Index: /binary-improvements/7dtd-server-fixes/src/PersistentData/Players.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/PersistentData/Players.cs	(revision 454)
+++ /binary-improvements/7dtd-server-fixes/src/PersistentData/Players.cs	(revision 455)
@@ -19,5 +19,5 @@
 			}
 
-			Log.Out ("Created new player entry for ID: " + _internalId);
+			Log.Out ($"Created new player entry for ID: {_internalId}");
 			Player p = new Player (_internalId, _platformId, _crossPlatformId);
 			Dict.Add (_internalId, p);
Index: nary-improvements/AllocsCommands/API.cs
===================================================================
--- /binary-improvements/AllocsCommands/API.cs	(revision 454)
+++ 	(revision )
@@ -1,6 +1,0 @@
-namespace AllocsFixes.CustomCommands {
-	public class API : IModApi {
-		public void InitMod (Mod _modInstance) {
-		}
-	}
-}
Index: /binary-improvements/AllocsCommands/AllocsCommands.csproj
===================================================================
--- /binary-improvements/AllocsCommands/AllocsCommands.csproj	(revision 454)
+++ /binary-improvements/AllocsCommands/AllocsCommands.csproj	(revision 455)
@@ -60,5 +60,4 @@
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="API.cs" />
     <Compile Include="AssemblyInfo.cs" />
     <Compile Include="Commands\ListKnownPlayers.cs" />
Index: /binary-improvements/AllocsCommands/Commands/ListKnownPlayers.cs
===================================================================
--- /binary-improvements/AllocsCommands/Commands/ListKnownPlayers.cs	(revision 454)
+++ /binary-improvements/AllocsCommands/Commands/ListKnownPlayers.cs	(revision 455)
@@ -1,6 +1,8 @@
 using System.Collections.Generic;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
 
 namespace AllocsFixes.CustomCommands {
+	[UsedImplicitly]
 	public class ListKnownPlayers : ConsoleCmdAbstract {
 		protected override string getDescription () {
Index: /binary-improvements/AllocsCommands/Commands/ListLandProtection.cs
===================================================================
--- /binary-improvements/AllocsCommands/Commands/ListLandProtection.cs	(revision 454)
+++ /binary-improvements/AllocsCommands/Commands/ListLandProtection.cs	(revision 455)
@@ -2,6 +2,8 @@
 using System.Collections.Generic;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
 
 namespace AllocsFixes.CustomCommands {
+	[UsedImplicitly]
 	public class ListLandProtection : ConsoleCmdAbstract {
 		protected override string getDescription () {
@@ -57,5 +59,5 @@
 						userIdFilter = ci.InternalId;
 					} else {
-						SdtdConsole.Instance.Output ("Player name or entity id \"" + _params [0] + "\" not found.");
+						SdtdConsole.Instance.Output ($"Player name or entity id \"{_params[0]}\" not found.");
 						return;
 					}
@@ -79,5 +81,5 @@
 					} catch (Exception e) {
 						SdtdConsole.Instance.Output ("Error getting current player's position");
-						Log.Out ("Error in ListLandProtection.Run: " + e);
+						Log.Out ($"Error in ListLandProtection.Run: {e}");
 						return;
 					}
@@ -102,19 +104,15 @@
 
 			foreach (KeyValuePair<Player, List<Vector3i>> kvp in claims) {
-				SdtdConsole.Instance.Output (string.Format (
-					"Player \"{0} ({1})\" owns {4} keystones (protected: {2}, current hardness multiplier: {3})",
-					kvp.Key.Name,
-					kvp.Key.InternalId,
-					kvp.Key.LandProtectionActive,
-					kvp.Key.LandProtectionMultiplier,
-					kvp.Value.Count));
-				if (!summaryOnly) {
-					foreach (Vector3i v in kvp.Value) {
-						if (parseableOutput) {
-							SdtdConsole.Instance.Output ("LandProtectionOf: id=" + kvp.Key.InternalId +
-							                             ", playerName=" + kvp.Key.Name + ", location=" + v);
-						} else {
-							SdtdConsole.Instance.Output ("   (" + v + ")");
-						}
+				SdtdConsole.Instance.Output (
+					$"Player \"{kvp.Key.Name} ({kvp.Key.InternalId})\" owns {kvp.Value.Count} keystones (protected: {kvp.Key.LandProtectionActive}, current hardness multiplier: {kvp.Key.LandProtectionMultiplier})");
+				if (summaryOnly) {
+					continue;
+				}
+
+				foreach (Vector3i v in kvp.Value) {
+					if (parseableOutput) {
+						SdtdConsole.Instance.Output ($"LandProtectionOf: id={kvp.Key.InternalId}, playerName={kvp.Key.Name}, location={v}");
+					} else {
+						SdtdConsole.Instance.Output ($"   ({v})");
 					}
 				}
@@ -122,5 +120,5 @@
 
 			if (userIdFilter == null) {
-				SdtdConsole.Instance.Output ("Total of " + ppl.m_lpBlockMap.Count + " keystones in the game");
+				SdtdConsole.Instance.Output ($"Total of {ppl.m_lpBlockMap.Count} keystones in the game");
 			}
 		}
Index: /binary-improvements/AllocsCommands/Commands/RemoveLandProtection.cs
===================================================================
--- /binary-improvements/AllocsCommands/Commands/RemoveLandProtection.cs	(revision 454)
+++ /binary-improvements/AllocsCommands/Commands/RemoveLandProtection.cs	(revision 455)
@@ -2,6 +2,8 @@
 using System.Collections.Generic;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
 
 namespace AllocsFixes.CustomCommands {
+	[UsedImplicitly]
 	public class RemoveLandProtection : ConsoleCmdAbstract {
 		protected override string getDescription () {
@@ -47,15 +49,13 @@
 				GameManager.Instance.SetBlocksRPC (changes);
 
-				SdtdConsole.Instance.Output ("Tried to remove #" + changes.Count +
-				                             " land protection blocks for player \"" + _id + "\". Note " +
-				                             "that only blocks in chunks that are currently loaded (close to any player) could be removed. " +
-				                             "Please check for remaining blocks by running:");
-				SdtdConsole.Instance.Output ("  listlandprotection " + _id);
+				SdtdConsole.Instance.Output (
+					$"Tried to remove #{changes.Count} land protection blocks for player \"{_id}\". Note that only blocks in chunks that are currently loaded (close to any player) could be removed. Please check for remaining blocks by running:");
+				SdtdConsole.Instance.Output ($"  listlandprotection {_id}");
 			} catch (Exception e) {
-				Log.Out ("Error in RemoveLandProtection.removeById: " + e);
+				Log.Out ($"Error in RemoveLandProtection.removeById: {e}");
 			}
 		}
 
-		private void removeByPosition (List<string> _coords) {
+		private void removeByPosition (IReadOnlyList<string> _coords) {
 			int.TryParse (_coords [0], out int x);
 			int.TryParse (_coords [1], out int y);
@@ -84,5 +84,5 @@
 			GameManager.Instance.SetBlocksRPC (changes);
 
-			SdtdConsole.Instance.Output ("Land protection block at (" + v + ") removed");
+			SdtdConsole.Instance.Output ($"Land protection block at ({v}) removed");
 		}
 
@@ -125,9 +125,9 @@
 					} catch (Exception e) {
 						SdtdConsole.Instance.Output ("Error removing claims");
-						Log.Out ("Error in RemoveLandProtection.Run: " + e);
+						Log.Out ($"Error in RemoveLandProtection.Run: {e}");
 					}
 				} catch (Exception e) {
 					SdtdConsole.Instance.Output ("Error getting current player's position");
-					Log.Out ("Error in RemoveLandProtection.Run: " + e);
+					Log.Out ($"Error in RemoveLandProtection.Run: {e}");
 				}
 			} else if (_params.Count == 1) {
Index: /binary-improvements/AllocsCommands/Commands/ShowInventory.cs
===================================================================
--- /binary-improvements/AllocsCommands/Commands/ShowInventory.cs	(revision 454)
+++ /binary-improvements/AllocsCommands/Commands/ShowInventory.cs	(revision 455)
@@ -1,6 +1,8 @@
 using System.Collections.Generic;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
 
 namespace AllocsFixes.CustomCommands {
+	[UsedImplicitly]
 	public class ShowInventory : ConsoleCmdAbstract {
 		protected override string getDescription () {
@@ -44,5 +46,5 @@
 
 			if (tag == null) {
-				SdtdConsole.Instance.Output ("Belt of player " + p.Name + ":");
+				SdtdConsole.Instance.Output ($"Belt of player {p.Name}:");
 			}
 
@@ -53,5 +55,5 @@
 
 			if (tag == null) {
-				SdtdConsole.Instance.Output ("Bagpack of player " + p.Name + ":");
+				SdtdConsole.Instance.Output ($"Bagpack of player {p.Name}:");
 			}
 
@@ -62,5 +64,5 @@
 
 			if (tag == null) {
-				SdtdConsole.Instance.Output ("Equipment of player " + p.Name + ":");
+				SdtdConsole.Instance.Output ($"Equipment of player {p.Name}:");
 			}
 
@@ -68,36 +70,37 @@
 
 			if (tag != null) {
-				SdtdConsole.Instance.Output ("tracker_item id=" + p.EntityID + ", tag=" + tag +
-				                             ", SHOWINVENTORY DONE");
+				SdtdConsole.Instance.Output ($"tracker_item id={p.EntityID}, tag={tag}, SHOWINVENTORY DONE");
 			}
 		}
 
-		private void PrintInv (List<InvItem> _inv, int _entityId, string _location, string _tag) {
+		private static void PrintInv (IReadOnlyList<InvItem> _inv, int _entityId, string _location, string _tag) {
 			for (int i = 0; i < _inv.Count; i++) {
-				if (_inv [i] != null) {
-					if (_tag == null) {
-						// no Tag defined -> readable output
-						if (_inv [i].quality < 0) {
-							SdtdConsole.Instance.Output (string.Format ("    Slot {0}: {1:000} * {2}", i,
-								_inv [i].count, _inv [i].itemName));
-						} else {
-							SdtdConsole.Instance.Output (string.Format ("    Slot {0}: {1:000} * {2} - quality: {3}", i,
-								_inv [i].count, _inv [i].itemName, _inv [i].quality));
-						}
+				if (_inv[i] == null) {
+					continue;
+				}
 
-						DoParts (_inv [i].parts, 1, null);
+				if (_tag == null) {
+					// no Tag defined -> readable output
+					if (_inv [i].quality < 0) {
+						SdtdConsole.Instance.Output (string.Format ("    Slot {0}: {1:000} * {2}", i,
+							_inv [i].count, _inv [i].itemName));
 					} else {
-						// Tag defined -> parseable output
-						string partsMsg = DoParts (_inv [i].parts, 1, "");
-						string msg = "tracker_item id=" + _entityId + ", tag=" + _tag + ", location=" + _location +
-						             ", slot=" + i + ", item=" + _inv [i].itemName + ", qnty=" + _inv [i].count +
-						             ", quality=" + _inv [i].quality + ", parts=(" + partsMsg + ")";
-						SdtdConsole.Instance.Output (msg);
+						SdtdConsole.Instance.Output (string.Format ("    Slot {0}: {1:000} * {2} - quality: {3}", i,
+							_inv [i].count, _inv [i].itemName, _inv [i].quality));
 					}
+
+					DoParts (_inv [i].parts, 1, null);
+				} else {
+					// Tag defined -> parseable output
+					string partsMsg = DoParts (_inv [i].parts, 1, "");
+					string msg = "tracker_item id=" + _entityId + ", tag=" + _tag + ", location=" + _location +
+					             ", slot=" + i + ", item=" + _inv [i].itemName + ", qnty=" + _inv [i].count +
+					             ", quality=" + _inv [i].quality + ", parts=(" + partsMsg + ")";
+					SdtdConsole.Instance.Output (msg);
 				}
 			}
 		}
 
-		private void PrintEquipment (InvItem[] _equipment, int _entityId, string _location, string _tag) {
+		private static void PrintEquipment (IReadOnlyList<InvItem> _equipment, int _entityId, string _location, string _tag) {
 			AddEquipment ("head", _equipment, EquipmentSlots.Headgear, _entityId, _location, _tag);
 			AddEquipment ("eyes", _equipment, EquipmentSlots.Eyewear, _entityId, _location, _tag);
@@ -115,62 +118,66 @@
 		}
 
-		private void AddEquipment (string _slotname, InvItem[] _items, EquipmentSlots _slot, int _entityId,
+		private static void AddEquipment (string _slotname, IReadOnlyList<InvItem> _items, EquipmentSlots _slot, int _entityId,
 			string _location, string _tag) {
 			int[] slotindices = XUiM_PlayerEquipment.GetSlotIndicesByEquipmentSlot (_slot);
 
 			for (int i = 0; i < slotindices.Length; i++) {
-				if (_items != null && _items [slotindices [i]] != null) {
-					InvItem item = _items [slotindices [i]];
-					if (_tag == null) {
-						// no Tag defined -> readable output
-						if (item.quality < 0) {
-							SdtdConsole.Instance.Output (string.Format ("    Slot {0:8}: {1:000}", _slotname,
-								item.itemName));
-						} else {
-							SdtdConsole.Instance.Output (string.Format ("    Slot {0:8}: {1:000} - quality: {2}",
-								_slotname, item.itemName, item.quality));
-						}
+				if (_items == null || _items[slotindices[i]] == null) {
+					continue;
+				}
 
-						DoParts (_items [slotindices [i]].parts, 1, null);
+				InvItem item = _items [slotindices [i]];
+				if (_tag == null) {
+					// no Tag defined -> readable output
+					if (item.quality < 0) {
+						SdtdConsole.Instance.Output (string.Format ("    Slot {0:8}: {1:000}", _slotname,
+							item.itemName));
 					} else {
-						// Tag defined -> parseable output
-						string partsMsg = DoParts (_items [slotindices [i]].parts, 1, "");
-						string msg = "tracker_item id=" + _entityId + ", tag=" + _tag + ", location=" + _location +
-						             ", slot=" + _slotname + ", item=" + item.itemName + ", qnty=1, quality=" +
-						             item.quality + ", parts=(" + partsMsg + ")";
-						SdtdConsole.Instance.Output (msg);
+						SdtdConsole.Instance.Output (string.Format ("    Slot {0:8}: {1:000} - quality: {2}",
+							_slotname, item.itemName, item.quality));
 					}
 
-					return;
+					DoParts (_items [slotindices [i]].parts, 1, null);
+				} else {
+					// Tag defined -> parseable output
+					string partsMsg = DoParts (_items [slotindices [i]].parts, 1, "");
+					string msg = $"tracker_item id={_entityId}, tag={_tag}, location={_location}, slot={_slotname}, item={item.itemName}, qnty=1, quality={item.quality}, parts=({partsMsg})";
+					SdtdConsole.Instance.Output (msg);
 				}
+
+				return;
 			}
 		}
 
-		private string DoParts (InvItem[] _parts, int _indent, string _currentMessage) {
-			if (_parts != null && _parts.Length > 0) {
-				string indenter = new string (' ', _indent * 4);
-				for (int i = 0; i < _parts.Length; i++) {
-					if (_parts [i] != null) {
-						if (_currentMessage == null) {
-							// no currentMessage given -> readable output
-							if (_parts [i].quality < 0) {
-								SdtdConsole.Instance.Output (string.Format ("{0}         - {1}", indenter,
-									_parts [i].itemName));
-							} else {
-								SdtdConsole.Instance.Output (string.Format ("{0}         - {1} - quality: {2}",
-									indenter, _parts [i].itemName, _parts [i].quality));
-							}
+		private static string DoParts (IReadOnlyList<InvItem> _parts, int _indent, string _currentMessage) {
+			if (_parts == null || _parts.Count <= 0) {
+				return _currentMessage;
+			}
 
-							DoParts (_parts [i].parts, _indent + 1, null);
-						} else {
-							// currentMessage given -> parseable output
-							if (_currentMessage.Length > 0) {
-								_currentMessage += ",";
-							}
+			string indenter = new string (' ', _indent * 4);
+			for (int i = 0; i < _parts.Count; i++) {
+				if (_parts[i] == null) {
+					continue;
+				}
 
-							_currentMessage += _parts [i].itemName + "@" + _parts [i].quality;
-							_currentMessage = DoParts (_parts [i].parts, _indent + 1, _currentMessage);
-						}
+				if (_currentMessage == null) {
+					// no currentMessage given -> readable output
+					if (_parts [i].quality < 0) {
+						SdtdConsole.Instance.Output (string.Format ("{0}         - {1}", indenter,
+							_parts [i].itemName));
+					} else {
+						SdtdConsole.Instance.Output (string.Format ("{0}         - {1} - quality: {2}",
+							indenter, _parts [i].itemName, _parts [i].quality));
 					}
+
+					DoParts (_parts [i].parts, _indent + 1, null);
+				} else {
+					// currentMessage given -> parseable output
+					if (_currentMessage.Length > 0) {
+						_currentMessage += ",";
+					}
+
+					_currentMessage += $"{_parts[i].itemName}@{_parts[i].quality}";
+					_currentMessage = DoParts (_parts [i].parts, _indent + 1, _currentMessage);
 				}
 			}
Index: /binary-improvements/AllocsCommands/ModInfo.xml
===================================================================
--- /binary-improvements/AllocsCommands/ModInfo.xml	(revision 454)
+++ /binary-improvements/AllocsCommands/ModInfo.xml	(revision 455)
@@ -5,6 +5,6 @@
 		<Description value="Additional commands for server operation" />
 		<Author value="Christian 'Alloc' Illy" />
-		<Version value="24" />
-		<Website value="http://7dtd.illy.bz" />
+		<Version value="25" />
+		<Website value="https://7dtd.illy.bz" />
 	</ModInfo>
 </xml>
Index: /binary-improvements/MapRendering/API.cs
===================================================================
--- /binary-improvements/MapRendering/API.cs	(revision 454)
+++ /binary-improvements/MapRendering/API.cs	(revision 455)
@@ -1,49 +1,42 @@
 using System;
 using System.IO;
-using Webserver;
+using JetBrains.Annotations;
 using Webserver.FileCache;
 using Webserver.UrlHandlers;
 
-namespace AllocsFixes {
+namespace AllocsFixes.Web {
+	[UsedImplicitly]
 	public class API : IModApi {
-		private Mod modInstance;
+		private static Mod modInstance;
 		
 		public void InitMod (Mod _modInstance) {
 			modInstance = _modInstance;
 
-			Web.ServerInitialized += _web => {
-				try {
-					const string legacyModUrl = "/legacymap";
-					const string legacyFilesFoldername = "webserver_legacy";
-					string legacyFilePath = $"{modInstance.Path}/{legacyFilesFoldername}";
-				
-					if (!Directory.Exists (legacyFilePath)) {
-						Log.Out ($"Legacy webmod feature not started (folder \"{legacyFilesFoldername}\" not found in Allocs_WebAndMapRendering mod folder)");
-						return;
-					}
-
-					// TODO: Read from config
-					bool useStaticCache = false;
-
-					_web.RegisterPathHandler ("/legacymap.htm", new SimpleRedirectHandler ($"{legacyModUrl}/index.html"));
-					_web.RegisterPathHandler ($"{legacyModUrl}/", new StaticHandler (legacyFilePath, useStaticCache ? (AbstractCache) new SimpleCache () : new DirectAccess (), false));
-
-					int webPort = GamePrefs.GetInt (EnumUtils.Parse<EnumGamePrefs> (nameof (EnumGamePrefs.WebDashboardPort)));
-					Log.Out ($"Started legacy webmod feature on port {webPort}, local adress {legacyModUrl}");
-				} catch (Exception e) {
-					Log.Out ("Error in Web.ctor: " + e);
-				}
-			};
+			Webserver.Web.ServerInitialized += OnWebServerInitialized;
 		}
 
-		// public static void SetResponseTextContent (HttpListenerResponse _context.Response, string _text) {
-		// 	byte[] buf = Encoding.UTF8.GetBytes (_text);
-		// 	_context.Response.ContentLength64 = buf.Length;
-		// 	_context.Response.ContentType = "text/html";
-		// 	_context.Response.ContentEncoding = Encoding.UTF8;
-		// 	_context.Response.OutputStream.Write (buf, 0, buf.Length);
-		// }
+		private static void OnWebServerInitialized (Webserver.Web _web) {
+			try {
+				const string legacyModUrl = "/legacymap";
+				const string legacyFilesFoldername = "webserver_legacy";
+				string legacyFilePath = $"{modInstance.Path}/{legacyFilesFoldername}";
 
-		
+				if (!Directory.Exists (legacyFilePath)) {
+					Log.Out ($"Legacy webmod feature not started (folder \"{legacyFilesFoldername}\" not found in Allocs_WebAndMapRendering mod folder)");
+					return;
+				}
+
+				// TODO: Read from config
+				bool useStaticCache = false;
+
+				_web.RegisterPathHandler ($"{legacyModUrl}", new SimpleRedirectHandler ($"{legacyModUrl}/index.html"));
+				_web.RegisterPathHandler ($"{legacyModUrl}/", new StaticHandler (legacyFilePath, useStaticCache ? new SimpleCache () : new DirectAccess (), false));
+
+				int webPort = GamePrefs.GetInt (EnumUtils.Parse<EnumGamePrefs> (nameof(EnumGamePrefs.WebDashboardPort)));
+				Log.Out ($"Started legacy webmod feature on port {webPort}, local adress {legacyModUrl}");
+			} catch (Exception e) {
+				Log.Out ($"Error in Web.ctor: {e}");
+			}
+		}
 	}
 }
Index: /binary-improvements/MapRendering/API/ExecuteConsoleCommand.cs
===================================================================
--- /binary-improvements/MapRendering/API/ExecuteConsoleCommand.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/ExecuteConsoleCommand.cs	(revision 455)
@@ -1,9 +1,11 @@
 using System;
 using System.Net;
+using JetBrains.Annotations;
 using Webserver;
 using Webserver.WebAPI;
-using WebCommandResult = AllocsFixes.NetConnections.Servers.Web.WebCommandResult;
+using WebCommandResult = AllocsFixes.Web.WebCommandResult;
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class ExecuteConsoleCommand : AbsWebAPI {
 		public override void HandleRequest (RequestContext _context) {
@@ -16,7 +18,7 @@
 				_context.Request.QueryString ["raw"] != null
 					? WebCommandResult.ResultType.Raw
-					: (_context.Request.QueryString ["simple"] != null
+					: _context.Request.QueryString ["simple"] != null
 						? WebCommandResult.ResultType.ResultOnly
-						: WebCommandResult.ResultType.Full);
+						: WebCommandResult.ResultType.Full;
 
 			string commandline = _context.Request.QueryString ["command"];
Index: /binary-improvements/MapRendering/API/GetAllowedCommands.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetAllowedCommands.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetAllowedCommands.cs	(revision 455)
@@ -1,32 +1,63 @@
-using AllocsFixes.JSON;
+using JetBrains.Annotations;
+using Utf8Json;
 using Webserver;
 using Webserver.WebAPI;
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetAllowedCommands : AbsWebAPI {
+		private static readonly byte[] jsonKeyCommands = JsonWriter.GetEncodedPropertyNameWithBeginObject ("commands");
+
+		private static readonly byte[] jsonKeyCommand = JsonWriter.GetEncodedPropertyNameWithBeginObject ("command");
+		private static readonly byte[] jsonKeyDescription = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("description");
+		private static readonly byte[] jsonKeyHelp = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("help");
+
 		public override void HandleRequest (RequestContext _context) {
-			JSONObject result = new JSONObject ();
-			JSONArray entries = new JSONArray ();
+			JsonWriter writer = new JsonWriter ();
+			
+			writer.WriteRaw (jsonKeyCommands);
+			writer.WriteBeginArray ();
+
+			bool first = true;
+
 			foreach (IConsoleCommand cc in SdtdConsole.Instance.GetCommands ()) {
 				int commandPermissionLevel = GameManager.Instance.adminTools.Commands.GetCommandPermissionLevel (cc.GetCommands ());
-				if (_context.PermissionLevel <= commandPermissionLevel) {
-					string cmd = string.Empty;
-					foreach (string s in cc.GetCommands ()) {
-						if (s.Length > cmd.Length) {
-							cmd = s;
-						}
+				if (_context.PermissionLevel > commandPermissionLevel) {
+					continue;
+				}
+
+				string cmd = string.Empty;
+				foreach (string s in cc.GetCommands ()) {
+					if (s.Length > cmd.Length) {
+						cmd = s;
 					}
+				}
 
-					JSONObject cmdObj = new JSONObject ();
-					cmdObj.Add ("command", new JSONString (cmd));
-					cmdObj.Add ("description", new JSONString (cc.GetDescription ()));
-					cmdObj.Add ("help", new JSONString (cc.GetHelp ()));
-					entries.Add (cmdObj);
+				if (!first) {
+					writer.WriteValueSeparator ();
 				}
+
+				first = false;
+				
+				writer.WriteRaw (jsonKeyCommand);
+				writer.WriteString (cmd);
+				
+				writer.WriteRaw (jsonKeyDescription);
+				writer.WriteString (cc.GetDescription ());
+				
+				writer.WriteRaw (jsonKeyHelp);
+				if (cc.GetHelp () == null) {
+					writer.WriteString ("");
+				} else {
+					writer.WriteString (cc.GetHelp ());
+				}
+
+				writer.WriteEndObject ();
 			}
 
-			result.Add ("commands", entries);
-
-			LegacyApiHelper.WriteJSON (_context.Response, result);
+			writer.WriteEndArray ();
+			writer.WriteEndObject ();
+			
+			WebUtils.WriteJsonData (_context.Response, ref writer);
 		}
 
Index: /binary-improvements/MapRendering/API/GetAnimalsLocation.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetAnimalsLocation.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetAnimalsLocation.cs	(revision 455)
@@ -1,40 +1,9 @@
-﻿using System.Collections.Generic;
-using AllocsFixes.JSON;
-using AllocsFixes.LiveData;
-using Webserver;
-using Webserver.WebAPI;
+﻿using AllocsFixes.LiveData;
+using JetBrains.Annotations;
 
 namespace AllocsFixes.WebAPIs {
-	internal class GetAnimalsLocation : AbsWebAPI {
-		private readonly List<EntityAnimal> animals = new List<EntityAnimal> ();
-
-		public override void HandleRequest (RequestContext _context) {
-			JSONArray animalsJsResult = new JSONArray ();
-
-			Animals.Instance.Get (animals);
-			for (int i = 0; i < animals.Count; i++) {
-				EntityAnimal entity = animals [i];
-				Vector3i position = new Vector3i (entity.GetPosition ());
-
-				JSONObject jsonPOS = new JSONObject ();
-				jsonPOS.Add ("x", new JSONNumber (position.x));
-				jsonPOS.Add ("y", new JSONNumber (position.y));
-				jsonPOS.Add ("z", new JSONNumber (position.z));
-
-				JSONObject pJson = new JSONObject ();
-				pJson.Add ("id", new JSONNumber (entity.entityId));
-
-				if (!string.IsNullOrEmpty (entity.EntityName)) {
-					pJson.Add ("name", new JSONString (entity.EntityName));
-				} else {
-					pJson.Add ("name", new JSONString ("animal class #" + entity.entityClass));
-				}
-
-				pJson.Add ("position", jsonPOS);
-
-				animalsJsResult.Add (pJson);
-			}
-
-			LegacyApiHelper.WriteJSON (_context.Response, animalsJsResult);
+	[UsedImplicitly]
+	internal class GetAnimalsLocation : GetEntityListAbs<EntityAnimal> {
+		public GetAnimalsLocation () : base (Animals.Instance.Get) {
 		}
 	}
Index: /binary-improvements/MapRendering/API/GetEntityListAbs.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetEntityListAbs.cs	(revision 455)
+++ /binary-improvements/MapRendering/API/GetEntityListAbs.cs	(revision 455)
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using JetBrains.Annotations;
+using Utf8Json;
+using Webserver;
+using Webserver.WebAPI;
+
+namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
+	internal abstract class GetEntityListAbs<T> : AbsWebAPI where T : EntityAlive {
+		private static readonly byte[] jsonKeyId = JsonWriter.GetEncodedPropertyNameWithBeginObject ("id");
+		private static readonly byte[] jsonKeyName = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("name");
+		private static readonly byte[] jsonKeyPosition = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("position");
+
+		private readonly List<T> entities = new List<T> ();
+		private readonly Action<List<T>> getterMethod;
+		
+
+		protected GetEntityListAbs (Action<List<T>> _getterMethod) {
+			getterMethod = _getterMethod;
+		}
+
+		public override void HandleRequest (RequestContext _context) {
+			JsonWriter writer = new JsonWriter ();
+			writer.WriteBeginArray ();
+
+			getterMethod (entities);
+			for (int i = 0; i < entities.Count; i++) {
+				T entity = entities [i];
+				Vector3i position = new Vector3i (entity.GetPosition ());
+
+				if (i > 0) {
+					writer.WriteValueSeparator ();
+				}
+				
+				writer.WriteRaw (jsonKeyId);
+				writer.WriteInt32 (entity.entityId);
+				
+				writer.WriteRaw (jsonKeyName);
+				if (!string.IsNullOrEmpty (entity.EntityName)) {
+					writer.WriteString (entity.EntityName);
+				} else {
+					writer.WriteString ($"entity class #{entity.entityClass}");
+				}
+				
+				writer.WriteRaw (jsonKeyPosition);
+				JsonCommons.WriteVector3I (ref writer, position);
+				
+				writer.WriteEndObject ();
+			}
+			
+			writer.WriteEndArray ();
+			WebUtils.WriteJsonData (_context.Response, ref writer);
+		}
+	}
+}
Index: /binary-improvements/MapRendering/API/GetHostileLocation.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetHostileLocation.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetHostileLocation.cs	(revision 455)
@@ -1,40 +1,9 @@
-﻿using System.Collections.Generic;
-using AllocsFixes.JSON;
-using AllocsFixes.LiveData;
-using Webserver;
-using Webserver.WebAPI;
+﻿using AllocsFixes.LiveData;
+using JetBrains.Annotations;
 
 namespace AllocsFixes.WebAPIs {
-	internal class GetHostileLocation : AbsWebAPI {
-		private readonly List<EntityEnemy> enemies = new List<EntityEnemy> ();
-
-		public override void HandleRequest (RequestContext _context) {
-			JSONArray hostilesJsResult = new JSONArray ();
-
-			Hostiles.Instance.Get (enemies);
-			for (int i = 0; i < enemies.Count; i++) {
-				EntityEnemy entity = enemies [i];
-				Vector3i position = new Vector3i (entity.GetPosition ());
-
-				JSONObject jsonPOS = new JSONObject ();
-				jsonPOS.Add ("x", new JSONNumber (position.x));
-				jsonPOS.Add ("y", new JSONNumber (position.y));
-				jsonPOS.Add ("z", new JSONNumber (position.z));
-
-				JSONObject pJson = new JSONObject ();
-				pJson.Add ("id", new JSONNumber (entity.entityId));
-
-				if (!string.IsNullOrEmpty (entity.EntityName)) {
-					pJson.Add ("name", new JSONString (entity.EntityName));
-				} else {
-					pJson.Add ("name", new JSONString ("enemy class #" + entity.entityClass));
-				}
-
-				pJson.Add ("position", jsonPOS);
-
-				hostilesJsResult.Add (pJson);
-			}
-
-			LegacyApiHelper.WriteJSON (_context.Response, hostilesJsResult);
+	[UsedImplicitly]
+	internal class GetHostileLocation : GetEntityListAbs<EntityEnemy> {
+		public GetHostileLocation () : base (Hostiles.Instance.Get) {
 		}
 	}
Index: /binary-improvements/MapRendering/API/GetLandClaims.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetLandClaims.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetLandClaims.cs	(revision 455)
@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using System.Net;
-using AllocsFixes.JSON;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
+using Utf8Json;
 using Webserver;
 using Webserver.Permissions;
@@ -8,5 +9,15 @@
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetLandClaims : AbsWebAPI {
+		private static readonly byte[] jsonKeyClaimSize = JsonWriter.GetEncodedPropertyNameWithBeginObject ("claimsize");
+		private static readonly byte[] jsonKeyClaimOwners = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("claimowners");
+
+		private static readonly byte[] jsonKeySteamId = JsonWriter.GetEncodedPropertyNameWithBeginObject ("steamid");
+		private static readonly byte[] jsonKeyCrossPlatformId = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("crossplatformid");
+		private static readonly byte[] jsonKeyClaimActive = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("claimactive");
+		private static readonly byte[] jsonKeyPlayerName = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("playername");
+		private static readonly byte[] jsonKeyClaims = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("claims");
+
 		public override void HandleRequest (RequestContext _context) {
 			PlatformUserIdentifierAbs requestedUserId = null;
@@ -23,10 +34,4 @@
 			bool bViewAll = PermissionUtils.CanViewAllClaims (_context.PermissionLevel);
 
-			JSONObject result = new JSONObject ();
-			result.Add ("claimsize", new JSONNumber (GamePrefs.GetInt (EnumUtils.Parse<EnumGamePrefs> (nameof(EnumGamePrefs.LandClaimSize)))));
-
-			JSONArray claimOwners = new JSONArray ();
-			result.Add ("claimowners", claimOwners);
-
 			LandClaimList.OwnerFilter[] ownerFilters = null;
 			if (requestedUserId != null || !bViewAll) {
@@ -34,5 +39,5 @@
 					ownerFilters = new[] {
 						LandClaimList.UserIdFilter (userId),
-						LandClaimList.UserIdFilter (requestedUserId)
+						LandClaimList.UserIdFilter (requestedUserId),
 					};
 				} else if (!bViewAll) {
@@ -47,32 +52,61 @@
 			Dictionary<Player, List<Vector3i>> claims = LandClaimList.GetLandClaims (ownerFilters, posFilters);
 
+			JsonWriter writer = new JsonWriter ();
+			
+			writer.WriteRaw (jsonKeyClaimSize);
+			writer.WriteInt32 (GamePrefs.GetInt (EnumUtils.Parse<EnumGamePrefs> (nameof(EnumGamePrefs.LandClaimSize))));
+			
+			writer.WriteRaw (jsonKeyClaimOwners);
+			writer.WriteBeginArray ();
+			
+			bool first = true;
+
 			foreach (KeyValuePair<Player, List<Vector3i>> kvp in claims) {
-				JSONObject owner = new JSONObject ();
-				claimOwners.Add (owner);
-
-				owner.Add ("steamid", new JSONString (kvp.Key.PlatformId?.CombinedString ?? ""));
-				owner.Add ("crossplatformid", new JSONString (kvp.Key.CrossPlatformId?.CombinedString ?? ""));
-				owner.Add ("claimactive", new JSONBoolean (kvp.Key.LandProtectionActive));
-
-				if (kvp.Key.Name.Length > 0) {
-					owner.Add ("playername", new JSONString (kvp.Key.Name));
-				} else {
-					owner.Add ("playername", new JSONNull ());
+				if (!first) {
+					writer.WriteValueSeparator ();
 				}
 
-				JSONArray claimsJson = new JSONArray ();
-				owner.Add ("claims", claimsJson);
+				first = false;
+				
+				writer.WriteRaw (jsonKeySteamId);
+				writer.WriteString (kvp.Key.PlatformId?.CombinedString ?? "");
+				
+				writer.WriteRaw (jsonKeyCrossPlatformId);
+				writer.WriteString (kvp.Key.CrossPlatformId?.CombinedString ?? "");
+				
+				writer.WriteRaw (jsonKeyClaimActive);
+				writer.WriteBoolean (kvp.Key.LandProtectionActive);
+				
+				writer.WriteRaw (jsonKeyPlayerName);
+				if (kvp.Key.Name.Length > 0) {
+					writer.WriteString (kvp.Key.Name);
+				} else {
+					writer.WriteNull ();
+				}
+				
+				writer.WriteRaw (jsonKeyClaims);
+				writer.WriteBeginArray ();
+
+				bool first2 = true;
 
 				foreach (Vector3i v in kvp.Value) {
-					JSONObject claim = new JSONObject ();
-					claim.Add ("x", new JSONNumber (v.x));
-					claim.Add ("y", new JSONNumber (v.y));
-					claim.Add ("z", new JSONNumber (v.z));
+					if (!first2) {
+						writer.WriteValueSeparator ();
+					}
 
-					claimsJson.Add (claim);
+					first2 = false;
+					
+					JsonCommons.WriteVector3I (ref writer, v);
 				}
+				
+				writer.WriteEndArray ();
+				
+				writer.WriteEndObject ();
 			}
 
-			LegacyApiHelper.WriteJSON (_context.Response, result);
+			writer.WriteEndArray ();
+			
+			writer.WriteEndObject ();
+			WebUtils.WriteJsonData (_context.Response, ref writer);
 		}
 	}
Index: /binary-improvements/MapRendering/API/GetLog.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetLog.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetLog.cs	(revision 455)
@@ -1,11 +1,24 @@
 using System.Collections.Generic;
-using AllocsFixes.JSON;
+using JetBrains.Annotations;
+using Utf8Json;
 using Webserver;
 using Webserver.WebAPI;
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetLog : AbsWebAPI {
 		private const int MAX_COUNT = 1000;
 		
+		private static readonly byte[] jsonKeyFirstLine = JsonWriter.GetEncodedPropertyNameWithBeginObject ("firstLine");
+		private static readonly byte[] jsonKeyLastLine = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("lastLine");
+		private static readonly byte[] jsonKeyEntries = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("entries");
+
+		private static readonly byte[] jsonKeyEntDate = JsonWriter.GetEncodedPropertyNameWithBeginObject ("date");
+		private static readonly byte[] jsonKeyEntTime = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("time");
+		private static readonly byte[] jsonKeyEntUptime = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("uptime");
+		private static readonly byte[] jsonKeyEntMsg = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("msg");
+		private static readonly byte[] jsonKeyEntTrace = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("trace");
+		private static readonly byte[] jsonKeyEntType = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("type");
+
 		public override void HandleRequest (RequestContext _context) {
 			if (_context.Request.QueryString ["count"] == null || !int.TryParse (_context.Request.QueryString ["count"], out var count)) {
@@ -26,33 +39,56 @@
 
 			if (_context.Request.QueryString ["firstLine"] == null || !int.TryParse (_context.Request.QueryString ["firstLine"], out var firstLine)) {
-				if (count > 0) {
-					firstLine = LogBuffer.Instance.OldestLine;
-				} else {
-					firstLine = LogBuffer.Instance.LatestLine;
-				}
+				firstLine = count > 0 ? LogBuffer.Instance.OldestLine : LogBuffer.Instance.LatestLine;
 			}
-
-			JSONObject result = new JSONObject ();
 
 			List<LogBuffer.LogEntry> logEntries = LogBuffer.Instance.GetRange (ref firstLine, count, out var lastLine);
 
-			JSONArray entries = new JSONArray ();
+			JsonWriter writer = new JsonWriter ();
+			
+			writer.WriteRaw (jsonKeyFirstLine);
+			writer.WriteInt32 (firstLine);
+			
+			writer.WriteRaw (jsonKeyLastLine);
+			writer.WriteInt32 (lastLine);
+			
+			writer.WriteRaw (jsonKeyEntries);
+			writer.WriteBeginArray ();
+			
+			bool first = true;
+
 			foreach (LogBuffer.LogEntry logEntry in logEntries) {
-				JSONObject entry = new JSONObject ();
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+
+				first = false;
+				
 				var logEntryTimestamp = logEntry.Timestamp;
-				entry.Add ("date", new JSONString ($"{logEntryTimestamp.Year:0000}-{logEntryTimestamp.Month:00}-{logEntryTimestamp.Day:00}"));
-				entry.Add ("time", new JSONString ($"{logEntryTimestamp.Hour:00}:{logEntryTimestamp.Minute:00}:{logEntryTimestamp.Second:00}"));
-				entry.Add ("uptime", new JSONString (logEntry.Uptime.ToString()));
-				entry.Add ("msg", new JSONString (logEntry.Message));
-				entry.Add ("trace", new JSONString (logEntry.Trace));
-				entry.Add ("type", new JSONString (logEntry.Type.ToStringCached ()));
-				entries.Add (entry);
+
+				writer.WriteRaw (jsonKeyEntDate);
+				writer.WriteString ($"{logEntryTimestamp.Year:0000}-{logEntryTimestamp.Month:00}-{logEntryTimestamp.Day:00}");
+				
+				writer.WriteRaw (jsonKeyEntTime);
+				writer.WriteString ($"{logEntryTimestamp.Hour:00}:{logEntryTimestamp.Minute:00}:{logEntryTimestamp.Second:00}");
+				
+				writer.WriteRaw (jsonKeyEntUptime);
+				writer.WriteString (logEntry.Uptime.ToString());
+				
+				writer.WriteRaw (jsonKeyEntMsg);
+				writer.WriteString (logEntry.Message);
+				
+				writer.WriteRaw (jsonKeyEntTrace);
+				writer.WriteString (logEntry.Trace);
+				
+				writer.WriteRaw (jsonKeyEntType);
+				writer.WriteString (logEntry.Type.ToStringCached ());
+
+				writer.WriteEndObject ();
 			}
 
-			result.Add ("firstLine", new JSONNumber (firstLine));
-			result.Add ("lastLine", new JSONNumber (lastLine));
-			result.Add ("entries", entries);
-
-			LegacyApiHelper.WriteJSON (_context.Response, result);
+			writer.WriteEndArray ();
+			
+			writer.WriteEndObject ();
+			WebUtils.WriteJsonData (_context.Response, ref writer);
 		}
 	}
Index: /binary-improvements/MapRendering/API/GetPlayerInventories.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetPlayerInventories.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetPlayerInventories.cs	(revision 455)
@@ -1,14 +1,20 @@
 using System.Collections.Generic;
-using AllocsFixes.JSON;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
+using Utf8Json;
 using Webserver;
 using Webserver.WebAPI;
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetPlayerInventories : AbsWebAPI {
 		public override void HandleRequest (RequestContext _context) {
 			GetPlayerInventory.GetInventoryArguments (_context, out bool showIconColor, out bool showIconName);
 
-			JSONArray AllInventoriesResult = new JSONArray ();
+			JsonWriter writer = new JsonWriter ();
+			
+			writer.WriteBeginArray ();
+			
+			bool first = true;
 
 			foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in PersistentContainer.Instance.Players.Dict) {
@@ -20,9 +26,17 @@
 
 				if (p.IsOnline) {
-					AllInventoriesResult.Add (GetPlayerInventory.DoPlayer (p, showIconColor, showIconName));
+					if (!first) {
+						writer.WriteValueSeparator ();
+					}
+
+					first = false;
+				
+					GetPlayerInventory.DoPlayer (ref writer, p, showIconColor, showIconName);
 				}
 			}
-
-			LegacyApiHelper.WriteJSON (_context.Response, AllInventoriesResult);
+			
+			writer.WriteEndArray ();
+			
+			WebUtils.WriteJsonData (_context.Response, ref writer);
 		}
 	}
Index: /binary-improvements/MapRendering/API/GetPlayerInventory.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetPlayerInventory.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetPlayerInventory.cs	(revision 455)
@@ -1,10 +1,12 @@
 using System.Collections.Generic;
 using System.Net;
-using AllocsFixes.JSON;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
+using Utf8Json;
 using Webserver;
 using Webserver.WebAPI;
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetPlayerInventory : AbsWebAPI {
 		public override void HandleRequest (RequestContext _context) {
@@ -28,7 +30,7 @@
 			GetInventoryArguments (_context, out bool showIconColor, out bool showIconName);
 
-			JSONObject result = DoPlayer (p, showIconColor, showIconName);
-
-			LegacyApiHelper.WriteJSON (_context.Response, result);
+			JsonWriter writer = new JsonWriter ();
+			DoPlayer (ref writer, p, showIconColor, showIconName);
+			WebUtils.WriteJsonData (_context.Response, ref writer);
 		}
 
@@ -43,47 +45,82 @@
 		}
 
-		internal static JSONObject DoPlayer (Player _player, bool _showIconColor, bool _showIconName) {
+		private static readonly byte[] jsonKeyUserId = JsonWriter.GetEncodedPropertyNameWithBeginObject ("userid");
+		private static readonly byte[] jsonKeyCrossPlatformId = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("crossplatformid");
+		private static readonly byte[] jsonKeyEntityId = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("entityid");
+		private static readonly byte[] jsonKeyPlayerName = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("playername");
+		private static readonly byte[] jsonKeyBag = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("bag");
+		private static readonly byte[] jsonKeyBelt = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("belt");
+		private static readonly byte[] jsonKeyEquipment = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("equipment");
+
+		internal static void DoPlayer (ref JsonWriter _writer, Player _player, bool _showIconColor, bool _showIconName) {
 			PersistentData.Inventory inv = _player.Inventory;
-
-			JSONObject result = new JSONObject ();
-
-			JSONArray bag = new JSONArray ();
-			JSONArray belt = new JSONArray ();
-			JSONObject equipment = new JSONObject ();
-			result.Add ("userid", new JSONString (_player.PlatformId.CombinedString));
-			result.Add ("crossplatformid", new JSONString (_player.CrossPlatformId?.CombinedString ?? ""));
-			result.Add ("entityid", new JSONNumber (_player.EntityID));
-			result.Add ("playername", new JSONString (_player.Name));
-			result.Add ("bag", bag);
-			result.Add ("belt", belt);
-			result.Add ("equipment", equipment);
-
-			DoInventory (belt, inv.belt, _showIconColor, _showIconName);
-			DoInventory (bag, inv.bag, _showIconColor, _showIconName);
-
-			AddEquipment (equipment, "head", inv.equipment, EquipmentSlots.Headgear, _showIconColor, _showIconName);
-			AddEquipment (equipment, "eyes", inv.equipment, EquipmentSlots.Eyewear, _showIconColor, _showIconName);
-			AddEquipment (equipment, "face", inv.equipment, EquipmentSlots.Face, _showIconColor, _showIconName);
-
-			AddEquipment (equipment, "armor", inv.equipment, EquipmentSlots.ChestArmor, _showIconColor, _showIconName);
-			AddEquipment (equipment, "jacket", inv.equipment, EquipmentSlots.Jacket, _showIconColor, _showIconName);
-			AddEquipment (equipment, "shirt", inv.equipment, EquipmentSlots.Shirt, _showIconColor, _showIconName);
-
-			AddEquipment (equipment, "legarmor", inv.equipment, EquipmentSlots.LegArmor, _showIconColor, _showIconName);
-			AddEquipment (equipment, "pants", inv.equipment, EquipmentSlots.Legs, _showIconColor, _showIconName);
-			AddEquipment (equipment, "boots", inv.equipment, EquipmentSlots.Feet, _showIconColor, _showIconName);
-
-			AddEquipment (equipment, "gloves", inv.equipment, EquipmentSlots.Hands, _showIconColor, _showIconName);
-
-			return result;
+			
+			_writer.WriteRaw (jsonKeyUserId);
+			_writer.WriteString (_player.PlatformId.CombinedString);
+			
+			_writer.WriteRaw (jsonKeyCrossPlatformId);
+			_writer.WriteString (_player.CrossPlatformId?.CombinedString ?? "");
+			
+			_writer.WriteRaw (jsonKeyEntityId);
+			_writer.WriteInt32 (_player.EntityID);
+			
+			_writer.WriteRaw (jsonKeyPlayerName);
+			_writer.WriteString (_player.Name);
+			
+			_writer.WriteRaw (jsonKeyBag);
+			DoInventory (ref _writer, inv.bag, _showIconColor, _showIconName);
+			
+			_writer.WriteRaw (jsonKeyBelt);
+			DoInventory (ref _writer, inv.belt, _showIconColor, _showIconName);
+			
+			_writer.WriteRaw (jsonKeyEquipment);
+			DoEquipment (ref _writer, inv.equipment, _showIconColor, _showIconName);
+			
+			_writer.WriteEndObject ();
 		}
 
-		private static void DoInventory (JSONArray _jsonRes, List<InvItem> _inv, bool _showIconColor, bool _showIconName) {
+		private static void DoInventory (ref JsonWriter _writer, IReadOnlyList<InvItem> _inv, bool _showIconColor, bool _showIconName) {
+			_writer.WriteBeginArray ();
 			for (int i = 0; i < _inv.Count; i++) {
-				_jsonRes.Add (GetJsonForItem (_inv [i], _showIconColor, _showIconName));
+				if (i > 0) {
+					_writer.WriteValueSeparator ();
+				}
+				GetJsonForItem (ref _writer, _inv [i], _showIconColor, _showIconName);
 			}
+			_writer.WriteEndArray ();
 		}
 
-		private static void AddEquipment (JSONObject _eq, string _slotname, InvItem[] _items, EquipmentSlots _slot, bool _showIconColor, bool _showIconName) {
+		private static void DoEquipment (ref JsonWriter _writer, IReadOnlyList<InvItem> _equ, bool _showIconColor, bool _showIconName) {
+			_writer.WriteBeginObject ();
+			
+			AddEquipment (ref _writer, "head", _equ, EquipmentSlots.Headgear, _showIconColor, _showIconName);
+			_writer.WriteValueSeparator ();
+			AddEquipment (ref _writer, "eyes", _equ, EquipmentSlots.Eyewear, _showIconColor, _showIconName);
+			_writer.WriteValueSeparator ();
+			AddEquipment (ref _writer, "face", _equ, EquipmentSlots.Face, _showIconColor, _showIconName);
+			_writer.WriteValueSeparator ();
+
+			AddEquipment (ref _writer, "armor", _equ, EquipmentSlots.ChestArmor, _showIconColor, _showIconName);
+			_writer.WriteValueSeparator ();
+			AddEquipment (ref _writer, "jacket", _equ, EquipmentSlots.Jacket, _showIconColor, _showIconName);
+			_writer.WriteValueSeparator ();
+			AddEquipment (ref _writer, "shirt", _equ, EquipmentSlots.Shirt, _showIconColor, _showIconName);
+			_writer.WriteValueSeparator ();
+
+			AddEquipment (ref _writer, "legarmor", _equ, EquipmentSlots.LegArmor, _showIconColor, _showIconName);
+			_writer.WriteValueSeparator ();
+			AddEquipment (ref _writer, "pants", _equ, EquipmentSlots.Legs, _showIconColor, _showIconName);
+			_writer.WriteValueSeparator ();
+			AddEquipment (ref _writer, "boots", _equ, EquipmentSlots.Feet, _showIconColor, _showIconName);
+			_writer.WriteValueSeparator ();
+
+			AddEquipment (ref _writer, "gloves", _equ, EquipmentSlots.Hands, _showIconColor, _showIconName);
+			
+			_writer.WriteEndObject ();
+		}
+
+		private static void AddEquipment (ref JsonWriter _writer, string _slotname, IReadOnlyList<InvItem> _items, EquipmentSlots _slot, bool _showIconColor, bool _showIconName) {
+			_writer.WritePropertyName (_slotname);
+
 			int[] slotindices = XUiM_PlayerEquipment.GetSlotIndicesByEquipmentSlot (_slot);
 
@@ -91,36 +128,52 @@
 				if (_items? [slotindices [i]] != null) {
 					InvItem item = _items [slotindices [i]];
-					_eq.Add (_slotname, GetJsonForItem (item, _showIconColor, _showIconName));
+					
+					GetJsonForItem (ref _writer, item, _showIconColor, _showIconName);
 					return;
 				}
 			}
 
-			_eq.Add (_slotname, new JSONNull ());
+			// Slot not found / empty
+			_writer.WriteNull ();
 		}
 
-		private static JSONNode GetJsonForItem (InvItem _item, bool _showIconColor, bool _showIconName) {
+		private static readonly byte[] jsonKeyCount = JsonWriter.GetEncodedPropertyNameWithBeginObject ("count");
+		private static readonly byte[] jsonKeyName = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("name");
+		private static readonly byte[] jsonKeyIcon = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("icon");
+		private static readonly byte[] jsonKeyIconColor = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("iconcolor");
+		private static readonly byte[] jsonKeyQuality = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("quality");
+		private static readonly byte[] jsonKeyQualityColor = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("qualitycolor");
+
+		private static void GetJsonForItem (ref JsonWriter _writer, InvItem _item, bool _showIconColor, bool _showIconName) {
 			if (_item == null) {
-				return new JSONNull ();
+				_writer.WriteNull ();
+				return;
 			}
-
-			JSONObject jsonItem = new JSONObject ();
-			jsonItem.Add ("count", new JSONNumber (_item.count));
-			jsonItem.Add ("name", new JSONString (_item.itemName));
+			
+			_writer.WriteRaw (jsonKeyCount);
+			_writer.WriteInt32 (_item.count);
+			
+			_writer.WriteRaw (jsonKeyName);
+			_writer.WriteString (_item.itemName);
 			
 			if (_showIconName) {
-				jsonItem.Add ("icon", new JSONString (_item.icon));
+				_writer.WriteRaw (jsonKeyIcon);
+				_writer.WriteString (_item.icon);
 			}
 
 			if (_showIconColor) {
-				jsonItem.Add ("iconcolor", new JSONString (_item.iconcolor));
+				_writer.WriteRaw (jsonKeyIconColor);
+				_writer.WriteString (_item.iconcolor);
 			}
 
-			jsonItem.Add ("quality", new JSONNumber (_item.quality));
+			_writer.WriteRaw (jsonKeyQuality);
+			_writer.WriteInt32 (_item.quality);
+
 			if (_item.quality >= 0) {
-				jsonItem.Add ("qualitycolor", new JSONString (QualityInfo.GetQualityColorHex (_item.quality)));
+				_writer.WriteRaw (jsonKeyQualityColor);
+				_writer.WriteString (QualityInfo.GetQualityColorHex (_item.quality));
 			}
-
-			return jsonItem;
-
+			
+			_writer.WriteEndObject ();
 		}
 	}
Index: /binary-improvements/MapRendering/API/GetPlayerList.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetPlayerList.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetPlayerList.cs	(revision 455)
@@ -2,7 +2,8 @@
 using System.Collections.Generic;
 using System.Linq;
+using System.Text;
 using System.Text.RegularExpressions;
-using AllocsFixes.JSON;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
 using Webserver;
 using Webserver.Permissions;
@@ -10,4 +11,5 @@
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetPlayerList : AbsWebAPI {
 		private static readonly Regex numberFilterMatcher =
@@ -121,8 +123,10 @@
 			result.Add ("players", playersJsResult);
 
-			LegacyApiHelper.WriteJSON (_context.Response, result);
-		}
-
-		private IEnumerable<JSONObject> ExecuteFilter (IEnumerable<JSONObject> _list, string _filterCol,
+			StringBuilder sb = new StringBuilder ();
+			result.ToString (sb);
+			WebUtils.WriteText (_context.Response, sb.ToString(), _mimeType: WebUtils.MimeJson);
+		}
+
+		private static IEnumerable<JSONObject> ExecuteFilter (IEnumerable<JSONObject> _list, string _filterCol,
 			string _filterVal) {
 			if (!_list.Any()) {
@@ -144,5 +148,5 @@
 					// regex-match whole ^string$, replace * by .*, ? by .?, + by .+
 					_filterVal = _filterVal.Replace ("*", ".*").Replace ("?", ".?").Replace ("+", ".+");
-					_filterVal = "^" + _filterVal + "$";
+					_filterVal = $"^{_filterVal}$";
 
 					//Log.Out ("GetPlayerList: Filter on String with Regex '" + _filterVal + "'");
@@ -156,5 +160,5 @@
 
 
-		private IEnumerable<JSONObject> ExecuteNumberFilter (IEnumerable<JSONObject> _list, string _filterCol,
+		private static IEnumerable<JSONObject> ExecuteNumberFilter (IEnumerable<JSONObject> _list, string _filterCol,
 			string _filterVal) {
 			// allow value (exact match), =, ==, >=, >, <=, <
@@ -207,10 +211,10 @@
 			}
 
-			Log.Out ("GetPlayerList: ignoring invalid filter for number-column '{0}': '{1}'", _filterCol, _filterVal);
+			Log.Out ($"GetPlayerList: ignoring invalid filter for number-column '{_filterCol}': '{_filterVal}'");
 			return _list;
 		}
 
 
-		private IEnumerable<JSONObject> ExecuteSort (IEnumerable<JSONObject> _list, string _sortCol, bool _ascending) {
+		private static IEnumerable<JSONObject> ExecuteSort (IEnumerable<JSONObject> _list, string _sortCol, bool _ascending) {
 			if (_list.Count () == 0) {
 				return _list;
@@ -246,5 +250,5 @@
 
 
-		private bool NearlyEqual (double _a, double _b, double _epsilon) {
+		private static bool NearlyEqual (double _a, double _b, double _epsilon) {
 			double absA = Math.Abs (_a);
 			double absB = Math.Abs (_b);
@@ -267,6 +271,214 @@
 			GreaterEqual,
 			Lesser,
-			LesserEqual
-		}
+			LesserEqual,
+		}
+
+
+#region JSON encoder
+
+		private abstract class JSONNode {
+			public abstract void ToString (StringBuilder _stringBuilder, bool _prettyPrint = false, int _currentLevel = 0);
+
+			public override string ToString () {
+				StringBuilder sb = new StringBuilder ();
+				ToString (sb);
+				return sb.ToString ();
+			}
+		}
+
+		private abstract class JSONValue : JSONNode {
+		}
+
+		private class JSONNull : JSONValue {
+			public override void ToString (StringBuilder _stringBuilder, bool _prettyPrint = false, int _currentLevel = 0) {
+				_stringBuilder.Append ("null");
+			}
+		}
+
+		private class JSONBoolean : JSONValue {
+			private readonly bool value;
+
+			public JSONBoolean (bool _value) {
+				value = _value;
+			}
+
+			public bool GetBool () {
+				return value;
+			}
+
+			public override void ToString (StringBuilder _stringBuilder, bool _prettyPrint = false, int _currentLevel = 0) {
+				_stringBuilder.Append (value ? "true" : "false");
+			}
+		}
+
+		private class JSONNumber : JSONValue {
+			private readonly double value;
+
+			public JSONNumber (double _value) {
+				value = _value;
+			}
+
+			public double GetDouble () {
+				return value;
+			}
+
+			public override void ToString (StringBuilder _stringBuilder, bool _prettyPrint = false, int _currentLevel = 0) {
+				_stringBuilder.Append (value.ToCultureInvariantString ());
+			}
+		}
+
+		private class JSONString : JSONValue {
+			private readonly string value;
+
+			public JSONString (string _value) {
+				value = _value;
+			}
+
+			public string GetString () {
+				return value;
+			}
+
+			public override void ToString (StringBuilder _stringBuilder, bool _prettyPrint = false, int _currentLevel = 0) {
+				if (string.IsNullOrEmpty (value)) {
+					_stringBuilder.Append ("\"\"");
+					return;
+				}
+
+				int len = value.Length;
+
+				_stringBuilder.EnsureCapacity (_stringBuilder.Length + 2 * len);
+
+				_stringBuilder.Append ('"');
+
+				foreach (char c in value) {
+					switch (c) {
+						case '\\':
+						case '"':
+
+//					case '/':
+							_stringBuilder.Append ('\\');
+							_stringBuilder.Append (c);
+							break;
+						case '\b':
+							_stringBuilder.Append ("\\b");
+							break;
+						case '\t':
+							_stringBuilder.Append ("\\t");
+							break;
+						case '\n':
+							_stringBuilder.Append ("\\n");
+							break;
+						case '\f':
+							_stringBuilder.Append ("\\f");
+							break;
+						case '\r':
+							_stringBuilder.Append ("\\r");
+							break;
+						default:
+							if (c < ' ') {
+								_stringBuilder.Append ("\\u");
+								_stringBuilder.Append (((int) c).ToString ("X4"));
+							} else {
+								_stringBuilder.Append (c);
+							}
+
+							break;
+					}
+				}
+
+				_stringBuilder.Append ('"');
+			}
+
+		}
+
+		private class JSONArray : JSONNode {
+			private readonly List<JSONNode> nodes = new List<JSONNode> ();
+
+			public void Add (JSONNode _node) {
+				nodes.Add (_node);
+			}
+
+			public override void ToString (StringBuilder _stringBuilder, bool _prettyPrint = false, int _currentLevel = 0) {
+				_stringBuilder.Append ("[");
+				if (_prettyPrint) {
+					_stringBuilder.Append ('\n');
+				}
+
+				foreach (JSONNode n in nodes) {
+					if (_prettyPrint) {
+						_stringBuilder.Append (new string ('\t', _currentLevel + 1));
+					}
+
+					n.ToString (_stringBuilder, _prettyPrint, _currentLevel + 1);
+					_stringBuilder.Append (",");
+					if (_prettyPrint) {
+						_stringBuilder.Append ('\n');
+					}
+				}
+
+				if (nodes.Count > 0) {
+					_stringBuilder.Remove (_stringBuilder.Length - (_prettyPrint ? 2 : 1), 1);
+				}
+
+				if (_prettyPrint) {
+					_stringBuilder.Append (new string ('\t', _currentLevel));
+				}
+
+				_stringBuilder.Append ("]");
+			}
+
+		}
+
+		private class JSONObject : JSONNode {
+			private readonly Dictionary<string, JSONNode> nodes = new Dictionary<string, JSONNode> ();
+
+			public JSONNode this [string _name] => nodes [_name];
+
+			public bool ContainsKey (string _name) {
+				return nodes.ContainsKey (_name);
+			}
+
+			public void Add (string _name, JSONNode _node) {
+				nodes.Add (_name, _node);
+			}
+
+			public override void ToString (StringBuilder _stringBuilder, bool _prettyPrint = false, int _currentLevel = 0) {
+				_stringBuilder.Append ("{");
+				if (_prettyPrint) {
+					_stringBuilder.Append ('\n');
+				}
+
+				foreach (KeyValuePair<string, JSONNode> kvp in nodes) {
+					if (_prettyPrint) {
+						_stringBuilder.Append (new string ('\t', _currentLevel + 1));
+					}
+
+					_stringBuilder.Append ($"\"{kvp.Key}\":");
+					if (_prettyPrint) {
+						_stringBuilder.Append (" ");
+					}
+
+					kvp.Value.ToString (_stringBuilder, _prettyPrint, _currentLevel + 1);
+					_stringBuilder.Append (",");
+					if (_prettyPrint) {
+						_stringBuilder.Append ('\n');
+					}
+				}
+
+				if (nodes.Count > 0) {
+					_stringBuilder.Remove (_stringBuilder.Length - (_prettyPrint ? 2 : 1), 1);
+				}
+
+				if (_prettyPrint) {
+					_stringBuilder.Append (new string ('\t', _currentLevel));
+				}
+
+				_stringBuilder.Append ("}");
+			}
+
+		}
+		
+#endregion
+
 	}
 }
Index: /binary-improvements/MapRendering/API/GetPlayersLocation.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetPlayersLocation.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetPlayersLocation.cs	(revision 455)
@@ -1,5 +1,6 @@
 using System.Collections.Generic;
-using AllocsFixes.JSON;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
+using Utf8Json;
 using Webserver;
 using Webserver.Permissions;
@@ -7,5 +8,12 @@
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetPlayersLocation : AbsWebAPI {
+		private static readonly byte[] jsonKeySteamId = JsonWriter.GetEncodedPropertyNameWithBeginObject ("steamid");
+		private static readonly byte[] jsonKeyCrossPlatformId = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("crossplatformid");
+		private static readonly byte[] jsonKeyName = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("name");
+		private static readonly byte[] jsonKeyOnline = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("online");
+		private static readonly byte[] jsonKeyPosition = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("position");
+
 		public override void HandleRequest (RequestContext _context) {
 			AdminTools admTools = GameManager.Instance.adminTools;
@@ -19,5 +27,9 @@
 			bool bViewAll = PermissionUtils.CanViewAllPlayers (_context.PermissionLevel);
 
-			JSONArray playersJsResult = new JSONArray ();
+			JsonWriter writer = new JsonWriter ();
+			
+			writer.WriteBeginArray ();
+			
+			bool first = true;
 
 			Players playersList = PersistentContainer.Instance.Players;
@@ -34,29 +46,34 @@
 				if (listOffline || p.IsOnline) {
 					if (bViewAll || p.InternalId.Equals (userId)) {
-						JSONObject pos = new JSONObject ();
-						pos.Add ("x", new JSONNumber (p.LastPosition.x));
-						pos.Add ("y", new JSONNumber (p.LastPosition.y));
-						pos.Add ("z", new JSONNumber (p.LastPosition.z));
 
-						JSONObject pJson = new JSONObject ();
-						pJson.Add ("steamid", new JSONString (kvp.Value.PlatformId.CombinedString));
-						pJson.Add ("crossplatformid", new JSONString (kvp.Value.CrossPlatformId?.CombinedString ?? ""));
+						if (!first) {
+							writer.WriteValueSeparator ();
+						}
 
-						//					pJson.Add("entityid", new JSONNumber (p.EntityID));
-						//                    pJson.Add("ip", new JSONString (p.IP));
-						pJson.Add ("name", new JSONString (p.Name));
-						pJson.Add ("online", new JSONBoolean (p.IsOnline));
-						pJson.Add ("position", pos);
-
-						//					pJson.Add ("totalplaytime", new JSONNumber (p.TotalPlayTime));
-						//					pJson.Add ("lastonline", new JSONString (p.LastOnline.ToString ("s")));
-						//					pJson.Add ("ping", new JSONNumber (p.IsOnline ? p.ClientInfo.ping : -1));
-
-						playersJsResult.Add (pJson);
+						first = false;
+				
+						writer.WriteRaw (jsonKeySteamId);
+						writer.WriteString (kvp.Value.PlatformId.CombinedString);
+				
+						writer.WriteRaw (jsonKeyCrossPlatformId);
+						writer.WriteString (kvp.Value.CrossPlatformId?.CombinedString ?? "");
+				
+						writer.WriteRaw (jsonKeyName);
+						writer.WriteString (p.Name);
+				
+						writer.WriteRaw (jsonKeyOnline);
+						writer.WriteBoolean (p.IsOnline);
+				
+						writer.WriteRaw (jsonKeyPosition);
+						JsonCommons.WriteVector3I (ref writer, p.LastPosition);
+				
+						writer.WriteEndObject ();
 					}
 				}
 			}
-
-			LegacyApiHelper.WriteJSON (_context.Response, playersJsResult);
+			
+			writer.WriteEndArray ();
+			
+			WebUtils.WriteJsonData (_context.Response, ref writer);
 		}
 	}
Index: /binary-improvements/MapRendering/API/GetPlayersOnline.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetPlayersOnline.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetPlayersOnline.cs	(revision 455)
@@ -1,12 +1,38 @@
 using System.Collections.Generic;
-using AllocsFixes.JSON;
 using AllocsFixes.PersistentData;
+using JetBrains.Annotations;
+using Utf8Json;
 using Webserver;
 using Webserver.WebAPI;
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetPlayersOnline : AbsWebAPI {
+		private static readonly byte[] jsonKeySteamId = JsonWriter.GetEncodedPropertyNameWithBeginObject ("steamid");
+		private static readonly byte[] jsonKeyCrossPlatformId = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("crossplatformid");
+		private static readonly byte[] jsonKeyEntityId = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("entityid");
+		private static readonly byte[] jsonKeyIp = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("ip");
+		private static readonly byte[] jsonKeyName = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("name");
+		private static readonly byte[] jsonKeyOnline = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("online");
+		private static readonly byte[] jsonKeyPosition = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("position");
+
+		private static readonly byte[] jsonKeyLevel = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("level");
+		private static readonly byte[] jsonKeyHealth = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("health");
+		private static readonly byte[] jsonKeyStamina = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("stamina");
+		private static readonly byte[] jsonKeyZombieKills = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("zombiekills");
+		private static readonly byte[] jsonKeyPlayerKills = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("playerkills");
+		private static readonly byte[] jsonKeyPlayerDeaths = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("playerdeaths");
+		private static readonly byte[] jsonKeyScore = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("score");
+
+		private static readonly byte[] jsonKeyTotalPlaytime = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("totalplaytime");
+		private static readonly byte[] jsonKeyLastOnline = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("lastonline");
+		private static readonly byte[] jsonKeyPing = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("ping");
+
 		public override void HandleRequest (RequestContext _context) {
-			JSONArray players = new JSONArray ();
+			JsonWriter writer = new JsonWriter ();
+			
+			writer.WriteBeginArray ();
+			
+			bool first = true;
 
 			World w = GameManager.Instance.World;
@@ -15,34 +41,67 @@
 				Player player = PersistentContainer.Instance.Players.GetByInternalId (ci.InternalId);
 
-				JSONObject pos = new JSONObject ();
-				pos.Add ("x", new JSONNumber ((int) current.Value.GetPosition ().x));
-				pos.Add ("y", new JSONNumber ((int) current.Value.GetPosition ().y));
-				pos.Add ("z", new JSONNumber ((int) current.Value.GetPosition ().z));
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
 
-				JSONObject p = new JSONObject ();
-				p.Add ("steamid", new JSONString (ci.PlatformId.CombinedString));
-				p.Add ("crossplatformid", new JSONString (ci.CrossplatformId?.CombinedString ?? ""));
-				p.Add ("entityid", new JSONNumber (ci.entityId));
-				p.Add ("ip", new JSONString (ci.ip));
-				p.Add ("name", new JSONString (current.Value.EntityName));
-				p.Add ("online", new JSONBoolean (true));
-				p.Add ("position", pos);
+				first = false;
+				
+				writer.WriteRaw (jsonKeySteamId);
+				writer.WriteString (ci.PlatformId.CombinedString);
+				
+				writer.WriteRaw (jsonKeyCrossPlatformId);
+				writer.WriteString (ci.CrossplatformId?.CombinedString ?? "");
+				
+				writer.WriteRaw (jsonKeyEntityId);
+				writer.WriteInt32 (ci.entityId);
+				
+				writer.WriteRaw (jsonKeyIp);
+				writer.WriteString (ci.ip);
+				
+				writer.WriteRaw (jsonKeyName);
+				writer.WriteString (current.Value.EntityName);
+				
+				writer.WriteRaw (jsonKeyOnline);
+				writer.WriteBoolean (true);
+				
+				writer.WriteRaw (jsonKeyPosition);
+				JsonCommons.WriteVector3I (ref writer, new Vector3i (current.Value.GetPosition ()));
+				
+				writer.WriteRaw (jsonKeyLevel);
+				writer.WriteSingle (player?.Level ?? -1);
+				
+				writer.WriteRaw (jsonKeyHealth);
+				writer.WriteInt32 (current.Value.Health);
+				
+				writer.WriteRaw (jsonKeyStamina);
+				writer.WriteSingle (current.Value.Stamina);
+				
+				writer.WriteRaw (jsonKeyZombieKills);
+				writer.WriteInt32 (current.Value.KilledZombies);
+				
+				writer.WriteRaw (jsonKeyPlayerKills);
+				writer.WriteInt32 (current.Value.KilledPlayers);
+				
+				writer.WriteRaw (jsonKeyPlayerDeaths);
+				writer.WriteInt32 (current.Value.Died);
+				
+				writer.WriteRaw (jsonKeyScore);
+				writer.WriteInt32 (current.Value.Score);
 
-				p.Add ("level", new JSONNumber (player?.Level ?? -1));
-				p.Add ("health", new JSONNumber (current.Value.Health));
-				p.Add ("stamina", new JSONNumber (current.Value.Stamina));
-				p.Add ("zombiekills", new JSONNumber (current.Value.KilledZombies));
-				p.Add ("playerkills", new JSONNumber (current.Value.KilledPlayers));
-				p.Add ("playerdeaths", new JSONNumber (current.Value.Died));
-				p.Add ("score", new JSONNumber (current.Value.Score));
+				writer.WriteRaw (jsonKeyTotalPlaytime);
+				writer.WriteInt64 (player?.TotalPlayTime ?? -1);
+				
+				writer.WriteRaw (jsonKeyLastOnline);
+				writer.WriteString (player != null ? player.LastOnline.ToString ("s") : string.Empty);
 
-				p.Add ("totalplaytime", new JSONNumber (player?.TotalPlayTime ?? -1));
-				p.Add ("lastonline", new JSONString (player != null ? player.LastOnline.ToString ("s") : string.Empty));
-				p.Add ("ping", new JSONNumber (ci.ping));
-
-				players.Add (p);
+				writer.WriteRaw (jsonKeyPing);
+				writer.WriteInt32 (ci.ping);
+				
+				writer.WriteEndObject ();
 			}
-
-			LegacyApiHelper.WriteJSON (_context.Response, players);
+			
+			writer.WriteEndArray ();
+			
+			WebUtils.WriteJsonData (_context.Response, ref writer);
 		}
 	}
Index: /binary-improvements/MapRendering/API/GetServerInfo.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetServerInfo.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetServerInfo.cs	(revision 455)
@@ -1,22 +1,39 @@
 using System;
-using AllocsFixes.JSON;
+using JetBrains.Annotations;
+using Utf8Json;
 using Webserver;
 using Webserver.WebAPI;
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetServerInfo : AbsWebAPI {
+		private static readonly byte[] jsonKeyType = JsonWriter.GetEncodedPropertyNameWithBeginObject ("type");
+		private static readonly byte[] jsonKeyValue = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("value");
+
 		public override void HandleRequest (RequestContext _context) {
-			JSONObject serverInfo = new JSONObject ();
+			GameServerInfo gsi = ConnectionManager.Instance.LocalServerInfo;
 
-			GameServerInfo gsi = ConnectionManager.Instance.LocalServerInfo;
+			JsonWriter writer = new JsonWriter ();
+			writer.WriteBeginObject ();
+
+			bool first = true;
 
 			foreach (string stringGamePref in Enum.GetNames (typeof (GameInfoString))) {
 				string value = gsi.GetValue ((GameInfoString) Enum.Parse (typeof (GameInfoString), stringGamePref));
+				
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
 
-				JSONObject singleStat = new JSONObject ();
-				singleStat.Add ("type", new JSONString ("string"));
-				singleStat.Add ("value", new JSONString (value));
+				first = false;
 
-				serverInfo.Add (stringGamePref, singleStat);
+				writer.WritePropertyName (stringGamePref);
+				writer.WriteRaw (jsonKeyType);
+				writer.WriteString ("string");
+				
+				writer.WriteRaw (jsonKeyValue);
+				writer.WriteString (value);
+				
+				writer.WriteEndObject ();
 			}
 
@@ -24,9 +41,18 @@
 				int value = gsi.GetValue ((GameInfoInt) Enum.Parse (typeof (GameInfoInt), intGamePref));
 
-				JSONObject singleStat = new JSONObject ();
-				singleStat.Add ("type", new JSONString ("int"));
-				singleStat.Add ("value", new JSONNumber (value));
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
 
-				serverInfo.Add (intGamePref, singleStat);
+				first = false;
+
+				writer.WritePropertyName (intGamePref);
+				writer.WriteRaw (jsonKeyType);
+				writer.WriteString ("int");
+				
+				writer.WriteRaw (jsonKeyValue);
+				writer.WriteInt32 (value);
+				
+				writer.WriteEndObject ();
 			}
 
@@ -34,13 +60,23 @@
 				bool value = gsi.GetValue ((GameInfoBool) Enum.Parse (typeof (GameInfoBool), boolGamePref));
 
-				JSONObject singleStat = new JSONObject ();
-				singleStat.Add ("type", new JSONString ("bool"));
-				singleStat.Add ("value", new JSONBoolean (value));
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
 
-				serverInfo.Add (boolGamePref, singleStat);
+				first = false;
+
+				writer.WritePropertyName (boolGamePref);
+				writer.WriteRaw (jsonKeyType);
+				writer.WriteString ("bool");
+				
+				writer.WriteRaw (jsonKeyValue);
+				writer.WriteBoolean (value);
+				
+				writer.WriteEndObject ();
 			}
-
-
-			LegacyApiHelper.WriteJSON (_context.Response, serverInfo);
+			
+			writer.WriteEndObject ();
+			
+			WebUtils.WriteJsonData (_context.Response, ref writer);
 		}
 	}
Index: /binary-improvements/MapRendering/API/GetStats.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetStats.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetStats.cs	(revision 455)
@@ -1,23 +1,36 @@
-using AllocsFixes.JSON;
 using AllocsFixes.LiveData;
+using JetBrains.Annotations;
+using Utf8Json;
 using Webserver;
 using Webserver.WebAPI;
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetStats : AbsWebAPI {
+		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");
+
 		public override void HandleRequest (RequestContext _context) {
-			JSONObject result = new JSONObject ();
-
-			JSONObject time = new JSONObject ();
-			time.Add ("days", new JSONNumber (GameUtils.WorldTimeToDays (GameManager.Instance.World.worldTime)));
-			time.Add ("hours", new JSONNumber (GameUtils.WorldTimeToHours (GameManager.Instance.World.worldTime)));
-			time.Add ("minutes", new JSONNumber (GameUtils.WorldTimeToMinutes (GameManager.Instance.World.worldTime)));
-			result.Add ("gametime", time);
-
-			result.Add ("players", new JSONNumber (GameManager.Instance.World.Players.Count));
-			result.Add ("hostiles", new JSONNumber (Hostiles.Instance.GetCount ()));
-			result.Add ("animals", new JSONNumber (Animals.Instance.GetCount ()));
-
-			LegacyApiHelper.WriteJSON (_context.Response, result);
+			JsonWriter writer = new JsonWriter ();
+	
+			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 ();
+			
+			WebUtils.WriteJsonData (_context.Response, ref writer);
 		}
 
Index: /binary-improvements/MapRendering/API/GetWebUIUpdates.cs
===================================================================
--- /binary-improvements/MapRendering/API/GetWebUIUpdates.cs	(revision 454)
+++ /binary-improvements/MapRendering/API/GetWebUIUpdates.cs	(revision 455)
@@ -1,9 +1,18 @@
-using AllocsFixes.JSON;
 using AllocsFixes.LiveData;
+using JetBrains.Annotations;
+using Utf8Json;
 using Webserver;
 using Webserver.WebAPI;
 
 namespace AllocsFixes.WebAPIs {
+	[UsedImplicitly]
 	public class GetWebUIUpdates : AbsWebAPI {
+		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");
+		private static readonly byte[] jsonKeyNewLogs = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("newlogs");
+
 		public override void HandleRequest (RequestContext _context) {
 			int latestLine;
@@ -13,19 +22,25 @@
 			}
 
-			JSONObject result = new JSONObject ();
+			JsonWriter writer = new JsonWriter ();
 
-			JSONObject time = new JSONObject ();
-			time.Add ("days", new JSONNumber (GameUtils.WorldTimeToDays (GameManager.Instance.World.worldTime)));
-			time.Add ("hours", new JSONNumber (GameUtils.WorldTimeToHours (GameManager.Instance.World.worldTime)));
-			time.Add ("minutes", new JSONNumber (GameUtils.WorldTimeToMinutes (GameManager.Instance.World.worldTime)));
-			result.Add ("gametime", time);
-
-			result.Add ("players", new JSONNumber (GameManager.Instance.World.Players.Count));
-			result.Add ("hostiles", new JSONNumber (Hostiles.Instance.GetCount ()));
-			result.Add ("animals", new JSONNumber (Animals.Instance.GetCount ()));
-
-			result.Add ("newlogs", new JSONNumber (LogBuffer.Instance.LatestLine - latestLine));
-
-			LegacyApiHelper.WriteJSON (_context.Response, result);
+			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.WriteRaw (jsonKeyNewLogs);
+			writer.WriteInt32 (LogBuffer.Instance.LatestLine - latestLine);
+			
+			writer.WriteEndObject ();
+			
+			WebUtils.WriteJsonData (_context.Response, ref writer);
 		}
 
Index: nary-improvements/MapRendering/LegacyApiHelper.cs
===================================================================
--- /binary-improvements/MapRendering/LegacyApiHelper.cs	(revision 454)
+++ 	(revision )
@@ -1,27 +1,0 @@
-using System.Net;
-using System.Text;
-using AllocsFixes.JSON;
-using Webserver;
-using HttpListenerResponse = SpaceWizards.HttpListener.HttpListenerResponse;
-
-namespace AllocsFixes {
-	public static class LegacyApiHelper {
-		public static void WriteJSON (HttpListenerResponse _resp, JSONNode _root) {
-#if ENABLE_PROFILER
-			jsonSerializeSampler.Begin ();
-#endif
-			StringBuilder sb = new StringBuilder ();
-			_root.ToString (sb);
-#if ENABLE_PROFILER
-			jsonSerializeSampler.End ();
-			netWriteSampler.Begin ();
-#endif
-			WebUtils.WriteText (_resp, sb.ToString(), HttpStatusCode.OK, WebUtils.MimeJson);
-			
-#if ENABLE_PROFILER
-			netWriteSampler.End ();
-#endif
-		}
-
-	}
-}
Index: /binary-improvements/MapRendering/ModInfo.xml
===================================================================
--- /binary-improvements/MapRendering/ModInfo.xml	(revision 454)
+++ /binary-improvements/MapRendering/ModInfo.xml	(revision 455)
@@ -5,6 +5,6 @@
 		<Description value="Render the game map to image map tiles as it is uncovered" />
 		<Author value="Christian 'Alloc' Illy" />
-		<Version value="43" />
-		<Website value="http://7dtd.illy.bz" />
+		<Version value="44" />
+		<Website value="https://7dtd.illy.bz" />
 	</ModInfo>
 </xml>
Index: /binary-improvements/MapRendering/SteamLoginApi.cs
===================================================================
--- /binary-improvements/MapRendering/SteamLoginApi.cs	(revision 454)
+++ /binary-improvements/MapRendering/SteamLoginApi.cs	(revision 455)
@@ -1,10 +1,10 @@
-using System;
 using System.Net;
-using Platform.Steam;
+using JetBrains.Annotations;
 using Webserver;
 using Webserver.UrlHandlers;
 using Webserver.WebAPI;
 
-namespace AllocsFixes {
+namespace AllocsFixes.Web {
+	[UsedImplicitly]
 	public class SteamLoginApi : AbsWebAPI {
 		public override int DefaultPermissionLevel () => 2000;
@@ -15,5 +15,5 @@
 		private const string steamLoginUrl = "loginsteam";
 		
-		public SteamLoginApi (Web _parentWeb) : base(_parentWeb) {
+		public SteamLoginApi (Webserver.Web _parentWeb) : base(_parentWeb) {
 		}
 
@@ -43,5 +43,5 @@
 			
 			if (subpath.StartsWith (steamLoginUrl)) {
-				var absoluteUrl = _context.Request.Url.AbsolutePath;
+				string absoluteUrl = _context.Request.Url.AbsolutePath;
 				absoluteUrl = absoluteUrl.Substring (0, absoluteUrl.Length - _context.RequestPath.Length);
 				SessionHandler.HandleSteamLogin (_context, $"{absoluteUrl}{steamOpenIdVerifyUrl}");
Index: /binary-improvements/MapRendering/WebAndMapRendering.csproj
===================================================================
--- /binary-improvements/MapRendering/WebAndMapRendering.csproj	(revision 454)
+++ /binary-improvements/MapRendering/WebAndMapRendering.csproj	(revision 455)
@@ -89,4 +89,8 @@
       <Private>False</Private>
     </Reference>
+    <Reference Include="Utf8Json">
+      <HintPath>..\7dtd-binaries\Utf8Json.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
     <Reference Include="WebServer, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
       <HintPath>..\7dtd-binaries\WebServer.dll</HintPath>
@@ -98,4 +102,5 @@
     <Compile Include="API\GetAllowedCommands.cs" />
     <Compile Include="API\GetAnimalsLocation.cs" />
+    <Compile Include="API\GetEntityListAbs.cs" />
     <Compile Include="API\GetHostileLocation.cs" />
     <Compile Include="API\GetLandClaims.cs" />
@@ -111,5 +116,4 @@
     <Compile Include="AssemblyInfo.cs" />
     <Compile Include="API.cs" />
-    <Compile Include="LegacyApiHelper.cs" />
     <Compile Include="SteamLoginApi.cs" />
     <Compile Include="WebCommandResult.cs" />
Index: /binary-improvements/MapRendering/WebCommandResult.cs
===================================================================
--- /binary-improvements/MapRendering/WebCommandResult.cs	(revision 454)
+++ /binary-improvements/MapRendering/WebCommandResult.cs	(revision 455)
@@ -5,14 +5,14 @@
 using System.Text;
 using System.Threading;
-using AllocsFixes.JSON;
 using UnityEngine;
+using Utf8Json;
 using Webserver;
 
-namespace AllocsFixes.NetConnections.Servers.Web {
+namespace AllocsFixes.Web {
 	public class WebCommandResult : IConsoleConnection {
 		public enum ResultType {
 			Full,
 			ResultOnly,
-			Raw
+			Raw,
 		}
 
@@ -35,4 +35,8 @@
 			responseType = _resultType;
 		}
+		
+		private static readonly byte[] jsonKeyCommand = JsonWriter.GetEncodedPropertyNameWithBeginObject ("command");
+		private static readonly byte[] jsonKeyParameters = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("parameters");
+		private static readonly byte[] jsonKeyResult = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("result");
 
 		public void SendLines (List<string> _output) {
@@ -44,33 +48,39 @@
 			}
 
+			string result = sb.ToString ();
+
 			try {
 				context.Response.SendChunked = false;
 
 				if (responseType == ResultType.Raw) {
-					WebUtils.WriteText (context.Response, sb.ToString ());
+					WebUtils.WriteText (context.Response, result);
 				} else {
-					JSONNode result;
+					JsonWriter writer = new JsonWriter ();
+
 					if (responseType == ResultType.ResultOnly) {
-						result = new JSONString (sb.ToString ());
+						writer.WriteString (result);
 					} else {
-						JSONObject resultObj = new JSONObject ();
-
-						resultObj.Add ("command", new JSONString (command));
-						resultObj.Add ("parameters", new JSONString (parameters));
-						resultObj.Add ("result", new JSONString (sb.ToString ()));
-
-						result = resultObj;
+						writer.WriteRaw (jsonKeyCommand);
+						writer.WriteString (command);
+						
+						writer.WriteRaw (jsonKeyParameters);
+						writer.WriteString (parameters);
+						
+						writer.WriteRaw (jsonKeyResult);
+						writer.WriteString (result);
+						
+						writer.WriteEndObject ();
 					}
 
-					LegacyApiHelper.WriteJSON (context.Response, result);
+					WebUtils.WriteJsonData (context.Response, ref writer);
 				}
 			} catch (IOException e) {
 				if (e.InnerException is SocketException) {
-					Log.Out ("Error in WebCommandResult.SendLines(): Remote host closed connection: " + e.InnerException.Message);
+					Log.Out ($"Error in WebCommandResult.SendLines(): Remote host closed connection: {e.InnerException.Message}");
 				} else {
-					Log.Out ("Error (IO) in WebCommandResult.SendLines(): " + e);
+					Log.Out ($"Error (IO) in WebCommandResult.SendLines(): {e}");
 				}
 			} catch (Exception e) {
-				Log.Out ("Error in WebCommandResult.SendLines(): " + e);
+				Log.Out ($"Error in WebCommandResult.SendLines(): {e}");
 			} finally {
 				context.Response?.Close ();
@@ -99,5 +109,5 @@
 
 		public string GetDescription () {
-			return "WebCommandResult_for_" + command;
+			return $"WebCommandResult_for_{command}";
 		}
 	}
Index: /binary-improvements/bin/Mods/Allocs_CommandExtensions/ModInfo.xml
===================================================================
--- /binary-improvements/bin/Mods/Allocs_CommandExtensions/ModInfo.xml	(revision 454)
+++ /binary-improvements/bin/Mods/Allocs_CommandExtensions/ModInfo.xml	(revision 455)
@@ -5,6 +5,6 @@
 		<Description value="Additional commands for server operation" />
 		<Author value="Christian 'Alloc' Illy" />
-		<Version value="24" />
-		<Website value="http://7dtd.illy.bz" />
+		<Version value="25" />
+		<Website value="https://7dtd.illy.bz" />
 	</ModInfo>
 </xml>
Index: /binary-improvements/bin/Mods/Allocs_CommonFunc/ModInfo.xml
===================================================================
--- /binary-improvements/bin/Mods/Allocs_CommonFunc/ModInfo.xml	(revision 454)
+++ /binary-improvements/bin/Mods/Allocs_CommonFunc/ModInfo.xml	(revision 455)
@@ -5,6 +5,6 @@
 		<Description value="Common functions" />
 		<Author value="Christian 'Alloc' Illy" />
-		<Version value="29" />
-		<Website value="http://7dtd.illy.bz" />
+		<Version value="30" />
+		<Website value="https://7dtd.illy.bz" />
 	</ModInfo>
 </xml>
Index: /binary-improvements/bin/Mods/Allocs_WebAndMapRendering/ModInfo.xml
===================================================================
--- /binary-improvements/bin/Mods/Allocs_WebAndMapRendering/ModInfo.xml	(revision 454)
+++ /binary-improvements/bin/Mods/Allocs_WebAndMapRendering/ModInfo.xml	(revision 455)
@@ -5,6 +5,6 @@
 		<Description value="Render the game map to image map tiles as it is uncovered" />
 		<Author value="Christian 'Alloc' Illy" />
-		<Version value="43" />
-		<Website value="http://7dtd.illy.bz" />
+		<Version value="44" />
+		<Website value="https://7dtd.illy.bz" />
 	</ModInfo>
 </xml>
