Index: binary-improvements/MapRendering/Web/API/ExecuteConsoleCommand.cs
===================================================================
--- binary-improvements/MapRendering/Web/API/ExecuteConsoleCommand.cs	(revision 277)
+++ binary-improvements/MapRendering/Web/API/ExecuteConsoleCommand.cs	(revision 279)
@@ -16,7 +16,9 @@
 			}
 
-			string commandName = req.QueryString ["command"];
+			string commandline = req.QueryString ["command"];
+			string commandPart = commandline.Split (' ') [0];
+			string argumentsPart = commandline.Substring (Math.Min (commandline.Length, commandPart.Length + 1));
 
-			IConsoleCommand command = SdtdConsole.Instance.GetCommand (commandName);
+			IConsoleCommand command = SdtdConsole.Instance.GetCommand (commandline);
 
 			if (command == null) {
@@ -36,50 +38,11 @@
 			// TODO: Execute command (store resp as IConsoleConnection instance to deliver response to the single client?)
 
-//			JSONObject result = new JSONObject ();
-//			result.Add ("claimsize", new JSONNumber (GamePrefs.GetInt (EnumGamePrefs.LandClaimSize)));
-//
-//			JSONArray claimOwners = new JSONArray ();
-//			result.Add ("claimowners", claimOwners);
-//
-//			Dictionary<Vector3i, PersistentPlayerData> d = GameManager.Instance.GetPersistentPlayerList ().m_lpBlockMap;
-//			if (d != null) {
-//				World w = GameManager.Instance.World;
-//				Dictionary<PersistentPlayerData, List<Vector3i>> owners = new Dictionary<PersistentPlayerData, List<Vector3i>> ();
-//				foreach (KeyValuePair<Vector3i, PersistentPlayerData> kvp in d) {
-//					if (steamid.Length == 0 || kvp.Value.PlayerId.Equals (steamid)) {
-//						if (!owners.ContainsKey (kvp.Value)) {
-//							owners.Add (kvp.Value, new List<Vector3i> ());
-//						}
-//						owners [kvp.Value].Add (kvp.Key);
-//					}
-//				}
-//
-//				foreach (KeyValuePair<PersistentPlayerData, List<Vector3i>> kvp in owners) {
-//					if (steamid.Length == 0 || kvp.Key.PlayerId.Equals (steamid)) {
-//						string curID = kvp.Key.PlayerId;
-//						bool isActive = w.IsLandProtectionValidForPlayer (kvp.Key);
-//
-//						JSONObject owner = new JSONObject ();
-//						claimOwners.Add (owner);
-//
-//						owner.Add ("steamid", new JSONString (curID));
-//						owner.Add ("claimactive", new JSONBoolean (isActive));
-//
-//						JSONArray claims = new JSONArray ();
-//						owner.Add ("claims", claims);
-//
-//						foreach (Vector3i v in kvp.Value) {
-//							JSONObject claim = new JSONObject ();
-//							claim.Add ("x", new JSONNumber (v.x));
-//							claim.Add ("y", new JSONNumber (v.y));
-//							claim.Add ("z", new JSONNumber (v.z));
-//
-//							claims.Add (claim);
-//						}
-//					}
-//				}
-//			}
-//
-//			WriteJSON (resp, result);
+			resp.SendChunked = true;
+			WebCommandResult wcr = new WebCommandResult (commandPart, argumentsPart, resp);
+			SdtdConsole.Instance.ExecuteAsync (commandline, wcr);
+		}
+
+		public override int DefaultPermissionLevel () {
+			return 2000;
 		}
 	}
