M Connections/ServerLogConnection.cs => Connections/ServerLogConnection.cs +3 -1
@@ 1,9 1,11 @@
using WebSocketSharp;
using WebSocketSharp.Server;
-namespace Mamoru.Routes;
+namespace Mamoru.Connections;
public class ServerLogConnection : WebSocketBehavior {
+ public static string Path = "/serverlog";
+
protected override void OnMessage(MessageEventArgs e) {
Send(e.Data);
Log.Info(e.Data);
M Mamoru.cs => Mamoru.cs +3 -3
@@ 7,9 7,9 @@ using YamlDotNet.Serialization.NamingConventions;
namespace Mamoru;
public class Mamoru : Plugin<Config> {
- internal static Mamoru Instance { get; } = new();
- internal ConfigManager ConfigManager = null!;
- internal RoutesManager RoutesManager = null!;
+ public static Mamoru Instance { get; } = new();
+ public ConfigManager ConfigManager = null!;
+ public RoutesManager RoutesManager = null!;
public override string Name => "Mamoru";
public override string Author => "furry";
M Mamoru.csproj => Mamoru.csproj +0 -1
@@ 11,7 11,6 @@
<ItemGroup>
<PackageReference Include="EXILED" Version="9.0.0-beta.2"/>
<PackageReference Include="System.Text.Json" Version="9.0.0-rc.1.24431.7" />
- <PackageReference Include="Unity3D" Version="3.0.0"/>
<PackageReference Include="WebSocketSharp" Version="1.0.3-rc11"/>
</ItemGroup>
M Managers/RoutesManager.cs => Managers/RoutesManager.cs +58 -14
@@ 1,37 1,81 @@
+using System.Net;
using System.Reflection;
-using System.Text;
+using Exiled.API.Extensions;
using Exiled.API.Features;
-using Mamoru.Routes;
+using Mamoru.Connections;
+using Mamoru.Utils;
using WebSocketSharp.Server;
namespace Mamoru.Managers;
public class RoutesManager {
- private readonly HttpServer _httpServer = new(System.Net.IPAddress.Loopback, 4649);
+ private readonly HttpServer _httpServer = new(IPAddress.Loopback, 4649);
+ private readonly Dictionary<string, AbstractRoute> _httpRoutes = new();
public RoutesManager() {
StartHttpServer();
}
- private void StartHttpServer() {
- Log.Info("Setting up HTTP server...");
- var types = Assembly.GetExecutingAssembly()
+ private void HandleRequest(object sender, HttpRequestEventArgs ev) {
+ // _httpServer.OnGet += (_, e) => {
+ // var req = e.Request;
+ // var res = e.Response;
+ //
+ // var route = httpRoutes[req.RawUrl];
+ // if (route == null) {
+ // EmptyResponse.Create(ref res, HttpStatusCode.NotFound);
+ // return;
+ // }
+ //
+ // route.Get(ref req, ref res);
+ // };
+ Log.Info($"{ev.Request.HttpMethod} {ev.Request.RawUrl}");
+ Log.Info(string.Join(Environment.NewLine, _httpRoutes));
+ }
+
+ private void LoadRoutes() {
+ Log.Info("Loading routes...");
+ Assembly.GetExecutingAssembly()
.GetTypes()
- .Where(type => type.IsClass && type is { IsAbstract: false, Namespace: "Mamoru.Routes" });
+ .Where(type => type.IsClass && type is { IsAbstract: false, Namespace: "Mamoru.Routes" })
+ .ToArray()
+ .ForEach((type) => {
+ try {
+ var clazz = (AbstractRoute)Activator.CreateInstance(type);
+ _httpRoutes[clazz.Path] = clazz;
+ Log.Info($"Loaded http route {type.Name} at {clazz.Path}");
+ }
+ catch {
+ Log.Warn($"Failed to load http route {type.Name}");
+ }
+ });
- foreach (var type in types) {
- Log.Info($"detected {type.Name}");
- }
+ _httpServer.AddWebSocketService<ServerLogConnection>(ServerLogConnection.Path);
+ }
-
- _httpServer.OnGet += (_, e) => { };
- _httpServer.AddWebSocketService<ServerLogConnection>("/");
+ private void RegisterEvents() {
+ var methodInfo = GetType().GetMethod("HandleRequest", BindingFlags.Instance | BindingFlags.NonPublic);
+ _httpServer.GetType().GetEvents().ForEach(eventInfo => {
+ eventInfo.AddEventHandler(_httpServer, Delegate.CreateDelegate(
+ eventInfo.EventHandlerType,
+ this,
+ methodInfo!
+ ));
+ });
+ }
+
+ private void StartHttpServer() {
+ Log.Info("Setting up HTTP server...");
+ LoadRoutes();
+
+ Log.Info("Registering HandleRequest events...");
+ RegisterEvents();
Log.Info("Starting HTTP server...");
_httpServer.Start();
}
- internal void StopHttpServer() {
+ public void StopHttpServer() {
Log.Info("Stopping HTTP server...");
_httpServer.Stop();
}
M Models.DedicatedServer/GameplayConfig.cs => Models.DedicatedServer/GameplayConfig.cs +1 -0
@@ 1,4 1,5 @@
using YamlDotNet.Serialization;
+#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Mamoru.Models.DedicatedServer;
M Models.DedicatedServer/RemoteAdminConfig.cs => Models.DedicatedServer/RemoteAdminConfig.cs +1 -0
@@ 1,4 1,5 @@
using YamlDotNet.Serialization;
+#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Mamoru.Models.DedicatedServer;
M Routes/StatusRoute.cs => Routes/StatusRoute.cs +2 -2
@@ 8,7 8,7 @@ public class StatusRoute : AbstractRoute {
public override string Path => "/.well-known/mamoru/status";
public override bool Cors => true;
- public override IResponse Get(ref HttpListenerRequest req, ref HttpListenerResponse res) {
- return new JsonResponse(ref res, new Status(true, true, "1", "2"));
+ public override void Get(ref HttpListenerRequest req, ref HttpListenerResponse res) {
+ JsonResponse.Create(ref res, new Status(true, true, "1", "2"));
}
}
M Utils/AbstractRoute.cs => Utils/AbstractRoute.cs +18 -18
@@ 6,39 6,39 @@ public abstract class AbstractRoute {
public abstract string Path { get; }
public virtual bool Cors => false;
- public virtual IResponse Connect(ref HttpListenerRequest req, ref HttpListenerResponse res) {
- return new EmptyResponse(ref res, HttpStatusCode.MethodNotAllowed);
+ public virtual void Connect(ref HttpListenerRequest req, ref HttpListenerResponse res) {
+ EmptyResponse.Create(ref res, HttpStatusCode.MethodNotAllowed);
}
- public virtual IResponse Delete(ref HttpListenerRequest req, ref HttpListenerResponse res) {
- return new EmptyResponse(ref res, HttpStatusCode.MethodNotAllowed);
+ public virtual void Delete(ref HttpListenerRequest req, ref HttpListenerResponse res) {
+ EmptyResponse.Create(ref res, HttpStatusCode.MethodNotAllowed);
}
- public virtual IResponse Get(ref HttpListenerRequest req, ref HttpListenerResponse res) {
- return new EmptyResponse(ref res, HttpStatusCode.MethodNotAllowed);
+ public virtual void Get(ref HttpListenerRequest req, ref HttpListenerResponse res) {
+ EmptyResponse.Create(ref res, HttpStatusCode.MethodNotAllowed);
}
- public virtual IResponse Head(ref HttpListenerRequest req, ref HttpListenerResponse res) {
- return new EmptyResponse(ref res, HttpStatusCode.MethodNotAllowed);
+ public virtual void Head(ref HttpListenerRequest req, ref HttpListenerResponse res) {
+ EmptyResponse.Create(ref res, HttpStatusCode.MethodNotAllowed);
}
- public virtual IResponse Options(ref HttpListenerRequest req, ref HttpListenerResponse res) {
- return new EmptyResponse(ref res, HttpStatusCode.MethodNotAllowed);
+ public virtual void Options(ref HttpListenerRequest req, ref HttpListenerResponse res) {
+ EmptyResponse.Create(ref res, HttpStatusCode.MethodNotAllowed);
}
- public virtual IResponse Post(ref HttpListenerRequest req, ref HttpListenerResponse res) {
- return new EmptyResponse(ref res, HttpStatusCode.MethodNotAllowed);
+ public virtual void Post(ref HttpListenerRequest req, ref HttpListenerResponse res) {
+ EmptyResponse.Create(ref res, HttpStatusCode.MethodNotAllowed);
}
- public virtual IResponse Patch(ref HttpListenerRequest req, ref HttpListenerResponse res) {
- return new EmptyResponse(ref res, HttpStatusCode.MethodNotAllowed);
+ public virtual void Patch(ref HttpListenerRequest req, ref HttpListenerResponse res) {
+ EmptyResponse.Create(ref res, HttpStatusCode.MethodNotAllowed);
}
- public virtual IResponse Put(ref HttpListenerRequest req, ref HttpListenerResponse res) {
- return new EmptyResponse(ref res, HttpStatusCode.MethodNotAllowed);
+ public virtual void Put(ref HttpListenerRequest req, ref HttpListenerResponse res) {
+ EmptyResponse.Create(ref res, HttpStatusCode.MethodNotAllowed);
}
- public virtual IResponse Trace(ref HttpListenerRequest req, ref HttpListenerResponse res) {
- return new EmptyResponse(ref res, HttpStatusCode.MethodNotAllowed);
+ public virtual void Trace(ref HttpListenerRequest req, ref HttpListenerResponse res) {
+ EmptyResponse.Create(ref res, HttpStatusCode.MethodNotAllowed);
}
}=
\ No newline at end of file
M Utils/Response.cs => Utils/Response.cs +4 -6
@@ 4,10 4,8 @@ using WebSocketSharp.Net;
namespace Mamoru.Utils;
-public interface IResponse;
-
-public sealed class JsonResponse : IResponse {
- public JsonResponse(ref HttpListenerResponse res, object value, HttpStatusCode statusCode = HttpStatusCode.OK) {
+public static class JsonResponse {
+ public static void Create(ref HttpListenerResponse res, object value, HttpStatusCode statusCode = HttpStatusCode.OK) {
var data = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(value));
res.StatusCode = (int)statusCode;
res.ContentType = "application/json";
@@ 17,8 15,8 @@ public sealed class JsonResponse : IResponse {
}
}
-public sealed class EmptyResponse : IResponse {
- public EmptyResponse(ref HttpListenerResponse res, HttpStatusCode statusCode = HttpStatusCode.OK) {
+public static class EmptyResponse {
+ public static void Create(ref HttpListenerResponse res, HttpStatusCode statusCode = HttpStatusCode.OK) {
res.StatusCode = (int)statusCode;
res.ContentLength64 = 0;
res.Close();