Index: TFP-WebServer/WebServer/ModInfo.xml
===================================================================
--- TFP-WebServer/WebServer/ModInfo.xml	(revision 453)
+++ TFP-WebServer/WebServer/ModInfo.xml	(revision 457)
@@ -5,5 +5,5 @@
 	<Description value="Integrated Webserver for the Web Dashboard and server APIs" />
 	<Author value="The Fun Pimps LLC" />
-	<Version value="21.1.9.0" />
+	<Version value="21.1.15.0" />
 	<Website value="" />
 </xml>
Index: TFP-WebServer/WebServer/WebServer.csproj
===================================================================
--- TFP-WebServer/WebServer/WebServer.csproj	(revision 453)
+++ TFP-WebServer/WebServer/WebServer.csproj	(revision 457)
@@ -135,4 +135,5 @@
     <Compile Include="src\WebAPI\APIs\ServerState\GamePrefs.cs" />
     <Compile Include="src\WebAPI\APIs\ServerState\GameStats.cs" />
+    <Compile Include="src\WebAPI\APIs\ServerState\KeyValueListAbs.cs" />
     <Compile Include="src\WebAPI\APIs\ServerState\ServerInfo.cs" />
     <Compile Include="src\WebAPI\APIs\ServerState\ServerStats.cs" />
@@ -193,3 +194,6 @@
     </ProjectReference>
   </ItemGroup>
+  <ItemGroup>
+    <Content Include="src\WebAPI\APIs\WorldState\Animal.openapi.json" />
+  </ItemGroup>
 </Project>
Index: TFP-WebServer/WebServer/src/UrlHandlers/SessionHandler.cs
===================================================================
--- TFP-WebServer/WebServer/src/UrlHandlers/SessionHandler.cs	(revision 453)
+++ TFP-WebServer/WebServer/src/UrlHandlers/SessionHandler.cs	(revision 457)
@@ -98,5 +98,5 @@
 			}
 