Index: binary-improvements/MapRendering/Web/API/GetAllowedCommands.cs
===================================================================
--- binary-improvements/MapRendering/Web/API/GetAllowedCommands.cs	(revision 279)
+++ binary-improvements/MapRendering/Web/API/GetAllowedCommands.cs	(revision 279)
@@ -0,0 +1,40 @@
+using AllocsFixes.JSON;
+using AllocsFixes.PersistentData;
+using System;
+using System.Collections.Generic;
+using System.Net;
+
+namespace AllocsFixes.NetConnections.Servers.Web.API
+{
+	public class GetAllowedCommands : WebAPI {
+		public override void HandleRequest (HttpListenerRequest req, HttpListenerResponse resp, WebConnection user, int permissionLevel) {
+			JSONObject result = new JSONObject ();
+			JSONArray entries = new JSONArray ();
+			foreach (IConsoleCommand cc in SdtdConsole.Instance.GetCommands ()) {
+				AdminToolsCommandPermissions atcp = GameManager.Instance.adminTools.GetAdminToolsCommandPermission (cc.GetCommands ());
+				if (permissionLevel <= atcp.PermissionLevel) {
+					string cmd = string.Empty;
+					foreach (string s in cc.GetCommands ()) {
+						if (s.Length > cmd.Length) {
+							cmd = s;
+						}
+					}
+					JSONObject cmdObj = new JSONObject ();
+					cmdObj.Add ("command", new JSONString (cmd));
+					cmdObj.Add ("description", new JSONString (cc.GetDescription ()));
+					cmdObj.Add ("help", new JSONString (cc.GetHelp ()));
+					entries.Add (cmdObj);
+				}
+			}
+
+			result.Add ("commands", entries);
+
+			WriteJSON (resp, result);
+		}
+
+		public override int DefaultPermissionLevel () {
+			return 2000;
+		}
+	}
+}
+
Index: binary-improvements/MapRendering/Web/API/GetPlayerList.cs
===================================================================
--- binary-improvements/MapRendering/Web/API/GetPlayerList.cs	(revision 279)
+++ binary-improvements/MapRendering/Web/API/GetPlayerList.cs	(revision 279)
@@ -0,0 +1,260 @@
+using AllocsFixes.JSON;
+using AllocsFixes.PersistentData;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text.RegularExpressions;
+
+namespace AllocsFixes.NetConnections.Servers.Web.API
+{
+	public class GetPlayerList : WebAPI
+	{
+		private static Regex numberFilterMatcher = new Regex (@"^(>=|=>|>|<=|=<|<|==|=)?\s*([0-9]+(\.[0-9]*)?)$");
+		private enum NumberMatchType {
+			Equal,
+			Greater,
+			GreaterEqual,
+			Lesser,
+			LesserEqual
+		}
+
+		public override void HandleRequest (HttpListenerRequest req, HttpListenerResponse resp, WebConnection user, int permissionLevel)
+		{
+            AdminTools admTools = GameManager.Instance.adminTools;
+            user = user ?? new WebConnection ("", "", 0L);
+
+            bool bViewAll = WebConnection.CanViewAllPlayers (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 (req.QueryString ["rowsperpage"] != null) {
+				int.TryParse (req.QueryString ["rowsperpage"], out rowsPerPage);
+			}
+
+			int page = 0;
+			if (req.QueryString ["page"] != null) {
+				int.TryParse (req.QueryString ["page"], out page);
+			}
+
+			int firstEntry = page * rowsPerPage;
+
+			Players playersList = PersistentContainer.Instance.Players;
+
+			List<JSONObject> playerList = new List<JSONObject> ();
+
+			foreach (string sid in playersList.SteamIDs) {
+                Player p = playersList [sid, false];
+
+                ulong player_steam_ID = 0L;
+                if (!ulong.TryParse (sid, out player_steam_ID))
+                    player_steam_ID = 0L;
+
+                if ((player_steam_ID == user.SteamID) || bViewAll) {
+                    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 (sid));
+					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 ("s")));
+					pJson.Add ("ping", new JSONNumber (p.IsOnline ? p.ClientInfo.ping : -1));
+
+					JSONBoolean banned = null;
+					if (admTools != null) {
+						banned = new JSONBoolean (admTools.IsBanned (sid));
+					} else {
+						banned = new JSONBoolean (false);
+					}
+					pJson.Add ("banned", banned);
+
+					playerList.Add (pJson);
+                }
+            }
+
+			IEnumerable<JSONObject> list = playerList;
+
+			foreach (string key in req.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 = req.QueryString.Get (key).Trim ();
+
+					list = ExecuteFilter (list, filterCol, filterVal);
+				}
+			}
+
+			int totalAfterFilter = list.Count ();
+
+			foreach (string key in req.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 = req.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);
+
+			WriteJSON (resp, result);
+		}
+
+		private IEnumerable<JSONObject> ExecuteFilter (IEnumerable<JSONObject> _list, string _filterCol, string _filterVal) {
+			if (_list.Count () == 0) {
+				return _list;
+			}
+
+			if (_list.First ().ContainsKey (_filterCol)) {
+				Type colType = _list.First () [_filterCol].GetType ();
+				if (colType == typeof(JSONNumber)) {
+					return ExecuteNumberFilter (_list, _filterCol, _filterVal);
+				} else if (colType == typeof(JSONBoolean)) {
+					bool value = _filterVal.Trim ().ToLower () == "true";
+					return _list.Where (line => (line [_filterCol] as JSONBoolean).GetBool () == value);
+				} else 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 ((line [_filterCol] as JSONString).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 = Utils.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 = (line [_filterCol] as JSONNumber).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);
+					}
+				});
+			} else {
+				Log.Out ("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 => (line [_sortCol] as JSONNumber).GetDouble ());
+					} else {
+						return _list.OrderByDescending (line => (line [_sortCol] as JSONNumber).GetDouble ());
+					}
+				} else if (colType == typeof(JSONBoolean)) {
+					if (_ascending) {
+						return _list.OrderBy (line => (line [_sortCol] as JSONBoolean).GetBool ());
+					} else {
+						return _list.OrderByDescending (line => (line [_sortCol] as JSONBoolean).GetBool ());
+					}
+				} else {
+					if (_ascending) {
+						return _list.OrderBy (line => line [_sortCol].ToString ());
+					} else {
+						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)
+			{ // shortcut, handles infinities
+				return true;
+			} 
+			else if (a == 0 || b == 0 || diff < Double.Epsilon) 
+			{
+				// a or b is zero or both are extremely close to it
+				// relative error is less meaningful here
+				return diff < epsilon;
+			}
+			else
+			{ // use relative error
+				return diff / (absA + absB) < epsilon;
+			}
+		}
+
+	}
+}
+
Index: binary-improvements/MapRendering/Web/API/GetServerInfo.cs
===================================================================
--- binary-improvements/MapRendering/Web/API/GetServerInfo.cs	(revision 279)
+++ binary-improvements/MapRendering/Web/API/GetServerInfo.cs	(revision 279)
@@ -0,0 +1,52 @@
+using AllocsFixes.JSON;
+using AllocsFixes.PersistentData;
+using System;
+using System.Collections.Generic;
+using System.Net;
+
+namespace AllocsFixes.NetConnections.Servers.Web.API
+{
+	public class GetServerInfo : WebAPI
+	{
+		public override void HandleRequest (HttpListenerRequest req, HttpListenerResponse resp, WebConnection user, int permissionLevel)
+		{
+			JSONObject serverInfo = new JSONObject ();
+
+			GameServerInfo gsi = Steam.Masterserver.Server.LocalGameInfo;
+
+			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);
+			}
+
+
+			WriteJSON(resp, serverInfo);
+		}
+	}
+}
+
Index: binary-improvements/MapRendering/Web/API/GetStats.cs
===================================================================
--- binary-improvements/MapRendering/Web/API/GetStats.cs	(revision 277)
+++ binary-improvements/MapRendering/Web/API/GetStats.cs	(revision 279)
@@ -26,4 +26,8 @@
 			WriteJSON (resp, result);
 		}
