Index: TFP-WebServer/WebServer/src/WebAPI/APIs/Log.cs
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/Log.cs	(revision 457)
+++ 	(revision )
@@ -1,87 +1,0 @@
-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 ();
-
-			for (int i = 0; i < logEntries.Count; i++) {
-				LogBuffer.LogEntry logEntry = logEntries [i];
-				
-				if (i > 0) {
-					writer.WriteValueSeparator ();
-				}
-
-				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: TFP-WebServer/WebServer/src/WebAPI/APIs/LogApi.cs
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/LogApi.cs	(revision 459)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/LogApi.cs	(revision 459)
@@ -0,0 +1,90 @@
+using System.Collections.Generic;
+using JetBrains.Annotations;
+using Utf8Json;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	public class LogApi : 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");
+
+		public LogApi () : base ("Log") {
+		}
+
+		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 ();
+
+			for (int i = 0; i < logEntries.Count; i++) {
+				LogBuffer.LogEntry logEntry = logEntries [i];
+				
+				if (i > 0) {
+					writer.WriteValueSeparator ();
+				}
+
+				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: TFP-WebServer/WebServer/src/WebAPI/APIs/OpenAPI.cs
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/OpenAPI.cs	(revision 459)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/OpenAPI.cs	(revision 459)
@@ -0,0 +1,24 @@
+using System.Net;
+using JetBrains.Annotations;
+using Webserver.Permissions;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	public class OpenAPI : AbsRestApi {
+		public OpenAPI (Web _parentWeb) : base (_parentWeb) {
+		}
+
+		protected override void HandleRestGet (RequestContext _context) {
+			string path = _context.RequestPath;
+			
+			if (!ParentWeb.OpenApiHelpers.TryGetOpenApiSpec (path, out string specText)) {
+				WebUtils.WriteText (_context.Response, $"Spec for {path} not found", HttpStatusCode.NotFound);
+				return;
+			}
+
+			WebUtils.WriteText (_context.Response, specText, _mimeType: "text/x-yaml");
+		}
+
+		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
+	}
+}
Index: TFP-WebServer/WebServer/src/WebAPI/APIs/OpenAPI.openapi.yaml
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/OpenAPI.openapi.yaml	(revision 459)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/OpenAPI.openapi.yaml	(revision 459)
@@ -0,0 +1,38 @@
+openapi: 3.1.0
+info:
+  title: OpenAPI
+  version: 1
+
+
+paths:
+  /api/OpenAPI/openapi.yaml:
+    get:
+      operationId: OpenAPI.openapi
+      responses:
+        200:
+          content:
+            application/json: {}
+          description: OpenAPI master document
+      tags:
+        - Meta
+  /api/OpenAPI/{name}.openapi.yaml:
+    get:
+      operationId: OpenAPI.getAPI
+      parameters:
+        - in: path
+          name: name
+          required: true
+          schema:
+            pattern: '[^\/#\?]+?'
+            type: string
+      responses:
+        200:
+          content:
+            application/json: {}
+          description: OpenAPI spec for API
+        404:
+          content:
+            application/json: {}
+          description: No OpenAPI spec found for the API name
+      tags:
+        - Meta
Index: TFP-WebServer/WebServer/src/WebAPI/APIs/Permissions/RegisterUser.cs
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/Permissions/RegisterUser.cs	(revision 457)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/Permissions/RegisterUser.cs	(revision 459)
@@ -95,10 +95,10 @@
 			// Log info
 			string crossplatformidString = regData.CrossPlatformUserId == null ? "" : $", crossplatform ID {regData.CrossPlatformUserId.CombinedString}";
-			global::Log.Out ($"[Web] User registered: Username '{username}' for platform ID {regData.PlatformUserId.CombinedString}{crossplatformidString}");
+			Log.Out ($"[Web] User registered: Username '{username}' for platform ID {regData.PlatformUserId.CombinedString}{crossplatformidString}");
 
 			if (AdminWebUsers.Instance.HasUser (regData.PlatformUserId, regData.CrossPlatformUserId, out AdminWebUsers.WebUser existingUser)) {
 				// Remove existing username of player, only allowing one user per player
 
-				global::Log.Out ($"[Web] Re-registration, replacing existing username '{existingUser.Name}'");
+				Log.Out ($"[Web] Re-registration, replacing existing username '{existingUser.Name}'");
 				AdminWebUsers.Instance.RemoveUser (existingUser.Name);
 			}
Index: TFP-WebServer/WebServer/src/WebAPI/APIs/WorldState/Animal.openapi.yaml
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/WorldState/Animal.openapi.yaml	(revision 459)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/WorldState/Animal.openapi.yaml	(revision 459)
@@ -0,0 +1,58 @@
+openapi: 3.1.0
+info:
+  title: Animal
+  version: 1
+
+components:
+  schemas:
+    AnimalElement:
+      type: object
+      properties:
+        id:
+          $ref: '#/components/schemas/TypeEntityId'
+        name:
+          type: string
+          examples:
+            - animalStag
+        position:
+          $ref: '#/components/schemas/TypeVector3i'
+      required:
+        - id
+        - name
+        - position
+
+    AnimalList:
+      type: array
+      items:
+        $ref: '#/components/schemas/AnimalElement'
+
+
+paths:
+  /api/animal:
+    get:
+      tags:
+        - WorldState
+      summary: Animals list
+      description: Fetch a list of the currently spawned animals in the world
+      operationId: animal.get
+      responses:
+        200:
+          description: List of animals
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  data:
+                    $ref: '#/components/schemas/AnimalList'
+                  meta:
+                    $ref: '#/components/schemas/ResultEnvelopeMeta'
+                required:
+                  - data
+                  - meta
+        403:
+          $ref: '#/components/responses/Unauthorized'
+      security:
+        - apiTokenName: []
+          apiTokenSecret: []
+        - sessionCookie: []
Index: TFP-WebServer/WebServer/src/WebAPI/APIs/WorldState/Player.cs
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/WorldState/Player.cs	(revision 457)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/WorldState/Player.cs	(revision 459)
@@ -93,5 +93,5 @@
 			ClientInfo ci = ConnectionManager.Instance.Clients.ForUserId (_nativeUserId);
 			if (ci == null) {
-				global::Log.Warning ($"[Web] Player.GET: ClientInfo null");
+				Log.Warning ($"[Web] Player.GET: ClientInfo null");
 				return;
 			}
@@ -101,5 +101,5 @@
 
 			if (entity == null) {
-				global::Log.Warning ($"[Web] Player.GET: EntityPlayer null");
+				Log.Warning ($"[Web] Player.GET: EntityPlayer null");
 				return;
 			}
