Index: binary-improvements2/WebServer/src/WebAPI/APIs/Animal.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/Animal.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/Animal.cs	(revision 402)
@@ -0,0 +1,47 @@
+﻿using System.Collections.Generic;
+using JetBrains.Annotations;
+using Utf8Json;
+using Webserver.LiveData;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	internal class Animal : AbsRestApi {
+		private readonly List<EntityAnimal> entities = new List<EntityAnimal> ();
+
+		private static readonly byte[] jsonKeyId = JsonWriter.GetEncodedPropertyNameWithBeginObject ("id");
+		private static readonly byte[] jsonKeyName = JsonWriter.GetEncodedPropertyNameWithBeginObject ("name");
+		private static readonly byte[] jsonKeyPosition = JsonWriter.GetEncodedPropertyNameWithBeginObject ("position");
+
+		protected override void HandleRestGet (RequestContext _context) {
+			PrepareEnvelopedResult (out JsonWriter writer);
+			writer.WriteBeginArray ();
+			
+			lock (entities) {
+				Animals.Instance.Get (entities);
+				
+				for (int i = 0; i < entities.Count; i++) {
+					if (i > 0) {
+						writer.WriteValueSeparator ();
+					}
+					
+					EntityAlive entity = entities [i];
+					Vector3i position = new Vector3i (entity.GetPosition ());
+					
+					writer.WriteRaw (jsonKeyId);
+					writer.WriteInt32 (entity.entityId);
+					
+					writer.WriteRaw (jsonKeyName);
+					writer.WriteString (!string.IsNullOrEmpty (entity.EntityName) ? entity.EntityName : $"animal class #{entity.entityClass}");
+					
+					writer.WriteRaw (jsonKeyPosition);
+					JsonCommons.WritePositionObject (writer, position);
+
+					writer.WriteEndObject ();
+				}
+			}
+			
+			writer.WriteEndArray ();
+			SendEnvelopedResult (_context, ref writer);
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/Command.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/Command.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/Command.cs	(revision 402)
@@ -0,0 +1,135 @@
+using System.Collections.Generic;
+using System.Net;
+using JetBrains.Annotations;
+using Utf8Json;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	public class Command : AbsRestApi {
+		private static readonly byte[] jsonCommandsKey = JsonWriter.GetEncodedPropertyNameWithBeginObject ("commands");
+
+		private static readonly byte[] jsonOverloadsKey = JsonWriter.GetEncodedPropertyNameWithBeginObject ("overloads");
+		private static readonly byte[] jsonCommandKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("command");
+		private static readonly byte[] jsonDescriptionKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("description");
+		private static readonly byte[] jsonHelpKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("help");
+		private static readonly byte[] jsonAllowedKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("allowed");
+		
+		protected override void HandleRestGet (RequestContext _context) {
+			string id = _context.RequestPath;
+			int permissionLevel = _context.PermissionLevel;
+			
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.WriteRaw (jsonCommandsKey);
+			writer.WriteBeginArray ();
+
+			if (string.IsNullOrEmpty (id)) {
+				bool first = true;
+				foreach (IConsoleCommand cc in SdtdConsole.Instance.GetCommands ()) {
+					if (!first) {
+						writer.WriteValueSeparator ();
+					}
+
+					first = false;
+
+					writeCommandJson (ref writer, cc, permissionLevel);
+				}
+			} else if (SdtdConsole.Instance.GetCommand (id) is { } command) {
+				writeCommandJson (ref writer, command, permissionLevel);
+			} else {
+				writer.WriteEndArray ();
+				writer.WriteEndObject ();
+				SendEnvelopedResult (_context, ref writer, HttpStatusCode.NotFound);
+				return;
+			}
+
+			writer.WriteEndArray ();
+			writer.WriteEndObject ();
+
+			SendEnvelopedResult (_context, ref writer);
+		}
+
+		private void writeCommandJson (ref JsonWriter _writer, IConsoleCommand _command, int _userPermissionLevel) {
+			_writer.WriteRaw (jsonOverloadsKey);
+			_writer.WriteBeginArray ();
+
+			string cmd = string.Empty;
+				
+			bool firstOverload = true;
+			foreach (string s in _command.GetCommands ()) {
+				if (!firstOverload) {
+					_writer.WriteValueSeparator ();
+				}
+				firstOverload = false;
+					
+				_writer.WriteString (s);
+					
+				if (s.Length > cmd.Length) {
+					cmd = s;
+				}
+			}
+				
+			_writer.WriteEndArray ();
+
+			_writer.WriteRaw (jsonCommandKey);
+			_writer.WriteString (cmd);
+				
+			_writer.WriteRaw (jsonDescriptionKey);
+			_writer.WriteString (_command.GetDescription ());
+				
+			_writer.WriteRaw (jsonHelpKey);
+			_writer.WriteString (_command.GetHelp ());
+				
+			int commandPermissionLevel = GameManager.Instance.adminTools.GetCommandPermissionLevel (_command.GetCommands ());
+			_writer.WriteRaw (jsonAllowedKey);
+			_writer.WriteBoolean (_userPermissionLevel <= commandPermissionLevel);
+
+			_writer.WriteEndObject ();
+		}
+
+		protected override void HandleRestPost (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
+			if (!TryGetJsonField (_jsonInput, "command", out string commandString)) {
+				SendErrorResult (_context, HttpStatusCode.BadRequest, _jsonInputData, "NO_COMMAND");
+				return;
+			}
+
+			WebCommandResult.ResultType responseType = WebCommandResult.ResultType.Full;
+
+			if (TryGetJsonField (_jsonInput, "format", out string formatString)) {
+				if (formatString.EqualsCaseInsensitive ("raw")) {
+					responseType = WebCommandResult.ResultType.Raw;
+				} else if (formatString.EqualsCaseInsensitive ("simple")) {
+					responseType = WebCommandResult.ResultType.ResultOnly;
+				}
+			}
+
+			int commandSepIndex = commandString.IndexOf (' ');
+			string commandPart = commandSepIndex > 0 ? commandString.Substring (0, commandSepIndex) : commandString;
+			string argumentsPart = commandSepIndex > 0
+				? commandString.Substring (commandPart.Length + 1)
+				: "";
+
+			IConsoleCommand command = SdtdConsole.Instance.GetCommand (commandPart, true);
+
+			if (command == null) {
+				SendErrorResult (_context, HttpStatusCode.NotFound, _jsonInputData, "UNKNOWN_COMMAND");
+				return;
+			}
+
+			int commandPermissionLevel = GameManager.Instance.adminTools.GetCommandPermissionLevel (command.GetCommands ());
+
+			if (_context.PermissionLevel > commandPermissionLevel) {
+				SendErrorResult (_context, HttpStatusCode.Forbidden, _jsonInputData, "NO_PERMISSION");
+				return;
+			}
+
+			_context.Response.SendChunked = true;
+			WebCommandResult wcr = new WebCommandResult (commandPart, argumentsPart, responseType, _context);
+			SdtdConsole.Instance.ExecuteAsync (commandString, wcr);
+		}
+
+		public override int DefaultPermissionLevel () {
+			return 2000;
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/GetLandClaims.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/GetLandClaims.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/GetLandClaims.cs	(revision 402)
@@ -0,0 +1,78 @@
+// using System.Collections.Generic;
+// using System.Net;
+// using AllocsFixes;
+// using AllocsFixes.PersistentData;
+// using JetBrains.Annotations;
+//
+// namespace Webserver.WebAPI.APIs {
+// 	[UsedImplicitly]
+// 	public class GetLandClaims : AbsWebAPI {
+// 		public override void HandleRequest (RequestContext _context) {
+// 			PlatformUserIdentifierAbs requestedUserId = null;
+// 			if (_context.Request.QueryString ["userid"] != null) {
+// 				if (!PlatformUserIdentifierAbs.TryFromCombinedString (_context.Request.QueryString ["userid"], out requestedUserId)) {
+// 					WebUtils.WriteText (_context.Response, "Invalid user id given", HttpStatusCode.BadRequest);
+// 					return;
+// 				}
+// 			}
+//
+// 			// default user, cheap way to avoid 'null reference exception'
+// 			PlatformUserIdentifierAbs userId = _context.Connection?.UserId;
+//
+// 			bool bViewAll = WebConnection.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) {
+// 				if (requestedUserId != null && !bViewAll) {
+// 					ownerFilters = new[] {
+// 						LandClaimList.UserIdFilter (userId),
+// 						LandClaimList.UserIdFilter (requestedUserId)
+// 					};
+// 				} else if (!bViewAll) {
+// 					ownerFilters = new[] {LandClaimList.UserIdFilter (userId)};
+// 				} else {
+// 					ownerFilters = new[] {LandClaimList.UserIdFilter (requestedUserId)};
+// 				}
+// 			}
+//
+// 			LandClaimList.PositionFilter[] posFilters = null;
+//
+// 			Dictionary<Player, List<Vector3i>> claims = LandClaimList.GetLandClaims (ownerFilters, posFilters);
+//
+// 			foreach ((Player player, List<Vector3i> claimPositions) in claims) {
+// 				JsonObject owner = new JsonObject ();
+// 				claimOwners.Add (owner);
+//
+// 				owner.Add ("steamid", new JsonString (player.PlatformId.CombinedString));
+// 				owner.Add ("claimactive", new JsonBoolean (player.LandProtectionActive));
+//
+// 				if (player.Name.Length > 0) {
+// 					owner.Add ("playername", new JsonString (player.Name));
+// 				} else {
+// 					owner.Add ("playername", new JsonNull ());
+// 				}
+//
+// 				JsonArray claimsJson = new JsonArray ();
+// 				owner.Add ("claims", claimsJson);
+//
+// 				foreach (Vector3i v in claimPositions) {
+// 					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));
+//
+// 					claimsJson.Add (claim);
+// 				}
+// 			}
+//
+// 			WebUtils.WriteJson (_context.Response, result);
+// 		}
+// 	}
+// }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerInventories.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerInventories.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerInventories.cs	(revision 402)
@@ -0,0 +1,25 @@
+// using AllocsFixes.PersistentData;
+// using JetBrains.Annotations;
+//
+// namespace Webserver.WebAPI.APIs {
+// 	[UsedImplicitly]
+// 	public class GetPlayerInventories : AbsWebAPI {
+// 		public override void HandleRequest (RequestContext _context) {
+// 			GetPlayerInventory.GetInventoryArguments (_context.Request, out bool showIconColor, out bool showIconName);
+//
+// 			JsonArray allInventoriesResult = new JsonArray ();
+//
+// 			foreach ((PlatformUserIdentifierAbs userId, Player player) in PersistentContainer.Instance.Players.Dict) {
+// 				if (player == null) {
+// 					continue;
+// 				}
+//
+// 				if (player.IsOnline) {
+// 					allInventoriesResult.Add (GetPlayerInventory.DoPlayer (userId.CombinedString, player, showIconColor, showIconName));
+// 				}
+// 			}
+//
+// 			WebUtils.WriteJson (_context.Response, allInventoriesResult);
+// 		}
+// 	}
+// }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerInventory.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerInventory.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerInventory.cs	(revision 402)
@@ -0,0 +1,128 @@
+// using System.Collections.Generic;
+// using System.Net;
+// using AllocsFixes.PersistentData;
+// using JetBrains.Annotations;
+// using HttpListenerRequest = SpaceWizards.HttpListener.HttpListenerRequest;
+//
+// namespace Webserver.WebAPI.APIs {
+// 	[UsedImplicitly]
+// 	public class GetPlayerInventory : AbsWebAPI {
+// 		public override void HandleRequest (RequestContext _context) {
+// 			if (_context.Request.QueryString ["userid"] == null) {
+// 				WebUtils.WriteText (_context.Response, "No user id given", HttpStatusCode.BadRequest);
+// 				return;
+// 			}
+//
+// 			string userIdString = _context.Request.QueryString ["userid"];
+// 			if (!PlatformUserIdentifierAbs.TryFromCombinedString (userIdString, out PlatformUserIdentifierAbs userId)) {
+// 				WebUtils.WriteText (_context.Response, "Invalid user id given", HttpStatusCode.BadRequest);
+// 				return;
+// 			}
+//
+// 			Player p = PersistentContainer.Instance.Players [userId, false];
+// 			if (p == null) {
+// 				WebUtils.WriteText (_context.Response, "Unknown user id given", HttpStatusCode.NotFound);
+// 				return;
+// 			}
+//
+// 			GetInventoryArguments (_context.Request, out bool showIconColor, out bool showIconName);
+//
+// 			JsonObject result = DoPlayer (userIdString, p, showIconColor, showIconName);
+//
+// 			WebUtils.WriteJson (_context.Response, result);
+// 		}
+//
+// 		internal static void GetInventoryArguments (HttpListenerRequest _req, out bool _showIconColor, out bool _showIconName) {
+// 			if (_req.QueryString ["showiconcolor"] == null || !bool.TryParse (_req.QueryString ["showiconcolor"], out _showIconColor)) {
+// 				_showIconColor = true;
+// 			}
+// 			
+// 			if (_req.QueryString ["showiconname"] == null || !bool.TryParse (_req.QueryString ["showiconname"], out _showIconName)) {
+// 				_showIconName = true;
+// 			}
+// 		}
+//
+// 		internal static JsonObject DoPlayer (string _steamId, Player _player, bool _showIconColor, bool _showIconName) {
+// 			AllocsFixes.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 (_steamId));
+// 			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;
+// 		}
+//
+// 		private static void DoInventory (JsonArray _jsonRes, List<InvItem> _inv, bool _showIconColor, bool _showIconName) {
+// 			for (int i = 0; i < _inv.Count; i++) {
+// 				_jsonRes.Add (GetJsonForItem (_inv [i], _showIconColor, _showIconName));
+// 			}
+// 		}
+//
+// 		private static void AddEquipment (JsonObject _eq, string _slotname, InvItem[] _items, EquipmentSlots _slot, bool _showIconColor, bool _showIconName) {
+// 			int[] slotindices = XUiM_PlayerEquipment.GetSlotIndicesByEquipmentSlot (_slot);
+//
+// 			for (int i = 0; i < slotindices.Length; i++) {
+// 				if (_items? [slotindices [i]] == null) {
+// 					continue;
+// 				}
+//
+// 				InvItem item = _items [slotindices [i]];
+// 				_eq.Add (_slotname, GetJsonForItem (item, _showIconColor, _showIconName));
+// 				return;
+// 			}
+//
+// 			_eq.Add (_slotname, new JsonNull ());
+// 		}
+//
+// 		private static JsonNode GetJsonForItem (InvItem _item, bool _showIconColor, bool _showIconName) {
+// 			if (_item == null) {
+// 				return new JsonNull ();
+// 			}
+//
+// 			JsonObject jsonItem = new JsonObject ();
+// 			jsonItem.Add ("count", new JsonNumber (_item.count));
+// 			jsonItem.Add ("name", new JsonString (_item.itemName));
+// 			
+// 			if (_showIconName) {
+// 				jsonItem.Add ("icon", new JsonString (_item.icon));
+// 			}
+//
+// 			if (_showIconColor) {
+// 				jsonItem.Add ("iconcolor", new JsonString (_item.iconcolor));
+// 			}
+//
+// 			jsonItem.Add ("quality", new JsonNumber (_item.quality));
+// 			if (_item.quality >= 0) {
+// 				jsonItem.Add ("qualitycolor", new JsonString (QualityInfo.GetQualityColorHex (_item.quality)));
+// 			}
+//
+// 			return jsonItem;
+//
+// 		}
+// 	}
+// }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerList.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerList.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayerList.cs	(revision 402)
@@ -0,0 +1,263 @@
+// using System;
+// using System.Collections.Generic;
+// using System.Linq;
+// using System.Text.RegularExpressions;
+// using AllocsFixes.PersistentData;
+// using JetBrains.Annotations;
+//
+// namespace Webserver.WebAPI.APIs {
+// 	[UsedImplicitly]
+// 	public class GetPlayerList : AbsWebAPI {
+// 		private static readonly Regex numberFilterMatcher =
+// 			new Regex (@"^(>=|=>|>|<=|=<|<|==|=)?\s*([0-9]+(\.[0-9]*)?)$");
+//
+// 		private static readonly UnityEngine.Profiling.CustomSampler jsonSerializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Build");
+//
+// 		public override void HandleRequest (RequestContext _context) {
+// 			AdminTools admTools = GameManager.Instance.adminTools;
+// 			PlatformUserIdentifierAbs userId = _context.Connection?.UserId;
+//
+// 			bool bViewAll = WebConnection.CanViewAllPlayers (_context.PermissionLevel);
+//
+// 			// TODO: Sort (and filter?) prior to converting to JSON ... hard as how to get the correct column's data? (i.e. column name matches JSON object field names, not source data)
+//
+// 			int rowsPerPage = 25;
+// 			if (_context.Request.QueryString ["rowsperpage"] != null) {
+// 				int.TryParse (_context.Request.QueryString ["rowsperpage"], out rowsPerPage);
+// 			}
+//
+// 			int page = 0;
+// 			if (_context.Request.QueryString ["page"] != null) {
+// 				int.TryParse (_context.Request.QueryString ["page"], out page);
+// 			}
+//
+// 			int firstEntry = page * rowsPerPage;
+//
+// 			Players playersList = PersistentContainer.Instance.Players;
+//
+// 			
+// 			List<JsonObject> playerList = new List<JsonObject> ();
+//
+// 			jsonSerializeSampler.Begin ();
+//
+// 			foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in playersList.Dict) {
+// 				Player p = kvp.Value;
+//
+// 				if (bViewAll || p.PlatformId.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.Key.CombinedString));
+// 					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.ToUniversalTime ().ToString ("yyyy-MM-ddTHH:mm:ssZ")));
+// 					pJson.Add ("ping", new JsonNumber (p.IsOnline ? p.ClientInfo.ping : -1));
+//
+// 					JsonBoolean banned = admTools != null ? new JsonBoolean (admTools.IsBanned (kvp.Key, out _, out _)) : new JsonBoolean (false);
+//
+// 					pJson.Add ("banned", banned);
+//
+// 					playerList.Add (pJson);
+// 				}
+// 			}
+//
+// 			jsonSerializeSampler.End ();
+//
+// 			IEnumerable<JsonObject> list = playerList;
+//
+// 			foreach (string key in _context.Request.QueryString.AllKeys) {
+// 				if (!string.IsNullOrEmpty (key) && key.StartsWith ("filter[")) {
+// 					string filterCol = key.Substring (key.IndexOf ('[') + 1);
+// 					filterCol = filterCol.Substring (0, filterCol.Length - 1);
+// 					string filterVal = _context.Request.QueryString.Get (key).Trim ();
+//
+// 					list = ExecuteFilter (list, filterCol, filterVal);
+// 				}
+// 			}
+//
+// 			int totalAfterFilter = list.Count ();
+//
+// 			foreach (string key in _context.Request.QueryString.AllKeys) {
+// 				if (!string.IsNullOrEmpty (key) && key.StartsWith ("sort[")) {
+// 					string sortCol = key.Substring (key.IndexOf ('[') + 1);
+// 					sortCol = sortCol.Substring (0, sortCol.Length - 1);
+// 					string sortVal = _context.Request.QueryString.Get (key);
+//
+// 					list = ExecuteSort (list, sortCol, sortVal == "0");
+// 				}
+// 			}
+//
+// 			list = list.Skip (firstEntry);
+// 			list = list.Take (rowsPerPage);
+//
+//
+// 			JsonArray playersJsResult = new JsonArray ();
+// 			foreach (JsonObject jsO in list) {
+// 				playersJsResult.Add (jsO);
+// 			}
+//
+// 			JsonObject result = new JsonObject ();
+// 			result.Add ("total", new JsonNumber (totalAfterFilter));
+// 			result.Add ("totalUnfiltered", new JsonNumber (playerList.Count));
+// 			result.Add ("firstResult", new JsonNumber (firstEntry));
+// 			result.Add ("players", playersJsResult);
+//
+// 			WebUtils.WriteJson (_context.Response, result);
+// 		}
+//
+// 		private IEnumerable<JsonObject> ExecuteFilter (IEnumerable<JsonObject> _list, string _filterCol,
+// 			string _filterVal) {
+// 			if (!_list.Any()) {
+// 				return _list;
+// 			}
+//
+// 			if (_list.First ().ContainsKey (_filterCol)) {
+// 				Type colType = _list.First () [_filterCol].GetType ();
+// 				if (colType == typeof (JsonNumber)) {
+// 					return ExecuteNumberFilter (_list, _filterCol, _filterVal);
+// 				}
+//
+// 				if (colType == typeof (JsonBoolean)) {
+// 					bool value = StringParsers.ParseBool (_filterVal);
+// 					return _list.Where (_line => ((JsonBoolean) _line [_filterCol]).GetBool () == value);
+// 				}
+//
+// 				if (colType == typeof (JsonString)) {
+// 					// regex-match whole ^string$, replace * by .*, ? by .?, + by .+
+// 					_filterVal = _filterVal.Replace ("*", ".*").Replace ("?", ".?").Replace ("+", ".+");
+// 					_filterVal = "^" + _filterVal + "$";
+//
+// 					//Log.Out ("GetPlayerList: Filter on String with Regex '" + _filterVal + "'");
+// 					Regex matcher = new Regex (_filterVal, RegexOptions.IgnoreCase);
+// 					return _list.Where (_line => matcher.IsMatch (((JsonString) _line [_filterCol]).GetString ()));
+// 				}
+// 			}
+//
+// 			return _list;
+// 		}
+//
+//
+// 		private IEnumerable<JsonObject> ExecuteNumberFilter (IEnumerable<JsonObject> _list, string _filterCol,
+// 			string _filterVal) {
+// 			// allow value (exact match), =, ==, >=, >, <=, <
+// 			Match filterMatch = numberFilterMatcher.Match (_filterVal);
+// 			if (filterMatch.Success) {
+// 				double value = StringParsers.ParseDouble (filterMatch.Groups [2].Value);
+// 				NumberMatchType matchType;
+// 				double epsilon = value / 100000;
+// 				switch (filterMatch.Groups [1].Value) {
+// 					case "":
+// 					case "=":
+// 					case "==":
+// 						matchType = NumberMatchType.Equal;
+// 						break;
+// 					case ">":
+// 						matchType = NumberMatchType.Greater;
+// 						break;
+// 					case ">=":
+// 					case "=>":
+// 						matchType = NumberMatchType.GreaterEqual;
+// 						break;
+// 					case "<":
+// 						matchType = NumberMatchType.Lesser;
+// 						break;
+// 					case "<=":
+// 					case "=<":
+// 						matchType = NumberMatchType.LesserEqual;
+// 						break;
+// 					default:
+// 						matchType = NumberMatchType.Equal;
+// 						break;
+// 				}
+//
+// 				return _list.Where (delegate (JsonObject _line) {
+// 					double objVal = ((JsonNumber) _line [_filterCol]).GetDouble ();
+// 					switch (matchType) {
+// 						case NumberMatchType.Greater:
+// 							return objVal > value;
+// 						case NumberMatchType.GreaterEqual:
+// 							return objVal >= value;
+// 						case NumberMatchType.Lesser:
+// 							return objVal < value;
+// 						case NumberMatchType.LesserEqual:
+// 							return objVal <= value;
+// 						case NumberMatchType.Equal:
+// 						default:
+// 							return NearlyEqual (objVal, value, epsilon);
+// 					}
+// 				});
+// 			}
+//
+// 			global::Log.Out ("[Web] GetPlayerList: ignoring invalid filter for number-column '{0}': '{1}'", _filterCol, _filterVal);
+// 			return _list;
+// 		}
+//
+//
+// 		private IEnumerable<JsonObject> ExecuteSort (IEnumerable<JsonObject> _list, string _sortCol, bool _ascending) {
+// 			if (_list.Count () == 0) {
+// 				return _list;
+// 			}
+//
+// 			if (_list.First ().ContainsKey (_sortCol)) {
+// 				Type colType = _list.First () [_sortCol].GetType ();
+// 				if (colType == typeof (JsonNumber)) {
+// 					if (_ascending) {
+// 						return _list.OrderBy (_line => ((JsonNumber) _line [_sortCol]).GetDouble ());
+// 					}
+//
+// 					return _list.OrderByDescending (_line => ((JsonNumber) _line [_sortCol]).GetDouble ());
+// 				}
+//
+// 				if (colType == typeof (JsonBoolean)) {
+// 					if (_ascending) {
+// 						return _list.OrderBy (_line => ((JsonBoolean) _line [_sortCol]).GetBool ());
+// 					}
+//
+// 					return _list.OrderByDescending (_line => ((JsonBoolean) _line [_sortCol]).GetBool ());
+// 				}
+//
+// 				if (_ascending) {
+// 					return _list.OrderBy (_line => _line [_sortCol].ToString ());
+// 				}
+//
+// 				return _list.OrderByDescending (_line => _line [_sortCol].ToString ());
+// 			}
+//
+// 			return _list;
+// 		}
+//
+//
+// 		private bool NearlyEqual (double _a, double _b, double _epsilon) {
+// 			double absA = Math.Abs (_a);
+// 			double absB = Math.Abs (_b);
+// 			double diff = Math.Abs (_a - _b);
+//
+// 			if (_a == _b) {
+// 				return true;
+// 			}
+//
+// 			if (_a == 0 || _b == 0 || diff < double.Epsilon) {
+// 				return diff < _epsilon;
+// 			}
+//
+// 			return diff / (absA + absB) < _epsilon;
+// 		}
+//
+// 		private enum NumberMatchType {
+// 			Equal,
+// 			Greater,
+// 			GreaterEqual,
+// 			Lesser,
+// 			LesserEqual
+// 		}
+// 	}
+// }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayersLocation.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayersLocation.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayersLocation.cs	(revision 402)
@@ -0,0 +1,61 @@
+// using AllocsFixes.PersistentData;
+// using JetBrains.Annotations;
+//
+// namespace Webserver.WebAPI.APIs {
+// 	[UsedImplicitly]
+// 	public class GetPlayersLocation : AbsWebAPI {
+// 		public override void HandleRequest (RequestContext _context) {
+// 			AdminTools admTools = GameManager.Instance.adminTools;
+// 			PlatformUserIdentifierAbs reqUserId = _context.Connection?.UserId;
+//
+// 			bool listOffline = false;
+// 			if (_context.Request.QueryString ["offline"] != null) {
+// 				bool.TryParse (_context.Request.QueryString ["offline"], out listOffline);
+// 			}
+//
+// 			bool bViewAll = WebConnection.CanViewAllPlayers (_context.PermissionLevel);
+//
+// 			JsonArray playersJsResult = new JsonArray ();
+//
+// 			Players playersList = PersistentContainer.Instance.Players;
+//
+// 			foreach ((PlatformUserIdentifierAbs userId, Player player) in playersList.Dict) {
+// 				if (admTools != null) {
+// 					if (admTools.IsBanned (userId, out _, out _)) {
+// 						continue;
+// 					}
+// 				}
+//
+// 				if (!listOffline && !player.IsOnline) {
+// 					continue;
+// 				}
+//
+// 				if (!bViewAll && !player.PlatformId.Equals (reqUserId)) {
+// 					continue;
+// 				}
+//
+// 				JsonObject pos = new JsonObject ();
+// 				pos.Add ("x", new JsonNumber (player.LastPosition.x));
+// 				pos.Add ("y", new JsonNumber (player.LastPosition.y));
+// 				pos.Add ("z", new JsonNumber (player.LastPosition.z));
+//
+// 				JsonObject pJson = new JsonObject ();
+// 				pJson.Add ("steamid", new JsonString (userId.CombinedString));
+//
+// 				//					pJson.Add("entityid", new JSONNumber (p.EntityID));
+// 				//                    pJson.Add("ip", new JSONString (p.IP));
+// 				pJson.Add ("name", new JsonString (player.Name));
+// 				pJson.Add ("online", new JsonBoolean (player.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);
+// 			}
+//
+// 			WebUtils.WriteJson (_context.Response, playersJsResult);
+// 		}
+// 	}
+// }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayersOnline.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayersOnline.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/GetPlayersOnline.cs	(revision 402)
@@ -0,0 +1,46 @@
+// using AllocsFixes.PersistentData;
+// using JetBrains.Annotations;
+//
+// namespace Webserver.WebAPI.APIs {
+// 	[UsedImplicitly]
+// 	public class GetPlayersOnline : AbsWebAPI {
+// 		public override void HandleRequest (RequestContext _context) {
+// 			JsonArray players = new JsonArray ();
+//
+// 			World w = GameManager.Instance.World;
+// 			foreach ((int entityId, EntityPlayer entityPlayer) in w.Players.dict) {
+// 				ClientInfo ci = ConnectionManager.Instance.Clients.ForEntityId (entityId);
+// 				Player player = PersistentContainer.Instance.Players [ci.InternalId, false];
+//
+// 				JsonObject pos = new JsonObject ();
+// 				pos.Add ("x", new JsonNumber ((int) entityPlayer.GetPosition ().x));
+// 				pos.Add ("y", new JsonNumber ((int) entityPlayer.GetPosition ().y));
+// 				pos.Add ("z", new JsonNumber ((int) entityPlayer.GetPosition ().z));
+//
+// 				JsonObject p = new JsonObject ();
+// 				p.Add ("steamid", new JsonString (ci.PlatformId.CombinedString));
+// 				p.Add ("entityid", new JsonNumber (ci.entityId));
+// 				p.Add ("ip", new JsonString (ci.ip));
+// 				p.Add ("name", new JsonString (entityPlayer.EntityName));
+// 				p.Add ("online", new JsonBoolean (true));
+// 				p.Add ("position", pos);
+//
+// 				p.Add ("level", new JsonNumber (player?.Level ?? -1));
+// 				p.Add ("health", new JsonNumber (entityPlayer.Health));
+// 				p.Add ("stamina", new JsonNumber (entityPlayer.Stamina));
+// 				p.Add ("zombiekills", new JsonNumber (entityPlayer.KilledZombies));
+// 				p.Add ("playerkills", new JsonNumber (entityPlayer.KilledPlayers));
+// 				p.Add ("playerdeaths", new JsonNumber (entityPlayer.Died));
+// 				p.Add ("score", new JsonNumber (entityPlayer.Score));
+//
+// 				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);
+// 			}
+//
+// 			WebUtils.WriteJson (_context.Response, players);
+// 		}
+// 	}
+// }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/Hostile.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/Hostile.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/Hostile.cs	(revision 402)
@@ -0,0 +1,47 @@
+﻿using System.Collections.Generic;
+using JetBrains.Annotations;
+using Utf8Json;
+using Webserver.LiveData;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	internal class Hostile : AbsRestApi {
+		private readonly List<EntityEnemy> entities = new List<EntityEnemy> ();
+
+		private static readonly byte[] jsonKeyId = JsonWriter.GetEncodedPropertyNameWithBeginObject ("id");
+		private static readonly byte[] jsonKeyName = JsonWriter.GetEncodedPropertyNameWithBeginObject ("name");
+		private static readonly byte[] jsonKeyPosition = JsonWriter.GetEncodedPropertyNameWithBeginObject ("position");
+
+		protected override void HandleRestGet (RequestContext _context) {
+			PrepareEnvelopedResult (out JsonWriter writer);
+			writer.WriteBeginArray ();
+			
+			lock (entities) {
+				Hostiles.Instance.Get (entities);
+				
+				for (int i = 0; i < entities.Count; i++) {
+					if (i > 0) {
+						writer.WriteValueSeparator ();
+					}
+					
+					EntityAlive entity = entities [i];
+					Vector3i position = new Vector3i (entity.GetPosition ());
+					
+					writer.WriteRaw (jsonKeyId);
+					writer.WriteInt32 (entity.entityId);
+					
+					writer.WriteRaw (jsonKeyName);
+					writer.WriteString (!string.IsNullOrEmpty (entity.EntityName) ? entity.EntityName : $"enemy class #{entity.entityClass}");
+					
+					writer.WriteRaw (jsonKeyPosition);
+					JsonCommons.WritePositionObject (writer, position);
+
+					writer.WriteEndObject ();
+				}
+			}
+			
+			writer.WriteEndArray ();
+			SendEnvelopedResult (_context, ref writer);
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/Log.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/Log.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/Log.cs	(revision 402)
@@ -0,0 +1,87 @@
+using System.Collections.Generic;
+using JetBrains.Annotations;
+using Utf8Json;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	public class Log : AbsRestApi {
+		private const int maxCount = 1000;
+
+		private static readonly byte[] jsonKeyEntries = JsonWriter.GetEncodedPropertyNameWithBeginObject ("entries");
+		private static readonly byte[] jsonKeyFirstLine = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("firstLine");
+		private static readonly byte[] jsonKeyLastLine = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("lastLine");
+
+		private static readonly byte[] jsonMsgKey = JsonWriter.GetEncodedPropertyNameWithBeginObject ("msg");
+		private static readonly byte[] jsonTypeKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("type");
+		private static readonly byte[] jsonTraceKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("trace");
+		private static readonly byte[] jsonIsotimeKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("isotime");
+		private static readonly byte[] jsonUptimeKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("uptime");
+		
+		protected override void HandleRestGet (RequestContext _context) {
+			if (_context.Request.QueryString ["count"] == null || !int.TryParse (_context.Request.QueryString ["count"], out int count)) {
+				count = 50;
+			}
+
+			if (count == 0) {
+				count = 1;
+			}
+
+			if (count > maxCount) {
+				count = maxCount;
+			}
+
+			if (count < -maxCount) {
+				count = -maxCount;
+			}
+
+			if (_context.Request.QueryString ["firstLine"] == null || !int.TryParse (_context.Request.QueryString ["firstLine"], out int firstLine)) {
+				firstLine = count > 0 ? LogBuffer.Instance.OldestLine : LogBuffer.Instance.LatestLine;
+			}
+			
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.WriteRaw (jsonKeyEntries);
+			
+			List<LogBuffer.LogEntry> logEntries = LogBuffer.Instance.GetRange (ref firstLine, count, out int lastLine);
+
+			writer.WriteBeginArray ();
+
+			bool first = true;
+			foreach (LogBuffer.LogEntry logEntry in logEntries) {
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+
+				first = false;
+				
+				writer.WriteRaw (jsonMsgKey);
+				writer.WriteString (logEntry.message);
+				
+				writer.WriteRaw (jsonTypeKey);
+				writer.WriteString (logEntry.type.ToStringCached ());
+				
+				writer.WriteRaw (jsonTraceKey);
+				writer.WriteString (logEntry.trace);
+				
+				writer.WriteRaw (jsonIsotimeKey);
+				writer.WriteString (logEntry.isoTime);
+				
+				writer.WriteRaw (jsonUptimeKey);
+				writer.WriteString (logEntry.uptime.ToString ());
+				
+				writer.WriteEndObject ();
+			}
+			writer.WriteEndArray ();
+
+			writer.WriteRaw (jsonKeyFirstLine);
+			writer.WriteInt32 (firstLine);
+			
+			writer.WriteRaw (jsonKeyLastLine);
+			writer.WriteInt32 (lastLine);
+			
+			writer.WriteEndObject ();
+
+			SendEnvelopedResult (_context, ref writer);
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/ServerInfo.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/ServerInfo.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/ServerInfo.cs	(revision 402)
@@ -0,0 +1,103 @@
+using JetBrains.Annotations;
+using Utf8Json;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	public class ServerInfo : AbsRestApi {
+		private static readonly UnityEngine.Profiling.CustomSampler buildSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_ServerInfo_BuildSampler");
+
+		private static readonly byte[] keyType = JsonWriter.GetEncodedPropertyNameWithBeginObject ("type");
+		private static readonly byte[] keyValue = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("value");
+
+		private int largestBuffer;
+
+		protected override void HandleRestGet (RequestContext _context) {
+			buildSampler.Begin ();
+
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.EnsureCapacity (largestBuffer);
+			writer.WriteBeginObject ();
+			
+			GameServerInfo gsi = ConnectionManager.Instance.LocalServerInfo;
+
+			bool first = true;
+			
+			
+
+			foreach (GameInfoString stringGamePref in EnumUtils.Values<GameInfoString> ()) {
+				string value = gsi.GetValue (stringGamePref);
+
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+
+				first = false;
+				
+				writer.WriteString (stringGamePref.ToStringCached ());
+				writer.WriteNameSeparator ();
+				
+				writer.WriteRaw (keyType);
+				writer.WriteString ("string");
+				
+				writer.WriteRaw (keyValue);
+				writer.WriteString (value);
+				
+				writer.WriteEndObject ();
+			}
+
+			foreach (GameInfoInt intGamePref in EnumUtils.Values<GameInfoInt> ()) {
+				int value = gsi.GetValue (intGamePref);
+
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+
+				first = false;
+				
+				writer.WriteString (intGamePref.ToStringCached ());
+				writer.WriteNameSeparator ();
+				
+				writer.WriteRaw (keyType);
+				writer.WriteString ("int");
+				
+				writer.WriteRaw (keyValue);
+				writer.WriteInt32 (value);
+				
+				writer.WriteEndObject ();
+			}
+
+			foreach (GameInfoBool boolGamePref in EnumUtils.Values<GameInfoBool> ()) {
+				bool value = gsi.GetValue (boolGamePref);
+
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+
+				first = false;
+				
+				writer.WriteString (boolGamePref.ToStringCached ());
+				writer.WriteNameSeparator ();
+				
+				writer.WriteRaw (keyType);
+				writer.WriteString ("bool");
+				
+				writer.WriteRaw (keyValue);
+				writer.WriteBoolean (value);
+				
+				writer.WriteEndObject ();
+			}
+			
+			writer.WriteEndObject ();
+			
+			buildSampler.End ();
+
+			int bufferContentSize = writer.CurrentOffset + 128;
+			if (bufferContentSize > largestBuffer) {
+				largestBuffer = bufferContentSize;
+			}
+			
+			SendEnvelopedResult (_context, ref writer);
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/ServerStats.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/ServerStats.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/ServerStats.cs	(revision 402)
@@ -0,0 +1,53 @@
+using JetBrains.Annotations;
+using Utf8Json;
+using Webserver.LiveData;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	public class ServerStats : AbsRestApi {
+		private static readonly byte[] jsonKeyGameTime = JsonWriter.GetEncodedPropertyNameWithBeginObject ("gameTime");
+		private static readonly byte[] jsonKeyPlayers = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("players");
+		private static readonly byte[] jsonKeyHostiles = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("hostiles");
+		private static readonly byte[] jsonKeyAnimals = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("animals");
+		
+		private static readonly byte[] jsonKeyDays = JsonWriter.GetEncodedPropertyNameWithBeginObject ("days");
+		private static readonly byte[] jsonKeyHours = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("hours");
+		private static readonly byte[] jsonKeyMinutes = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("minutes");
+
+		protected override void HandleRestGet (RequestContext _context) {
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.WriteRaw (jsonKeyGameTime);
+
+			(int days, int hours, int minutes) = GameUtils.WorldTimeToElements (GameManager.Instance.World.worldTime);
+			
+			writer.WriteRaw (jsonKeyDays);
+			writer.WriteInt32 (days);
+			
+			writer.WriteRaw (jsonKeyHours);
+			writer.WriteInt32 (hours);
+			
+			writer.WriteRaw (jsonKeyMinutes);
+			writer.WriteInt32 (minutes);
+			
+			writer.WriteEndObject ();
+
+			writer.WriteRaw (jsonKeyPlayers);
+			writer.WriteInt32 (GameManager.Instance.World.Players.Count);
+			
+			writer.WriteRaw (jsonKeyHostiles);
+			writer.WriteInt32 (Hostiles.Instance.GetCount ());
+			
+			writer.WriteRaw (jsonKeyAnimals);
+			writer.WriteInt32 (Animals.Instance.GetCount ());
+			
+			writer.WriteEndObject ();
+
+			SendEnvelopedResult (_context, ref writer);
+		}
+
+		public override int DefaultPermissionLevel () {
+			return 2000;
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/WebMods.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/WebMods.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/WebMods.cs	(revision 402)
@@ -0,0 +1,60 @@
+using JetBrains.Annotations;
+using Utf8Json;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	public class WebMods : AbsRestApi {
+		private readonly byte[] loadedWebMods;
+
+		public WebMods (Web _parent) {
+			JsonWriter writer = new JsonWriter ();
+			writer.WriteBeginArray ();
+
+			bool first = true;
+			foreach (WebMod webMod in _parent.webMods) {
+				if (!first) {
+					writer.WriteValueSeparator ();
+				}
+				first = false;
+				
+				writer.WriteBeginObject ();
+				
+				writer.WriteString ("name");
+				writer.WriteNameSeparator ();
+				writer.WriteString (webMod.ParentMod.Name);
+
+				string webModReactBundle = webMod.ReactBundle;
+				if (webModReactBundle != null) {
+					writer.WriteValueSeparator ();
+					writer.WriteString ("bundle");
+					writer.WriteNameSeparator ();
+					writer.WriteString (webModReactBundle);
+				}
+				
+				string webModCssFile = webMod.CssPath;
+				if (webModCssFile != null) {
+					writer.WriteValueSeparator ();
+					writer.WriteString ("css");
+					writer.WriteNameSeparator ();
+					writer.WriteString (webModCssFile);
+				}
+				
+				writer.WriteEndObject ();
+			}
+			
+			writer.WriteEndArray ();
+
+			loadedWebMods = writer.ToUtf8ByteArray ();
+		}
+
+		protected override void HandleRestGet (RequestContext _context) {
+			PrepareEnvelopedResult (out JsonWriter writer);
+			writer.WriteRaw (loadedWebMods);
+			SendEnvelopedResult (_context, ref writer);
+		}
+
+		public override int DefaultPermissionLevel () {
+			return 2000;
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/WebUiUpdates.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/WebUiUpdates.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/WebUiUpdates.cs	(revision 402)
@@ -0,0 +1,63 @@
+using JetBrains.Annotations;
+using Utf8Json;
+using Webserver.LiveData;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	public class WebUiUpdates : AbsRestApi {
+		private static readonly byte[] jsonKeyGameTime = JsonWriter.GetEncodedPropertyNameWithBeginObject ("gameTime");
+		private static readonly byte[] jsonKeyPlayers = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("players");
+		private static readonly byte[] jsonKeyHostiles = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("hostiles");
+		private static readonly byte[] jsonKeyAnimals = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("animals");
+		private static readonly byte[] jsonKeyNewLogs = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("newLogs");
+		
+		private static readonly byte[] jsonKeyDays = JsonWriter.GetEncodedPropertyNameWithBeginObject ("days");
+		private static readonly byte[] jsonKeyHours = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("hours");
+		private static readonly byte[] jsonKeyMinutes = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("minutes");
+
+		
+		protected override void HandleRestGet (RequestContext _context) {
+			if (_context.Request.QueryString ["latestLine"] == null ||
+			    !int.TryParse (_context.Request.QueryString ["latestLine"], out int latestLine)) {
+				latestLine = 0;
+			}
+			
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.WriteRaw (jsonKeyGameTime);
+
+			(int days, int hours, int minutes) = GameUtils.WorldTimeToElements (GameManager.Instance.World.worldTime);
+			
+			writer.WriteRaw (jsonKeyDays);
+			writer.WriteInt32 (days);
+			
+			writer.WriteRaw (jsonKeyHours);
+			writer.WriteInt32 (hours);
+			
+			writer.WriteRaw (jsonKeyMinutes);
+			writer.WriteInt32 (minutes);
+			
+			writer.WriteEndObject ();
+
+			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 ();
+
+			SendEnvelopedResult (_context, ref writer);
+		}
+
+		public override int DefaultPermissionLevel () {
+			return 2000;
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/AbsRestApi.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/AbsRestApi.cs	(revision 401)
+++ binary-improvements2/WebServer/src/WebAPI/AbsRestApi.cs	(revision 402)
@@ -1,6 +1,7 @@
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Net;
-using AllocsFixes.JSON;
+using Utf8Json;
 
 namespace Webserver.WebAPI {
@@ -9,20 +10,28 @@
 
 		public sealed override void HandleRequest (RequestContext _context) {
-			JsonNode jsonBody = null;
+			IDictionary<string, object> inputJson = null;
+			byte[] jsonInputData = null;
+			
+			if (_context.Request.HasEntityBody) {
+				Stream requestInputStream = _context.Request.InputStream;
+				
+				jsonInputData = new byte[_context.Request.ContentLength64];
+				requestInputStream.Read (jsonInputData, 0, (int)_context.Request.ContentLength64);
+				
+				try {
+					jsonDeserializeSampler.Begin ();
+					inputJson = JsonSerializer.Deserialize<IDictionary<string, object>> (jsonInputData);
+					
+					// Log.Out ("JSON body:");
+					// foreach ((string key, object value) in inputJson) {
+					// 	Log.Out ($" - {key} = {value} ({value.GetType ()})");
+					// }
+					
+					jsonDeserializeSampler.End ();
+				} catch (Exception e) {
+					jsonDeserializeSampler.End ();
 
-			if (_context.Request.HasEntityBody) {
-				string body = new StreamReader (_context.Request.InputStream).ReadToEnd ();
-
-				if (!string.IsNullOrEmpty (body)) {
-					try {
-						jsonDeserializeSampler.Begin ();
-						jsonBody = Parser.Parse (body);
-						jsonDeserializeSampler.End ();
-					} catch (Exception e) {
-						jsonDeserializeSampler.End ();
-
-						SendEnvelopedResult (_context, null, HttpStatusCode.BadRequest, null, "INVALID_BODY", e);
-						return;
-					}
+					SendErrorResult (_context, HttpStatusCode.BadRequest, null, "INVALID_BODY", e);
+					return;
 				}
 			}
@@ -31,6 +40,6 @@
 				switch (_context.Request.HttpMethod) {
 					case "GET":
-						if (jsonBody != null) {
-							SendEnvelopedResult (_context, null, HttpStatusCode.BadRequest, jsonBody, "GET_WITH_BODY");
+						if (inputJson != null) {
+							SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "GET_WITH_BODY");
 							return;
 						}
@@ -40,36 +49,36 @@
 					case "POST":
 						if (!string.IsNullOrEmpty (_context.RequestPath)) {
-							SendEnvelopedResult (_context, null, HttpStatusCode.BadRequest, jsonBody, "POST_WITH_ID");
+							SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "POST_WITH_ID");
 							return;
 						}
 
-						if (jsonBody == null) {
-							SendEnvelopedResult (_context, null, HttpStatusCode.BadRequest, null, "POST_WITHOUT_BODY");
+						if (inputJson == null) {
+							SendErrorResult (_context, HttpStatusCode.BadRequest, null, "POST_WITHOUT_BODY");
 							return;
 						}
 
-						HandleRestPost (_context, jsonBody);
+						HandleRestPost (_context, inputJson, jsonInputData);
 						return;
 					case "PUT":
 						if (string.IsNullOrEmpty (_context.RequestPath)) {
-							SendEnvelopedResult (_context, null, HttpStatusCode.BadRequest, jsonBody, "PUT_WITHOUT_ID");
+							SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "PUT_WITHOUT_ID");
 							return;
 						}
 
-						if (jsonBody == null) {
-							SendEnvelopedResult (_context, null, HttpStatusCode.BadRequest, null, "PUT_WITHOUT_BODY");
+						if (inputJson == null) {
+							SendErrorResult (_context, HttpStatusCode.BadRequest, null, "PUT_WITHOUT_BODY");
 							return;
 						}
 
-						HandleRestPut (_context, jsonBody);
+						HandleRestPut (_context, inputJson, jsonInputData);
 						return;
 					case "DELETE":
 						if (string.IsNullOrEmpty (_context.RequestPath)) {
-							SendEnvelopedResult (_context, null, HttpStatusCode.BadRequest, jsonBody, "DELETE_WITHOUT_ID");
+							SendErrorResult (_context, HttpStatusCode.BadRequest, jsonInputData, "DELETE_WITHOUT_ID");
 							return;
 						}
 
-						if (jsonBody != null) {
-							SendEnvelopedResult (_context, null, HttpStatusCode.BadRequest, null, "DELETE_WITH_BODY");
+						if (inputJson != null) {
+							SendErrorResult (_context, HttpStatusCode.BadRequest, null, "DELETE_WITH_BODY");
 							return;
 						}
@@ -78,50 +87,50 @@
 						return;
 					default:
-						SendEnvelopedResult (_context, null, HttpStatusCode.BadRequest, null, "INVALID_METHOD");
+						SendErrorResult (_context, HttpStatusCode.BadRequest, null, "INVALID_METHOD");
 						return;
 				}
 			} catch (Exception e) {
-				SendEnvelopedResult (_context, null, HttpStatusCode.InternalServerError, jsonBody, "ERROR_PROCESSING", e);
+				SendErrorResult (_context, HttpStatusCode.InternalServerError, jsonInputData, "ERROR_PROCESSING", e);
 			}
 		}
 
-		private static readonly JsonArray emptyData = new JsonArray ();
-
-		protected void SendEnvelopedResult (RequestContext _context, JsonNode _resultData, HttpStatusCode _statusCode = HttpStatusCode.OK,
-			JsonNode _jsonInputBody = null, string _errorCode = null, Exception _exception = null) {
-			JsonObject meta = new JsonObject ();
-
-			meta.Add ("serverTime", new JsonString (DateTime.Now.ToString ("o")));
-			if (!string.IsNullOrEmpty (_errorCode)) {
-				meta.Add ("requestMethod", new JsonString (_context.Request.HttpMethod));
-				meta.Add ("requestSubpath", new JsonString (_context.RequestPath));
-				meta.Add ("requestBody", new JsonString (_jsonInputBody?.ToString () ?? "-empty-"));
-				meta.Add ("errorCode", new JsonString (_errorCode));
-				if (_exception != null) {
-					meta.Add ("exceptionMessage", new JsonString (_exception.Message));
-					meta.Add ("exceptionTrace", new JsonString (_exception.StackTrace));
-				}
-			}
-
-			JsonObject envelope = new JsonObject ();
-			envelope.Add ("meta", meta);
-			envelope.Add ("data", _resultData ?? emptyData);
-
-			WebUtils.WriteJson (_context.Response, envelope, _statusCode);
+		protected void SendErrorResult (RequestContext _context, HttpStatusCode _statusCode, byte[] _jsonInputData = null, string _errorCode = null, Exception _exception = null) {
+			PrepareEnvelopedResult (out JsonWriter writer);
+			writer.WriteRaw (JsonEmptyData);
+			SendEnvelopedResult (_context, ref writer, _statusCode, _jsonInputData, _errorCode, _exception);
 		}
 
-		protected bool TryGetJsonField (JsonObject _jsonObject, string _fieldName, out int _value) {
+		static AbsRestApi () {
+			JsonWriter writer = new JsonWriter ();
+			writer.WriteBeginArray ();
+			writer.WriteEndArray ();
+			JsonEmptyData = writer.ToUtf8ByteArray ();
+		}
+
+		protected static readonly byte[] JsonEmptyData;
+		
+		protected void PrepareEnvelopedResult (out JsonWriter _writer) {
+			WebUtils.PrepareEnvelopedResult (out _writer);
+		}
+
+		protected void SendEnvelopedResult (RequestContext _context, ref JsonWriter _writer, HttpStatusCode _statusCode = HttpStatusCode.OK,
+			byte[] _jsonInputData = null, string _errorCode = null, Exception _exception = null) {
+			
+			WebUtils.SendEnvelopedResult (_context, ref _writer, _statusCode, _jsonInputData, _errorCode, _exception);
+		}
+
+		protected bool TryGetJsonField (IDictionary<string, object> _jsonObject, string _fieldName, out int _value) {
 			_value = default;
 			
-			if (!_jsonObject.TryGetValue (_fieldName, out JsonNode fieldNode)) {
+			if (!_jsonObject.TryGetValue (_fieldName, out object fieldNode)) {
 				return false;
 			}
 
-			if (!(fieldNode is JsonValue valueField)) {
+			if (fieldNode is not double value) {
 				return false;
 			}
 
 			try {
-				_value = valueField.AsInt;
+				_value = (int)value;
 				return true;
 			} catch (Exception) {
@@ -130,17 +139,17 @@
 		}
 
-		protected bool TryGetJsonField (JsonObject _jsonObject, string _fieldName, out double _value) {
+		protected bool TryGetJsonField (IDictionary<string, object> _jsonObject, string _fieldName, out double _value) {
 			_value = default;
 			
-			if (!_jsonObject.TryGetValue (_fieldName, out JsonNode fieldNode)) {
+			if (!_jsonObject.TryGetValue (_fieldName, out object fieldNode)) {
 				return false;
 			}
 
-			if (!(fieldNode is JsonValue valueField)) {
+			if (fieldNode is not double value) {
 				return false;
 			}
 
 			try {
-				_value = valueField.AsDouble;
+				_value = value;
 				return true;
 			} catch (Exception) {
@@ -149,17 +158,17 @@
 		}
 
-		protected bool TryGetJsonField (JsonObject _jsonObject, string _fieldName, out string _value) {
+		protected bool TryGetJsonField (IDictionary<string, object> _jsonObject, string _fieldName, out string _value) {
 			_value = default;
 			
-			if (!_jsonObject.TryGetValue (_fieldName, out JsonNode fieldNode)) {
+			if (!_jsonObject.TryGetValue (_fieldName, out object fieldNode)) {
 				return false;
 			}
 
-			if (!(fieldNode is JsonValue valueField)) {
+			if (fieldNode is not string value) {
 				return false;
 			}
 
 			try {
-				_value = valueField.AsString;
+				_value = value;
 				return true;
 			} catch (Exception) {
@@ -168,11 +177,19 @@
 		}
 
-		protected abstract void HandleRestGet (RequestContext _context);
+		protected virtual void HandleRestGet (RequestContext _context) {
+			SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, null, "Unsupported");
+		}
 
-		protected abstract void HandleRestPost (RequestContext _context, JsonNode _jsonBody);
+		protected virtual void HandleRestPost (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
+			SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, _jsonInputData, "Unsupported");
+		}
 
-		protected abstract void HandleRestPut (RequestContext _context, JsonNode _jsonBody);
+		protected virtual void HandleRestPut (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
+			SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, _jsonInputData, "Unsupported");
+		}
 
-		protected abstract void HandleRestDelete (RequestContext _context);
+		protected virtual void HandleRestDelete (RequestContext _context) {
+			SendErrorResult (_context, HttpStatusCode.MethodNotAllowed, null, "Unsupported");
+		}
 	}
 }
Index: binary-improvements2/WebServer/src/WebAPI/ExecuteConsoleCommand.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/ExecuteConsoleCommand.cs	(revision 401)
+++ 	(revision )
@@ -1,52 +1,0 @@
-using System;
-using System.Net;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class ExecuteConsoleCommand : AbsWebAPI {
-		public override void HandleRequest (RequestContext _context) {
-			if (string.IsNullOrEmpty (_context.Request.QueryString ["command"])) {
-				WebUtils.WriteText (_context.Response, "No command given", HttpStatusCode.BadRequest);
-				return;
-			}
-
-			WebCommandResult.ResultType responseType = WebCommandResult.ResultType.Full;
-
-			string formatArg = _context.Request.QueryString ["format"];
-			if (formatArg != null) {
-				if (formatArg.EqualsCaseInsensitive ("raw")) {
-					responseType = WebCommandResult.ResultType.Raw;
-				} else if (formatArg.EqualsCaseInsensitive ("simple")) {
-					responseType = WebCommandResult.ResultType.ResultOnly;
-				}
-			}
-			
-			string commandline = _context.Request.QueryString ["command"];
-			string commandPart = commandline.Split (' ') [0];
-			string argumentsPart = commandline.Substring (Math.Min (commandline.Length, commandPart.Length + 1));
-
-			IConsoleCommand command = SdtdConsole.Instance.GetCommand (commandline);
-
-			if (command == null) {
-				WebUtils.WriteText (_context.Response, "Unknown command", HttpStatusCode.NotFound);
-				return;
-			}
-
-			int commandPermissionLevel = GameManager.Instance.adminTools.GetCommandPermissionLevel (command.GetCommands ());
-
-			if (_context.PermissionLevel > commandPermissionLevel) {
-				WebUtils.WriteText (_context.Response, "You are not allowed to execute this command", HttpStatusCode.Forbidden);
-				return;
-			}
-
-			_context.Response.SendChunked = true;
-			WebCommandResult wcr = new WebCommandResult (commandPart, argumentsPart, responseType, _context.Response);
-			SdtdConsole.Instance.ExecuteAsync (commandline, wcr);
-		}
-
-		public override int DefaultPermissionLevel () {
-			return 2000;
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetAllowedCommands.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetAllowedCommands.cs	(revision 401)
+++ 	(revision )
@@ -1,43 +1,0 @@
-using AllocsFixes.JSON;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetAllowedCommands : AbsWebAPI {
-		public override void HandleRequest (RequestContext _context) {
-			JsonObject result = new JsonObject ();
-			JsonArray entries = new JsonArray ();
-			foreach (IConsoleCommand cc in SdtdConsole.Instance.GetCommands ()) {
-				int commandPermissionLevel = GameManager.Instance.adminTools.GetCommandPermissionLevel (cc.GetCommands ());
-				if (_context.PermissionLevel > commandPermissionLevel) {
-					continue;
-				}
-
-				JsonArray cmdOverloads = new JsonArray ();
-
-				string cmd = string.Empty;
-				foreach (string s in cc.GetCommands ()) {
-					cmdOverloads.Add (new JsonString (s));
-					if (s.Length > cmd.Length) {
-						cmd = s;
-					}
-				}
-
-				JsonObject cmdObj = new JsonObject ();
-				cmdObj.Add ("command", new JsonString (cmd));
-				cmdObj.Add ("overloads", cmdOverloads);
-				cmdObj.Add ("description", new JsonString (cc.GetDescription ()));
-				cmdObj.Add ("help", new JsonString (cc.GetHelp ()));
-				entries.Add (cmdObj);
-			}
-
-			result.Add ("commands", entries);
-
-			WebUtils.WriteJson (_context.Response, result);
-		}
-
-		public override int DefaultPermissionLevel () {
-			return 2000;
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetAnimalsLocation.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetAnimalsLocation.cs	(revision 401)
+++ 	(revision )
@@ -1,41 +1,0 @@
-﻿using System.Collections.Generic;
-using AllocsFixes.JSON;
-using AllocsFixes.LiveData;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	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);
-			}
-
-			WebUtils.WriteJson (_context.Response, animalsJsResult);
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetHostileLocation.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetHostileLocation.cs	(revision 401)
+++ 	(revision )
@@ -1,41 +1,0 @@
-﻿using System.Collections.Generic;
-using AllocsFixes.JSON;
-using AllocsFixes.LiveData;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	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);
-			}
-
-			WebUtils.WriteJson (_context.Response, hostilesJsResult);
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetLandClaims.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetLandClaims.cs	(revision 401)
+++ 	(revision )
@@ -1,78 +1,0 @@
-using System.Collections.Generic;
-using System.Net;
-using AllocsFixes;
-using AllocsFixes.JSON;
-using AllocsFixes.PersistentData;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetLandClaims : AbsWebAPI {
-		public override void HandleRequest (RequestContext _context) {
-			PlatformUserIdentifierAbs requestedUserId = null;
-			if (_context.Request.QueryString ["userid"] != null) {
-				if (!PlatformUserIdentifierAbs.TryFromCombinedString (_context.Request.QueryString ["userid"], out requestedUserId)) {
-					WebUtils.WriteText (_context.Response, "Invalid user id given", HttpStatusCode.BadRequest);
-					return;
-				}
-			}
-
-			// default user, cheap way to avoid 'null reference exception'
-			PlatformUserIdentifierAbs userId = _context.Connection?.UserId;
-
-			bool bViewAll = WebConnection.CanViewAllClaims (_context.PermissionLevel);
-
-			JsonObject result = new JsonObject ();
-			result.Add ("claimsize", new JsonNumber (GamePrefs.GetInt (EnumUtils.Parse<EnumGamePrefs> ("LandClaimSize"))));
-
-			JsonArray claimOwners = new JsonArray ();
-			result.Add ("claimowners", claimOwners);
-
-			LandClaimList.OwnerFilter[] ownerFilters = null;
-			if (requestedUserId != null || !bViewAll) {
-				if (requestedUserId != null && !bViewAll) {
-					ownerFilters = new[] {
-						LandClaimList.UserIdFilter (userId),
-						LandClaimList.UserIdFilter (requestedUserId)
-					};
-				} else if (!bViewAll) {
-					ownerFilters = new[] {LandClaimList.UserIdFilter (userId)};
-				} else {
-					ownerFilters = new[] {LandClaimList.UserIdFilter (requestedUserId)};
-				}
-			}
-
-			LandClaimList.PositionFilter[] posFilters = null;
-
-			Dictionary<Player, List<Vector3i>> claims = LandClaimList.GetLandClaims (ownerFilters, posFilters);
-
-			foreach ((Player player, List<Vector3i> claimPositions) in claims) {
-				JsonObject owner = new JsonObject ();
-				claimOwners.Add (owner);
-
-				owner.Add ("steamid", new JsonString (player.PlatformId.CombinedString));
-				owner.Add ("claimactive", new JsonBoolean (player.LandProtectionActive));
-
-				if (player.Name.Length > 0) {
-					owner.Add ("playername", new JsonString (player.Name));
-				} else {
-					owner.Add ("playername", new JsonNull ());
-				}
-
-				JsonArray claimsJson = new JsonArray ();
-				owner.Add ("claims", claimsJson);
-
-				foreach (Vector3i v in claimPositions) {
-					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));
-
-					claimsJson.Add (claim);
-				}
-			}
-
-			WebUtils.WriteJson (_context.Response, result);
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetLog.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetLog.cs	(revision 401)
+++ 	(revision )
@@ -1,53 +1,0 @@
-using System.Collections.Generic;
-using AllocsFixes.JSON;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetLog : AbsWebAPI {
-		private const int MAX_COUNT = 1000;
-		
-		public override void HandleRequest (RequestContext _context) {
-			if (_context.Request.QueryString ["count"] == null || !int.TryParse (_context.Request.QueryString ["count"], out int count)) {
-				count = 50;
-			}
-
-			if (count == 0) {
-				count = 1;
-			}
-
-			if (count > MAX_COUNT) {
-				count = MAX_COUNT;
-			}
-
-			if (count < -MAX_COUNT) {
-				count = -MAX_COUNT;
-			}
-
-			if (_context.Request.QueryString ["firstLine"] == null || !int.TryParse (_context.Request.QueryString ["firstLine"], out int firstLine)) {
-				firstLine = count > 0 ? LogBuffer.Instance.OldestLine : LogBuffer.Instance.LatestLine;
-			}
-
-			JsonObject result = new JsonObject ();
-
-			List<LogBuffer.LogEntry> logEntries = LogBuffer.Instance.GetRange (ref firstLine, count, out int lastLine);
-
-			JsonArray entries = new JsonArray ();
-			foreach (LogBuffer.LogEntry logEntry in logEntries) {
-				JsonObject entry = new JsonObject ();
-				entry.Add ("isotime", new JsonString (logEntry.isoTime));
-				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);
-			}
-
-			result.Add ("firstLine", new JsonNumber (firstLine));
-			result.Add ("lastLine", new JsonNumber (lastLine));
-			result.Add ("entries", entries);
-
-			WebUtils.WriteJson (_context.Response, result);
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetPlayerInventories.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetPlayerInventories.cs	(revision 401)
+++ 	(revision )
@@ -1,27 +1,0 @@
-using System.Collections.Generic;
-using AllocsFixes.JSON;
-using AllocsFixes.PersistentData;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetPlayerInventories : AbsWebAPI {
-		public override void HandleRequest (RequestContext _context) {
-			GetPlayerInventory.GetInventoryArguments (_context.Request, out bool showIconColor, out bool showIconName);
-
-			JsonArray allInventoriesResult = new JsonArray ();
-
-			foreach ((PlatformUserIdentifierAbs userId, Player player) in PersistentContainer.Instance.Players.Dict) {
-				if (player == null) {
-					continue;
-				}
-
-				if (player.IsOnline) {
-					allInventoriesResult.Add (GetPlayerInventory.DoPlayer (userId.CombinedString, player, showIconColor, showIconName));
-				}
-			}
-
-			WebUtils.WriteJson (_context.Response, allInventoriesResult);
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetPlayerInventory.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetPlayerInventory.cs	(revision 401)
+++ 	(revision )
@@ -1,129 +1,0 @@
-using System.Collections.Generic;
-using System.Net;
-using AllocsFixes.JSON;
-using AllocsFixes.PersistentData;
-using JetBrains.Annotations;
-using HttpListenerRequest = SpaceWizards.HttpListener.HttpListenerRequest;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetPlayerInventory : AbsWebAPI {
-		public override void HandleRequest (RequestContext _context) {
-			if (_context.Request.QueryString ["userid"] == null) {
-				WebUtils.WriteText (_context.Response, "No user id given", HttpStatusCode.BadRequest);
-				return;
-			}
-
-			string userIdString = _context.Request.QueryString ["userid"];
-			if (!PlatformUserIdentifierAbs.TryFromCombinedString (userIdString, out PlatformUserIdentifierAbs userId)) {
-				WebUtils.WriteText (_context.Response, "Invalid user id given", HttpStatusCode.BadRequest);
-				return;
-			}
-
-			Player p = PersistentContainer.Instance.Players [userId, false];
-			if (p == null) {
-				WebUtils.WriteText (_context.Response, "Unknown user id given", HttpStatusCode.NotFound);
-				return;
-			}
-
-			GetInventoryArguments (_context.Request, out bool showIconColor, out bool showIconName);
-
-			JsonObject result = DoPlayer (userIdString, p, showIconColor, showIconName);
-
-			WebUtils.WriteJson (_context.Response, result);
-		}
-
-		internal static void GetInventoryArguments (HttpListenerRequest _req, out bool _showIconColor, out bool _showIconName) {
-			if (_req.QueryString ["showiconcolor"] == null || !bool.TryParse (_req.QueryString ["showiconcolor"], out _showIconColor)) {
-				_showIconColor = true;
-			}
-			
-			if (_req.QueryString ["showiconname"] == null || !bool.TryParse (_req.QueryString ["showiconname"], out _showIconName)) {
-				_showIconName = true;
-			}
-		}
-
-		internal static JsonObject DoPlayer (string _steamId, Player _player, bool _showIconColor, bool _showIconName) {
-			AllocsFixes.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 (_steamId));
-			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;
-		}
-
-		private static void DoInventory (JsonArray _jsonRes, List<InvItem> _inv, bool _showIconColor, bool _showIconName) {
-			for (int i = 0; i < _inv.Count; i++) {
-				_jsonRes.Add (GetJsonForItem (_inv [i], _showIconColor, _showIconName));
-			}
-		}
-
-		private static void AddEquipment (JsonObject _eq, string _slotname, InvItem[] _items, EquipmentSlots _slot, bool _showIconColor, bool _showIconName) {
-			int[] slotindices = XUiM_PlayerEquipment.GetSlotIndicesByEquipmentSlot (_slot);
-
-			for (int i = 0; i < slotindices.Length; i++) {
-				if (_items? [slotindices [i]] == null) {
-					continue;
-				}
-
-				InvItem item = _items [slotindices [i]];
-				_eq.Add (_slotname, GetJsonForItem (item, _showIconColor, _showIconName));
-				return;
-			}
-
-			_eq.Add (_slotname, new JsonNull ());
-		}
-
-		private static JsonNode GetJsonForItem (InvItem _item, bool _showIconColor, bool _showIconName) {
-			if (_item == null) {
-				return new JsonNull ();
-			}
-
-			JsonObject jsonItem = new JsonObject ();
-			jsonItem.Add ("count", new JsonNumber (_item.count));
-			jsonItem.Add ("name", new JsonString (_item.itemName));
-			
-			if (_showIconName) {
-				jsonItem.Add ("icon", new JsonString (_item.icon));
-			}
-
-			if (_showIconColor) {
-				jsonItem.Add ("iconcolor", new JsonString (_item.iconcolor));
-			}
-
-			jsonItem.Add ("quality", new JsonNumber (_item.quality));
-			if (_item.quality >= 0) {
-				jsonItem.Add ("qualitycolor", new JsonString (QualityInfo.GetQualityColorHex (_item.quality)));
-			}
-
-			return jsonItem;
-
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetPlayerList.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetPlayerList.cs	(revision 401)
+++ 	(revision )
@@ -1,264 +1,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text.RegularExpressions;
-using AllocsFixes.JSON;
-using AllocsFixes.PersistentData;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetPlayerList : AbsWebAPI {
-		private static readonly Regex numberFilterMatcher =
-			new Regex (@"^(>=|=>|>|<=|=<|<|==|=)?\s*([0-9]+(\.[0-9]*)?)$");
-
-		private static readonly UnityEngine.Profiling.CustomSampler jsonSerializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Build");
-
-		public override void HandleRequest (RequestContext _context) {
-			AdminTools admTools = GameManager.Instance.adminTools;
-			PlatformUserIdentifierAbs userId = _context.Connection?.UserId;
-
-			bool bViewAll = WebConnection.CanViewAllPlayers (_context.PermissionLevel);
-
-			// TODO: Sort (and filter?) prior to converting to JSON ... hard as how to get the correct column's data? (i.e. column name matches JSON object field names, not source data)
-
-			int rowsPerPage = 25;
-			if (_context.Request.QueryString ["rowsperpage"] != null) {
-				int.TryParse (_context.Request.QueryString ["rowsperpage"], out rowsPerPage);
-			}
-
-			int page = 0;
-			if (_context.Request.QueryString ["page"] != null) {
-				int.TryParse (_context.Request.QueryString ["page"], out page);
-			}
-
-			int firstEntry = page * rowsPerPage;
-
-			Players playersList = PersistentContainer.Instance.Players;
-
-			
-			List<JsonObject> playerList = new List<JsonObject> ();
-
-			jsonSerializeSampler.Begin ();
-
-			foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in playersList.Dict) {
-				Player p = kvp.Value;
-
-				if (bViewAll || p.PlatformId.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.Key.CombinedString));
-					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.ToUniversalTime ().ToString ("yyyy-MM-ddTHH:mm:ssZ")));
-					pJson.Add ("ping", new JsonNumber (p.IsOnline ? p.ClientInfo.ping : -1));
-
-					JsonBoolean banned = admTools != null ? new JsonBoolean (admTools.IsBanned (kvp.Key, out _, out _)) : new JsonBoolean (false);
-
-					pJson.Add ("banned", banned);
-
-					playerList.Add (pJson);
-				}
-			}
-
-			jsonSerializeSampler.End ();
-
-			IEnumerable<JsonObject> list = playerList;
-
-			foreach (string key in _context.Request.QueryString.AllKeys) {
-				if (!string.IsNullOrEmpty (key) && key.StartsWith ("filter[")) {
-					string filterCol = key.Substring (key.IndexOf ('[') + 1);
-					filterCol = filterCol.Substring (0, filterCol.Length - 1);
-					string filterVal = _context.Request.QueryString.Get (key).Trim ();
-
-					list = ExecuteFilter (list, filterCol, filterVal);
-				}
-			}
-
-			int totalAfterFilter = list.Count ();
-
-			foreach (string key in _context.Request.QueryString.AllKeys) {
-				if (!string.IsNullOrEmpty (key) && key.StartsWith ("sort[")) {
-					string sortCol = key.Substring (key.IndexOf ('[') + 1);
-					sortCol = sortCol.Substring (0, sortCol.Length - 1);
-					string sortVal = _context.Request.QueryString.Get (key);
-
-					list = ExecuteSort (list, sortCol, sortVal == "0");
-				}
-			}
-
-			list = list.Skip (firstEntry);
-			list = list.Take (rowsPerPage);
-
-
-			JsonArray playersJsResult = new JsonArray ();
-			foreach (JsonObject jsO in list) {
-				playersJsResult.Add (jsO);
-			}
-
-			JsonObject result = new JsonObject ();
-			result.Add ("total", new JsonNumber (totalAfterFilter));
-			result.Add ("totalUnfiltered", new JsonNumber (playerList.Count));
-			result.Add ("firstResult", new JsonNumber (firstEntry));
-			result.Add ("players", playersJsResult);
-
-			WebUtils.WriteJson (_context.Response, result);
-		}
-
-		private IEnumerable<JsonObject> ExecuteFilter (IEnumerable<JsonObject> _list, string _filterCol,
-			string _filterVal) {
-			if (!_list.Any()) {
-				return _list;
-			}
-
-			if (_list.First ().ContainsKey (_filterCol)) {
-				Type colType = _list.First () [_filterCol].GetType ();
-				if (colType == typeof (JsonNumber)) {
-					return ExecuteNumberFilter (_list, _filterCol, _filterVal);
-				}
-
-				if (colType == typeof (JsonBoolean)) {
-					bool value = StringParsers.ParseBool (_filterVal);
-					return _list.Where (_line => ((JsonBoolean) _line [_filterCol]).GetBool () == value);
-				}
-
-				if (colType == typeof (JsonString)) {
-					// regex-match whole ^string$, replace * by .*, ? by .?, + by .+
-					_filterVal = _filterVal.Replace ("*", ".*").Replace ("?", ".?").Replace ("+", ".+");
-					_filterVal = "^" + _filterVal + "$";
-
-					//Log.Out ("GetPlayerList: Filter on String with Regex '" + _filterVal + "'");
-					Regex matcher = new Regex (_filterVal, RegexOptions.IgnoreCase);
-					return _list.Where (_line => matcher.IsMatch (((JsonString) _line [_filterCol]).GetString ()));
-				}
-			}
-
-			return _list;
-		}
-
-
-		private IEnumerable<JsonObject> ExecuteNumberFilter (IEnumerable<JsonObject> _list, string _filterCol,
-			string _filterVal) {
-			// allow value (exact match), =, ==, >=, >, <=, <
-			Match filterMatch = numberFilterMatcher.Match (_filterVal);
-			if (filterMatch.Success) {
-				double value = StringParsers.ParseDouble (filterMatch.Groups [2].Value);
-				NumberMatchType matchType;
-				double epsilon = value / 100000;
-				switch (filterMatch.Groups [1].Value) {
-					case "":
-					case "=":
-					case "==":
-						matchType = NumberMatchType.Equal;
-						break;
-					case ">":
-						matchType = NumberMatchType.Greater;
-						break;
-					case ">=":
-					case "=>":
-						matchType = NumberMatchType.GreaterEqual;
-						break;
-					case "<":
-						matchType = NumberMatchType.Lesser;
-						break;
-					case "<=":
-					case "=<":
-						matchType = NumberMatchType.LesserEqual;
-						break;
-					default:
-						matchType = NumberMatchType.Equal;
-						break;
-				}
-
-				return _list.Where (delegate (JsonObject _line) {
-					double objVal = ((JsonNumber) _line [_filterCol]).GetDouble ();
-					switch (matchType) {
-						case NumberMatchType.Greater:
-							return objVal > value;
-						case NumberMatchType.GreaterEqual:
-							return objVal >= value;
-						case NumberMatchType.Lesser:
-							return objVal < value;
-						case NumberMatchType.LesserEqual:
-							return objVal <= value;
-						case NumberMatchType.Equal:
-						default:
-							return NearlyEqual (objVal, value, epsilon);
-					}
-				});
-			}
-
-			Log.Out ("[Web] GetPlayerList: ignoring invalid filter for number-column '{0}': '{1}'", _filterCol, _filterVal);
-			return _list;
-		}
-
-
-		private IEnumerable<JsonObject> ExecuteSort (IEnumerable<JsonObject> _list, string _sortCol, bool _ascending) {
-			if (_list.Count () == 0) {
-				return _list;
-			}
-
-			if (_list.First ().ContainsKey (_sortCol)) {
-				Type colType = _list.First () [_sortCol].GetType ();
-				if (colType == typeof (JsonNumber)) {
-					if (_ascending) {
-						return _list.OrderBy (_line => ((JsonNumber) _line [_sortCol]).GetDouble ());
-					}
-
-					return _list.OrderByDescending (_line => ((JsonNumber) _line [_sortCol]).GetDouble ());
-				}
-
-				if (colType == typeof (JsonBoolean)) {
-					if (_ascending) {
-						return _list.OrderBy (_line => ((JsonBoolean) _line [_sortCol]).GetBool ());
-					}
-
-					return _list.OrderByDescending (_line => ((JsonBoolean) _line [_sortCol]).GetBool ());
-				}
-
-				if (_ascending) {
-					return _list.OrderBy (_line => _line [_sortCol].ToString ());
-				}
-
-				return _list.OrderByDescending (_line => _line [_sortCol].ToString ());
-			}
-
-			return _list;
-		}
-
-
-		private bool NearlyEqual (double _a, double _b, double _epsilon) {
-			double absA = Math.Abs (_a);
-			double absB = Math.Abs (_b);
-			double diff = Math.Abs (_a - _b);
-
-			if (_a == _b) {
-				return true;
-			}
-
-			if (_a == 0 || _b == 0 || diff < double.Epsilon) {
-				return diff < _epsilon;
-			}
-
-			return diff / (absA + absB) < _epsilon;
-		}
-
-		private enum NumberMatchType {
-			Equal,
-			Greater,
-			GreaterEqual,
-			Lesser,
-			LesserEqual
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetPlayersLocation.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetPlayersLocation.cs	(revision 401)
+++ 	(revision )
@@ -1,63 +1,0 @@
-using System.Collections.Generic;
-using AllocsFixes.JSON;
-using AllocsFixes.PersistentData;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetPlayersLocation : AbsWebAPI {
-		public override void HandleRequest (RequestContext _context) {
-			AdminTools admTools = GameManager.Instance.adminTools;
-			PlatformUserIdentifierAbs reqUserId = _context.Connection?.UserId;
-
-			bool listOffline = false;
-			if (_context.Request.QueryString ["offline"] != null) {
-				bool.TryParse (_context.Request.QueryString ["offline"], out listOffline);
-			}
-
-			bool bViewAll = WebConnection.CanViewAllPlayers (_context.PermissionLevel);
-
-			JsonArray playersJsResult = new JsonArray ();
-
-			Players playersList = PersistentContainer.Instance.Players;
-
-			foreach ((PlatformUserIdentifierAbs userId, Player player) in playersList.Dict) {
-				if (admTools != null) {
-					if (admTools.IsBanned (userId, out _, out _)) {
-						continue;
-					}
-				}
-
-				if (!listOffline && !player.IsOnline) {
-					continue;
-				}
-
-				if (!bViewAll && !player.PlatformId.Equals (reqUserId)) {
-					continue;
-				}
-
-				JsonObject pos = new JsonObject ();
-				pos.Add ("x", new JsonNumber (player.LastPosition.x));
-				pos.Add ("y", new JsonNumber (player.LastPosition.y));
-				pos.Add ("z", new JsonNumber (player.LastPosition.z));
-
-				JsonObject pJson = new JsonObject ();
-				pJson.Add ("steamid", new JsonString (userId.CombinedString));
-
-				//					pJson.Add("entityid", new JSONNumber (p.EntityID));
-				//                    pJson.Add("ip", new JSONString (p.IP));
-				pJson.Add ("name", new JsonString (player.Name));
-				pJson.Add ("online", new JsonBoolean (player.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);
-			}
-
-			WebUtils.WriteJson (_context.Response, playersJsResult);
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetPlayersOnline.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetPlayersOnline.cs	(revision 401)
+++ 	(revision )
@@ -1,48 +1,0 @@
-using System.Collections.Generic;
-using AllocsFixes.JSON;
-using AllocsFixes.PersistentData;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetPlayersOnline : AbsWebAPI {
-		public override void HandleRequest (RequestContext _context) {
-			JsonArray players = new JsonArray ();
-
-			World w = GameManager.Instance.World;
-			foreach ((int entityId, EntityPlayer entityPlayer) in w.Players.dict) {
-				ClientInfo ci = ConnectionManager.Instance.Clients.ForEntityId (entityId);
-				Player player = PersistentContainer.Instance.Players [ci.InternalId, false];
-
-				JsonObject pos = new JsonObject ();
-				pos.Add ("x", new JsonNumber ((int) entityPlayer.GetPosition ().x));
-				pos.Add ("y", new JsonNumber ((int) entityPlayer.GetPosition ().y));
-				pos.Add ("z", new JsonNumber ((int) entityPlayer.GetPosition ().z));
-
-				JsonObject p = new JsonObject ();
-				p.Add ("steamid", new JsonString (ci.PlatformId.CombinedString));
-				p.Add ("entityid", new JsonNumber (ci.entityId));
-				p.Add ("ip", new JsonString (ci.ip));
-				p.Add ("name", new JsonString (entityPlayer.EntityName));
-				p.Add ("online", new JsonBoolean (true));
-				p.Add ("position", pos);
-
-				p.Add ("level", new JsonNumber (player?.Level ?? -1));
-				p.Add ("health", new JsonNumber (entityPlayer.Health));
-				p.Add ("stamina", new JsonNumber (entityPlayer.Stamina));
-				p.Add ("zombiekills", new JsonNumber (entityPlayer.KilledZombies));
-				p.Add ("playerkills", new JsonNumber (entityPlayer.KilledPlayers));
-				p.Add ("playerdeaths", new JsonNumber (entityPlayer.Died));
-				p.Add ("score", new JsonNumber (entityPlayer.Score));
-
-				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);
-			}
-
-			WebUtils.WriteJson (_context.Response, players);
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetServerInfo.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetServerInfo.cs	(revision 401)
+++ 	(revision )
@@ -1,47 +1,0 @@
-using System;
-using AllocsFixes.JSON;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetServerInfo : AbsWebAPI {
-		public override void HandleRequest (RequestContext _context) {
-			JsonObject serverInfo = new JsonObject ();
-
-			GameServerInfo gsi = ConnectionManager.Instance.LocalServerInfo;
-
-			foreach (string stringGamePref in Enum.GetNames (typeof (GameInfoString))) {
-				string value = gsi.GetValue ((GameInfoString) Enum.Parse (typeof (GameInfoString), stringGamePref));
-
-				JsonObject singleStat = new JsonObject ();
-				singleStat.Add ("type", new JsonString ("string"));
-				singleStat.Add ("value", new JsonString (value));
-
-				serverInfo.Add (stringGamePref, singleStat);
-			}
-
-			foreach (string intGamePref in Enum.GetNames (typeof (GameInfoInt))) {
-				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));
-
-				serverInfo.Add (intGamePref, singleStat);
-			}
-
-			foreach (string boolGamePref in Enum.GetNames (typeof (GameInfoBool))) {
-				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));
-
-				serverInfo.Add (boolGamePref, singleStat);
-			}
-
-
-			WebUtils.WriteJson (_context.Response, serverInfo);
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetStats.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetStats.cs	(revision 401)
+++ 	(revision )
@@ -1,28 +1,0 @@
-using AllocsFixes.JSON;
-using AllocsFixes.LiveData;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetStats : AbsWebAPI {
-		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 ()));
-
-			WebUtils.WriteJson (_context.Response, result);
-		}
-
-		public override int DefaultPermissionLevel () {
-			return 2000;
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetWebMods.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetWebMods.cs	(revision 401)
+++ 	(revision )
@@ -1,37 +1,0 @@
-using AllocsFixes.JSON;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetWebMods : AbsWebAPI {
-		private readonly JsonArray loadedWebMods = new JsonArray ();
-
-		public GetWebMods (Web _parent) {
-			foreach (WebMod webMod in _parent.webMods) {
-				JsonObject modJson = new JsonObject ();
-
-				modJson.Add ("name", new JsonString (webMod.ParentMod.ModInfo.Name.Value));
-				
-				string webModReactBundle = webMod.ReactBundle;
-				if (webModReactBundle != null) {
-					modJson.Add ("bundle", new JsonString (webModReactBundle));
-				}
-
-				string webModCssFile = webMod.CssPath;
-				if (webModCssFile != null) {
-					modJson.Add ("css", new JsonString (webModCssFile));
-				}
-
-				loadedWebMods.Add (modJson);
-			}
-		}
-
-		public override void HandleRequest (RequestContext _context) {
-			WebUtils.WriteJson (_context.Response, loadedWebMods);
-		}
-
-		public override int DefaultPermissionLevel () {
-			return 2000;
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/GetWebUIUpdates.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/GetWebUIUpdates.cs	(revision 401)
+++ 	(revision )
@@ -1,35 +1,0 @@
-using AllocsFixes.JSON;
-using AllocsFixes.LiveData;
-using JetBrains.Annotations;
-
-namespace Webserver.WebAPI {
-	[UsedImplicitly]
-	public class GetWebUIUpdates : AbsWebAPI {
-		public override void HandleRequest (RequestContext _context) {
-			if (_context.Request.QueryString ["latestLine"] == null ||
-			    !int.TryParse (_context.Request.QueryString ["latestLine"], out int latestLine)) {
-				latestLine = 0;
-			}
-
-			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 ()));
-
-			result.Add ("newlogs", new JsonNumber (LogBuffer.Instance.LatestLine - latestLine));
-
-			WebUtils.WriteJson (_context.Response, result);
-		}
-
-		public override int DefaultPermissionLevel () {
-			return 2000;
-		}
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/JsonCommons.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/JsonCommons.cs	(revision 402)
+++ binary-improvements2/WebServer/src/WebAPI/JsonCommons.cs	(revision 402)
@@ -0,0 +1,23 @@
+using Utf8Json;
+
+namespace Webserver.WebAPI {
+	public static class JsonCommons {
+		private static readonly byte[] jsonKeyPositionX = JsonWriter.GetEncodedPropertyNameWithBeginObject ("x");
+		private static readonly byte[] jsonKeyPositionY = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("y");
+		private static readonly byte[] jsonKeyPositionZ = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("z");
+
+		public static void WritePositionObject (JsonWriter _writer, Vector3i _position) {
+			_writer.WriteRaw (jsonKeyPositionX);
+			_writer.WriteInt32 (_position.x);
+			
+			_writer.WriteRaw (jsonKeyPositionY);
+			_writer.WriteInt32 (_position.y);
+			
+			_writer.WriteRaw (jsonKeyPositionZ);
+			_writer.WriteInt32 (_position.z);
+			
+			_writer.WriteEndObject ();
+		}
+		
+	}
+}