+
+		public override int DefaultPermissionLevel () {
+			return 2000;
+		}
 	}
 }
Index: binary-improvements/MapRendering/Web/API/GetWebUIUpdates.cs
===================================================================
--- binary-improvements/MapRendering/Web/API/GetWebUIUpdates.cs	(revision 277)
+++ binary-improvements/MapRendering/Web/API/GetWebUIUpdates.cs	(revision 279)
@@ -31,4 +31,8 @@
 			WriteJSON (resp, result);
 		}
+
+		public override int DefaultPermissionLevel () {
+			return 2000;
+		}
 	}
 }
Index: binary-improvements/MapRendering/Web/API/WebAPI.cs
===================================================================
--- binary-improvements/MapRendering/Web/API/WebAPI.cs	(revision 277)
+++ binary-improvements/MapRendering/Web/API/WebAPI.cs	(revision 279)
@@ -17,4 +17,8 @@
 
 		public abstract void HandleRequest (HttpListenerRequest req, HttpListenerResponse resp, WebConnection user, int permissionLevel);
+
+		public virtual int DefaultPermissionLevel () {
+			return 0;
+		}
 	}
 }
Index: binary-improvements/MapRendering/Web/Handlers/ApiHandler.cs
===================================================================
--- binary-improvements/MapRendering/Web/Handlers/ApiHandler.cs	(revision 277)
+++ binary-improvements/MapRendering/Web/Handlers/ApiHandler.cs	(revision 279)
@@ -40,5 +40,5 @@
 		private void addApi (string _apiName, WebAPI _api) {
 			apis.Add (_apiName, _api);
-			WebPermissions.Instance.AddKnownModule ("webapi." + _apiName);
+			WebPermissions.Instance.AddKnownModule ("webapi." + _apiName, _api.DefaultPermissionLevel ());
 		}
 