-			var loginResult = HandleUserIdLogin (_connectionHandler, _context, _remoteEndpointString, userPassLoginName, webUser.Name, webUser.PlatformUser, webUser.CrossPlatformUser);
+			bool loginResult = HandleUserIdLogin (_connectionHandler, _context, _remoteEndpointString, userPassLoginName, webUser.Name, webUser.PlatformUser, webUser.CrossPlatformUser);
 			if (loginResult) {
 				WebUtils.WriteText (_context.Response, "");
Index: TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/GamePrefs.cs
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/GamePrefs.cs	(revision 453)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/GamePrefs.cs	(revision 457)
@@ -5,21 +5,11 @@
 namespace Webserver.WebAPI.APIs.ServerState {
 	[UsedImplicitly]
-	public class GamePrefs : AbsRestApi {
-		private static readonly byte[] keyType = JsonWriter.GetEncodedPropertyNameWithBeginObject ("type");
-		private static readonly byte[] keyValue = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("value");
-		private static readonly byte[] keyDefault = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("default");
+	public class GamePrefs : KeyValueListAbs {
+		public GamePrefs () : base ("GamePrefs") {
+		}
 
-		private int largestBuffer;
-
-		protected override void HandleRestGet (RequestContext _context) {
-			PrepareEnvelopedResult (out JsonWriter writer);
-			
-			writer.EnsureCapacity (largestBuffer);
-			writer.WriteBeginObject ();
-
-			bool first = true;
+		protected override void iterateList (ref JsonWriter _writer, ref bool _first) {
 			foreach (EnumGamePrefs stat in EnumUtils.Values<EnumGamePrefs> ()) {
 				string name = stat.ToStringCached ();
-				global::GamePrefs.EnumType? type = global::GamePrefs.GetPrefType (stat);
 
 				if (name.Contains ("Password", StringComparison.Ordinal)) {
@@ -27,86 +17,28 @@
 				}
 
-				if (!first) {
-					writer.WriteValueSeparator ();
-				}
+				global::GamePrefs.EnumType? type = global::GamePrefs.GetPrefType (stat);
+				object defaultValue = global::GamePrefs.GetDefault (stat);
 
-				first = false;
-				
-				writer.WritePropertyName (name);
-				
-				writer.WriteRaw (keyType);
-				writer.WriteString (type.HasValue ? type.Value.ToStringCached () : "noType");
-				
-				writer.WriteRaw (keyValue);
 				switch (type) {
 					case global::GamePrefs.EnumType.Int:
-						writer.WriteInt32 (global::GamePrefs.GetInt (stat));
+						int? defaultInt = defaultValue as int?;
+						addItem (ref _writer, ref _first, name, global::GamePrefs.GetInt (stat), defaultInt);
 						break;
 					case global::GamePrefs.EnumType.Float:
-						writer.WriteSingle (global::GamePrefs.GetFloat (stat));
+						float? defaultFloat = defaultValue as float?;
+						addItem (ref _writer, ref _first, name, global::GamePrefs.GetFloat (stat), defaultFloat);
 						break;
 					case global::GamePrefs.EnumType.String:
-						writer.WriteString (global::GamePrefs.GetString (stat));
+						string defaultString = defaultValue as string;
+						addItem (ref _writer, ref _first, name, global::GamePrefs.GetString (stat), defaultString);
 						break;
 					case global::GamePrefs.EnumType.Bool:
-						writer.WriteBoolean (global::GamePrefs.GetBool (stat));
-						break;
-					default:
-						writer.WriteNull ();
+						bool? defaultBool = defaultValue as bool?;
+						addItem (ref _writer, ref _first, name, global::GamePrefs.GetBool (stat), defaultBool);
 						break;
 				}
-				
-				writer.WriteRaw (keyDefault);
-				object defaultValue = global::GamePrefs.GetDefault (stat);
-				switch (type) {
-					case global::GamePrefs.EnumType.Int:
-						if (defaultValue is int defaultInt) {
-							writer.WriteInt32 (defaultInt);
-						} else {
-							writer.WriteNull ();
-						}
+			}
+		}
 
-						break;
-					case global::GamePrefs.EnumType.Float:
-						if (defaultValue is float defaultFloat) {
-							writer.WriteSingle (defaultFloat);
-						} else {
-							writer.WriteNull ();
-						}
-
-						break;
-					case global::GamePrefs.EnumType.String:
-						if (defaultValue is string defaultString) {
-							writer.WriteString (defaultString);
-						} else {
-							writer.WriteNull ();
-						}
-
-						break;
-					case global::GamePrefs.EnumType.Bool:
-						if (defaultValue is bool defaultBool) {
-							writer.WriteBoolean (defaultBool);
-						} else {
-							writer.WriteNull ();
-						}
-
-						break;
-					default:
-						writer.WriteNull ();
-						break;
-				}
-
-				writer.WriteEndObject ();
-			}
-			
-			writer.WriteEndObject ();
-
-			int bufferContentSize = writer.CurrentOffset + 128;
-			if (bufferContentSize > largestBuffer) {
-				largestBuffer = bufferContentSize;
-			}
-			
-			SendEnvelopedResult (_context, ref writer);
-		}
 	}
 }
Index: TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/GameStats.cs
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/GameStats.cs	(revision 453)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/GameStats.cs	(revision 457)
@@ -4,62 +4,28 @@
 namespace Webserver.WebAPI.APIs.ServerState {
 	[UsedImplicitly]
-	public class GameStats : AbsRestApi {
-		private static readonly byte[] keyType = JsonWriter.GetEncodedPropertyNameWithBeginObject ("type");
-		private static readonly byte[] keyValue = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("value");
+	public class GameStats : KeyValueListAbs {
+		public GameStats () : base ("GameStats") {
+		}
 
-		private int largestBuffer;
-
-		protected override void HandleRestGet (RequestContext _context) {
-			PrepareEnvelopedResult (out JsonWriter writer);
-			
-			writer.EnsureCapacity (largestBuffer);
-			writer.WriteBeginObject ();
-
-			bool first = true;
+		protected override void iterateList (ref JsonWriter _writer, ref bool _first) {
 			foreach (EnumGameStats stat in EnumUtils.Values<EnumGameStats> ()) {
 				string name = stat.ToStringCached ();
 				global::GameStats.EnumType? type = global::GameStats.GetStatType (stat);
 
-				if (!first) {
-					writer.WriteValueSeparator ();
-				}
-
-				first = false;
-				
-				writer.WritePropertyName (name);
-				
-				writer.WriteRaw (keyType);
-				writer.WriteString (type.HasValue ? type.Value.ToStringCached () : "noType");
-				
-				writer.WriteRaw (keyValue);
 				switch (type) {
 					case global::GameStats.EnumType.Int:
-						writer.WriteInt32 (global::GameStats.GetInt (stat));
+						addItem (ref _writer, ref _first, name, global::GameStats.GetInt (stat));
 						break;
 					case global::GameStats.EnumType.Float:
-						writer.WriteSingle (global::GameStats.GetFloat (stat));
+						addItem (ref _writer, ref _first, name, global::GameStats.GetFloat (stat));
 						break;
 					case global::GameStats.EnumType.String:
-						writer.WriteString (global::GameStats.GetString (stat));
+						addItem (ref _writer, ref _first, name, global::GameStats.GetString (stat));
 						break;
 					case global::GameStats.EnumType.Bool:
-						writer.WriteBoolean (global::GameStats.GetBool (stat));
-						break;
-					default:
-						writer.WriteNull ();
+						addItem (ref _writer, ref _first, name, global::GameStats.GetBool (stat));
 						break;
 				}
-				
-				writer.WriteEndObject ();
 			}
-			
-			writer.WriteEndObject ();
-
-			int bufferContentSize = writer.CurrentOffset + 128;
-			if (bufferContentSize > largestBuffer) {
-				largestBuffer = bufferContentSize;
-			}
-			
-			SendEnvelopedResult (_context, ref writer);
 		}
 	}
Index: TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/KeyValueListAbs.cs
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/KeyValueListAbs.cs	(revision 457)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/KeyValueListAbs.cs	(revision 457)
@@ -0,0 +1,153 @@
+using Utf8Json;
+
+namespace Webserver.WebAPI.APIs.ServerState {
+	public abstract class KeyValueListAbs : AbsRestApi {
+		private readonly UnityEngine.Profiling.CustomSampler buildSampler;
+
+		private static readonly byte[] keyName = JsonWriter.GetEncodedPropertyNameWithBeginObject ("name");
+		private static readonly byte[] keyType = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("type");
+		private static readonly byte[] keyValue = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("value");
+		private static readonly byte[] keyDefault = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("default");
+
+		private int largestBuffer;
+
+		protected KeyValueListAbs (string _listName) {
+			buildSampler = UnityEngine.Profiling.CustomSampler.Create ($"JSON_{_listName}_BuildSampler");
+		}
+
+		protected override void HandleRestGet (RequestContext _context) {
+			buildSampler.Begin ();
+
+			PrepareEnvelopedResult (out JsonWriter writer);
+			
+			writer.EnsureCapacity (largestBuffer);
+			writer.WriteBeginArray ();
+
+			bool first = true;
+			iterateList (ref writer, ref first);
+			
+			writer.WriteEndArray ();
+
+			buildSampler.End ();
+
+			int bufferContentSize = writer.CurrentOffset + 128;
+			if (bufferContentSize > largestBuffer) {
+				largestBuffer = bufferContentSize;
+			}
+			
+			SendEnvelopedResult (_context, ref writer);
+		}
+
+		private void writeKeyType (ref JsonWriter _writer, ref bool _first, string _key, string _type) {
+			if (!_first) {
+				_writer.WriteValueSeparator ();
+			}
+
+			_first = false;
+				
+			_writer.WriteRaw (keyName);
+			_writer.WriteString (_key);
+				
+			_writer.WriteRaw (keyType);
+			_writer.WriteString (_type);
+		}
+
+		private void writeInt (ref JsonWriter _writer, ref bool _first, string _key, int _value) {
+			writeKeyType (ref _writer, ref _first, _key, "int");
+			_writer.WriteRaw (keyValue);
+			_writer.WriteInt32 (_value);
+		}
+
+		protected void addItem (ref JsonWriter _writer, ref bool _first, string _key, int _value) {
+			writeInt (ref _writer, ref _first, _key, _value);
+			_writer.WriteEndObject ();
+		}
+
+		protected void addItem (ref JsonWriter _writer, ref bool _first, string _key, int _value, int? _default) {
+			writeInt (ref _writer, ref _first, _key, _value);
+
+			_writer.WriteRaw (keyDefault);
+			if (_default.HasValue) {
+				_writer.WriteInt32 (_default.Value);
+			} else {
+				_writer.WriteNull ();
+			}
+
+			_writer.WriteEndObject ();
+		}
+
+		
+		private void writeFloat (ref JsonWriter _writer, ref bool _first, string _key, float _value) {
+			writeKeyType (ref _writer, ref _first, _key, "float");
+			_writer.WriteRaw (keyValue);
+			_writer.WriteSingle (_value);
+		}
+
+		protected void addItem (ref JsonWriter _writer, ref bool _first, string _key, float _value) {
+			writeFloat (ref _writer, ref _first, _key, _value);
+			_writer.WriteEndObject ();
+		}
+
+		protected void addItem (ref JsonWriter _writer, ref bool _first, string _key, float _value, float? _default) {
+			writeFloat (ref _writer, ref _first, _key, _value);
+
+			_writer.WriteRaw (keyDefault);
+			if (_default.HasValue) {
+				_writer.WriteSingle (_default.Value);
+			} else {
+				_writer.WriteNull ();
+			}
+
+			_writer.WriteEndObject ();
+		}
+
+		
+		private void writeBool (ref JsonWriter _writer, ref bool _first, string _key, bool _value) {
+			writeKeyType (ref _writer, ref _first, _key, "bool");
+			_writer.WriteRaw (keyValue);
+			_writer.WriteBoolean (_value);
+		}
+
+		protected void addItem (ref JsonWriter _writer, ref bool _first, string _key, bool _value) {
+			writeBool (ref _writer, ref _first, _key, _value);
+			_writer.WriteEndObject ();
+		}
+
+		protected void addItem (ref JsonWriter _writer, ref bool _first, string _key, bool _value, bool? _default) {
+			writeBool (ref _writer, ref _first, _key, _value);
+
+			_writer.WriteRaw (keyDefault);
+			if (_default.HasValue) {
+				_writer.WriteBoolean (_default.Value);
+			} else {
+				_writer.WriteNull ();
+			}
+
+			_writer.WriteEndObject ();
+		}
+
+		
+		private void writeString (ref JsonWriter _writer, ref bool _first, string _key, string _value) {
+			writeKeyType (ref _writer, ref _first, _key, "string");
+			_writer.WriteRaw (keyValue);
+			_writer.WriteString (_value);
+		}
+
+		protected void addItem (ref JsonWriter _writer, ref bool _first, string _key, string _value) {
+			writeString (ref _writer, ref _first, _key, _value);
+			_writer.WriteEndObject ();
+		}
+
+		protected void addItem (ref JsonWriter _writer, ref bool _first, string _key, string _value, string _default) {
+			writeString (ref _writer, ref _first, _key, _value);
+
+			_writer.WriteRaw (keyDefault);
+			_writer.WriteString (_default);
+
+			_writer.WriteEndObject ();
+		}
+
+		protected abstract void iterateList (ref JsonWriter _writer, ref bool _first);
+
+	}
+}
Index: TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/ServerInfo.cs
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/ServerInfo.cs	(revision 453)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/ServerState/ServerInfo.cs	(revision 457)
@@ -6,43 +6,15 @@
 namespace Webserver.WebAPI.APIs.ServerState {
 	[UsedImplicitly]
-	public class ServerInfo : AbsRestApi {
-		private static readonly UnityEngine.Profiling.CustomSampler buildSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_ServerInfo_BuildSampler");
+	public class ServerInfo : KeyValueListAbs {
+		public ServerInfo () : base ("ServerInfo") {
+		}
 
-		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 ();
-			
+		protected override void iterateList (ref JsonWriter _writer, ref bool _first) {
 			GameServerInfo gsi = ConnectionManager.Instance.LocalServerInfo;
-
-			bool first = true;
 
 			IList<GameInfoString> list = EnumUtils.Values<GameInfoString> ();
 			for (int i = 0; i < list.Count; i++) {
 				GameInfoString stringGamePref = list [i];
-
-				if (!first) {
-					writer.WriteValueSeparator ();
-				}
-
-				first = false;
-
-				writer.WritePropertyName (stringGamePref.ToStringCached ());
-
-				writer.WriteRaw (keyType);
-				writer.WriteString ("string");
-
-				writer.WriteRaw (keyValue);
-				writer.WriteString (gsi.GetValue (stringGamePref));
-
-				writer.WriteEndObject ();
+				addItem (ref _writer, ref _first, stringGamePref.ToStringCached (), gsi.GetValue (stringGamePref));
 			}
 
@@ -50,20 +22,5 @@
 			for (int i = 0; i < ints.Count; i++) {
 				GameInfoInt intGamePref = ints [i];
-
-				if (!first) {
-					writer.WriteValueSeparator ();
-				}
-
-				first = false;
-
-				writer.WritePropertyName (intGamePref.ToStringCached ());
-
-				writer.WriteRaw (keyType);
-				writer.WriteString ("int");
-
-				writer.WriteRaw (keyValue);
-				writer.WriteInt32 (gsi.GetValue (intGamePref));
-
-				writer.WriteEndObject ();
+				addItem (ref _writer, ref _first, intGamePref.ToStringCached (), gsi.GetValue (intGamePref));
 			}
 
@@ -71,32 +28,6 @@
 			for (int i = 0; i < prefs.Count; i++) {
 				GameInfoBool boolGamePref = prefs [i];
-
-				if (!first) {
-					writer.WriteValueSeparator ();
-				}
-
-				first = false;
-
-				writer.WritePropertyName (boolGamePref.ToStringCached ());
-
-				writer.WriteRaw (keyType);
-				writer.WriteString ("bool");
-
-				writer.WriteRaw (keyValue);
-				writer.WriteBoolean (gsi.GetValue (boolGamePref));
-
-				writer.WriteEndObject ();
+				addItem (ref _writer, ref _first, boolGamePref.ToStringCached (), gsi.GetValue (boolGamePref));
 			}
-
-			writer.WriteEndObject ();
-			
-			buildSampler.End ();
-
-			int bufferContentSize = writer.CurrentOffset + 128;
-			if (bufferContentSize > largestBuffer) {
-				largestBuffer = bufferContentSize;
-			}
-			
-			SendEnvelopedResult (_context, ref writer);
 		}
 