Index: binary-improvements/MapRendering/Web/Handlers/PathHandler.cs
===================================================================
--- binary-improvements/MapRendering/Web/Handlers/PathHandler.cs	(revision 277)
+++ binary-improvements/MapRendering/Web/Handlers/PathHandler.cs	(revision 279)
@@ -11,7 +11,7 @@
 		}
 
-		protected PathHandler (string _moduleName) {
+		protected PathHandler (string _moduleName, int _defaultPermissionLevel = 0) {
 			this.moduleName = _moduleName;
-			WebPermissions.Instance.AddKnownModule (_moduleName);
+			WebPermissions.Instance.AddKnownModule (_moduleName, _defaultPermissionLevel);
 		}
 
Index: binary-improvements/MapRendering/Web/Web.cs
===================================================================
--- binary-improvements/MapRendering/Web/Web.cs	(revision 277)
+++ binary-improvements/MapRendering/Web/Web.cs	(revision 279)
@@ -20,4 +20,5 @@
 		public static int handlingCount = 0;
 		public static int currentHandlers = 0;
+		public static long totalHandlingTime = 0;
 		private string dataFolder;
 		private bool useStaticCache = false;
@@ -131,4 +132,5 @@
 				Interlocked.Increment (ref handlingCount);
 				Interlocked.Increment (ref currentHandlers);
+				MicroStopwatch msw = new MicroStopwatch ();
 				HttpListenerContext ctx = _listener.EndGetContext (result);
 				_listener.BeginGetContext (new AsyncCallback (HandleRequest), _listener);
@@ -136,4 +138,5 @@
 					HttpListenerRequest request = ctx.Request;
 					HttpListenerResponse response = ctx.Response;
+					response.SendChunked = false;
 
 					response.ProtocolVersion = new Version ("1.1");
@@ -187,7 +190,10 @@
 					Log.Out ("Error in Web.HandleRequest(): " + e);
 				} finally {
-					if (ctx != null) {
+					if (ctx != null && !ctx.Response.SendChunked) {
 						ctx.Response.Close ();
 					}
+					msw.Stop ();
+					totalHandlingTime += msw.ElapsedMicroseconds;
+					Log.Out ("Web.HandleRequest(): Took {0} µs", msw.ElapsedMicroseconds);
 					Interlocked.Decrement (ref currentHandlers);
 				}
Index: binary-improvements/MapRendering/Web/WebCommandResult.cs
===================================================================
--- binary-improvements/MapRendering/Web/WebCommandResult.cs	(revision 279)
+++ binary-improvements/MapRendering/Web/WebCommandResult.cs	(revision 279)
@@ -0,0 +1,96 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Text;
+
+using AllocsFixes.JSON;
+using System.IO;
+using System.Net.Sockets;
+using System.Threading;
+
+namespace AllocsFixes.NetConnections.Servers.Web
+{
+	public class WebCommandResult : IConsoleConnection
+	{
+		public static int handlingCount = 0;
+		public static int currentHandlers = 0;
+		public static long totalHandlingTime = 0;
+
+		private HttpListenerResponse response;
+		private string command;
+		private string parameters;
+
+		public WebCommandResult (string _command, string _parameters, HttpListenerResponse _response) {
+			Interlocked.Increment (ref handlingCount);
+			Interlocked.Increment (ref currentHandlers);
+
+			response = _response;
+			command = _command;
+			parameters = _parameters;
+		}
+
+		public void SendLines (List<string> _output) {
+			MicroStopwatch msw = new MicroStopwatch ();
+
+			StringBuilder sb = new StringBuilder ();
+			foreach (string line in _output) {
+				sb.AppendLine (line);
+			}
+
+			JSONObject result = new JSONObject ();
+
+			result.Add ("command", new JSONString (command));
+			result.Add ("parameters", new JSONString (parameters));
+			result.Add ("result", new JSONString (sb.ToString ()));
+
+			response.SendChunked = false;
+
+			try {
+				WriteJSON (response, result);
+			} catch (IOException e) {
+				if (e.InnerException is SocketException) {
+					Log.Out ("Error in WebCommandResult.SendLines(): Remote host closed connection: " + e.InnerException.Message);
+				} else {
+					Log.Out ("Error (IO) in WebCommandResult.SendLines(): " + e);
+				}
+			} catch (Exception e) {
+				Log.Out ("Error in WebCommandResult.SendLines(): " + e);
+			} finally {
+				if (response != null) {
+					response.Close ();
+				}
+
+				msw.Stop ();
+				totalHandlingTime += msw.ElapsedMicroseconds;
+				Log.Out ("WebCommandResult.SendLines(): Took {0} µs", msw.ElapsedMicroseconds);
+				Interlocked.Decrement (ref currentHandlers);
+			}
+		}
+
+		public void WriteJSON (HttpListenerResponse resp, JSON.JSONNode root)
+		{
+			byte[] buf = Encoding.UTF8.GetBytes (root.ToString());
+			resp.ContentLength64 = buf.Length;
+			resp.ContentType = "application/json";
+			resp.ContentEncoding = Encoding.UTF8;
+			resp.OutputStream.Write (buf, 0, buf.Length);
+		}
+
+		public void SendLine (string _text) {
+			//throw new NotImplementedException ();
+		}
+
+		public void SendLog (string _msg, string _trace, UnityEngine.LogType _type) {
+			//throw new NotImplementedException ();
+		}
+
+		public void EnableLogLevel (UnityEngine.LogType _type, bool _enable) {
+			//throw new NotImplementedException ();
+		}
+
+		public string GetDescription () {
+			return "WebCommandResult_for_" + command;
+		}
+	}
+}
+
Index: binary-improvements/MapRendering/Web/WebPermissions.cs
===================================================================
--- binary-improvements/MapRendering/Web/WebPermissions.cs	(revision 277)
+++ binary-improvements/MapRendering/Web/WebPermissions.cs	(revision 279)
@@ -96,10 +96,13 @@
 		}
 
-		public void AddKnownModule (string _module) {
+		public void AddKnownModule (string _module, int _defaultPermission) {
 			if (!string.IsNullOrEmpty (_module)) {
 				lock (this) {
                     if (!IsKnownModule( _module)) {
-					    knownModules.Add (_module, new WebModulePermission (_module, 0));
+						knownModules.Add (_module, new WebModulePermission (_module, _defaultPermission));
                     }
+					if (_defaultPermission > 0 && !modules.ContainsKey (_module.ToLower ())) {
+						AddModulePermission (_module, _defaultPermission);
+					}
 				}
 			}
