This commit is contained in:
zznty
2022-07-21 21:57:27 +07:00
commit bc4546410e
75 changed files with 2709 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
using System.Net;
using System.Reflection;
using System.Text.RegularExpressions;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using EmbedIO.WebSockets;
using Sandbox.Engine.Multiplayer;
using Sandbox.Game.Multiplayer;
using Sandbox.Game.World;
using Torch.API;
using Torch.API.Managers;
using Torch.API.Plugins;
using Torch.Commands;
using Torch.Managers;
using Torch.Utils;
using TorchRemote.Models.Requests;
using TorchRemote.Models.Responses;
using TorchRemote.Models.Shared;
using TorchRemote.Plugin.Modules;
using TorchRemote.Plugin.Utils;
using VRage.Network;
namespace TorchRemote.Plugin.Controllers;
public class ChatController : WebApiController
{
private const string RootPath = "/chat";
[ReflectedMethodInfo(typeof(MyMultiplayerBase), "OnChatMessageReceived_BroadcastExcept")]
private static readonly MethodInfo BroadcastExceptMethod = null!;
[ReflectedMethodInfo(typeof(MyMultiplayerBase), "OnChatMessageReceived_SingleTarget")]
private static readonly MethodInfo SingleTargetMethod = null!;
[Route(HttpVerbs.Post, $"{RootPath}/message")]
public void SendMessage([JsonData] ChatMessageRequest request)
{
if (MyMultiplayer.Static is null)
throw new HttpException(HttpStatusCode.ServiceUnavailable);
var msg = new ChatMsg
{
CustomAuthorName = request.Author,
Text = request.Message,
Channel = (byte)request.Channel,
TargetId = request.TargetId.GetValueOrDefault()
};
switch (request.Channel)
{
case ChatChannel.Global:
case ChatChannel.GlobalScripted when request.TargetId is null:
NetworkManager.RaiseStaticEvent(BroadcastExceptMethod, msg);
break;
case ChatChannel.Private when request.TargetId is not null:
case ChatChannel.GlobalScripted:
var steamId = Sync.Players.TryGetSteamId(request.TargetId.Value);
if (steamId == 0)
throw HttpException.NotFound($"Unable to find player with identity id {request.TargetId.Value}", request.TargetId.Value);
NetworkManager.RaiseStaticEvent(SingleTargetMethod, msg, new(steamId));
break;
case ChatChannel.Faction when request.TargetId is not null:
var faction = MySession.Static.Factions.TryGetFactionById(request.TargetId.Value);
if (faction is null)
throw HttpException.NotFound($"Unable to find faction with id {request.TargetId.Value}", request.TargetId.Value);
foreach (var playerId in faction.Members.Keys.Where(Sync.Players.IsPlayerOnline))
{
NetworkManager.RaiseStaticEvent(SingleTargetMethod, msg, new(Sync.Players.TryGetSteamId(playerId)));
}
break;
default:
throw HttpException.BadRequest("Invalid channel and targetId combination");
}
if (Statics.Torch.CurrentSession?.Managers.GetManager<IChatManagerServer>() is { } manager)
manager.DisplayMessageOnSelf(request.Author, request.Message);
}
[Route(HttpVerbs.Post, $"{RootPath}/command")]
public async Task<Guid> InvokeCommand([JsonData] ChatCommandRequest request)
{
if (Statics.CommandManager is null)
throw new HttpException(HttpStatusCode.ServiceUnavailable);
if (Statics.CommandManager.Commands.GetCommand(request.Command, out var argText) is not { } command)
throw HttpException.NotFound($"Unable to find command {request.Command}", request.Command);
var argsList = Regex.Matches(argText, "(\"[^\"]+\"|\\S+)").Cast<Match>().Select(x => x.ToString().Replace("\"", "")).ToList();
var id = new Guid();
var context = new WebSocketCommandContext(Statics.Torch, command.Plugin, argText, argsList, Statics.ChatModule, id);
if (await Statics.Torch.InvokeAsync(() => command.TryInvoke(context)))
return id;
throw HttpException.BadRequest("Invalid syntax", request.Command);
}
}
internal class WebSocketCommandContext : CommandContext
{
private readonly ChatModule _module;
private readonly Guid _id;
public WebSocketCommandContext(ITorchBase torch, ITorchPlugin plugin, string rawArgs, List<string> args, ChatModule module, Guid id) : base(torch, plugin, Sync.MyId, rawArgs, args)
{
_module = module;
_id = id;
}
public override void Respond(string message, string? sender = null, string? font = null)
{
_module.SendChatResponse(new ChatCommandResponse(_id, sender ?? Torch.Config.ChatName, message));
}
}

View File

@@ -0,0 +1,76 @@
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using Sandbox.Game.Multiplayer;
using Sandbox.Game.World;
using Torch.API.Session;
using TorchRemote.Models.Requests;
using TorchRemote.Models.Responses;
using TorchRemote.Models.Shared;
using TorchRemote.Plugin.Utils;
namespace TorchRemote.Plugin.Controllers;
public class ServerController : WebApiController
{
private const string RootPath = "/server";
[Route(HttpVerbs.Get, $"{RootPath}/status")]
public ServerStatusResponse GetStatus()
{
return new(Math.Round(Sync.ServerSimulationRatio, 2),
MySession.Static?.Players?.GetOnlinePlayerCount() ?? 0,
Statics.Torch.ElapsedPlayTime,
(ServerStatus)Statics.Torch.State);
}
[Route(HttpVerbs.Post, $"{RootPath}/start")]
public void Start()
{
if (!Statics.Torch.CanRun)
throw HttpException.BadRequest($"Server can't start in state {Statics.Torch.State}", Statics.Torch.State);
Statics.Torch.Start();
}
[Route(HttpVerbs.Post, $"{RootPath}/stop")]
public async Task Stop(StopServerRequest request)
{
if (!Statics.Torch.IsRunning)
throw HttpException.BadRequest($"Server can't stop in state {Statics.Torch.State}", Statics.Torch.State);
var saveResult = await Statics.Torch.Save(exclusive: true);
if (saveResult is not GameSaveResult.Success)
throw HttpException.InternalServerError($"Save resulted in {saveResult}", saveResult);
Statics.Torch.Stop();
}
[Route(HttpVerbs.Get, $"{RootPath}/settings")]
public ServerSettings GetSettings()
{
var settings = Statics.Torch.DedicatedInstance.DedicatedConfig;
return new(settings.ServerName ?? "unamed",
settings.WorldName ?? "unamed",
settings.ServerDescription ?? string.Empty,
settings.SessionSettings.MaxPlayers,
new(settings.IP, settings.Port));
}
[Route(HttpVerbs.Post, $"{RootPath}/settings")]
public async Task SetSettings([JsonData] ServerSettings request)
{
var settings = Statics.Torch.DedicatedInstance.DedicatedConfig;
settings.ServerName = request.ServerName;
settings.WorldName = request.MapName;
settings.ServerDescription = request.ServerDescription;
settings.SessionSettings.MaxPlayers = request.MemberLimit;
settings.IP = request.ListenEndPoint.Ip;
settings.Port = request.ListenEndPoint.Port;
if (Statics.Torch.IsRunning)
await Statics.Torch.InvokeAsync(request.ApplyDynamically);
}
}

View File

@@ -0,0 +1,24 @@
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using Swan;
using TorchRemote.Models.Responses;
using TorchRemote.Plugin.Utils;
namespace TorchRemote.Plugin.Controllers;
public class SettingsController : WebApiController
{
private const string RootPath = "/settings";
[Route(HttpVerbs.Get, $"{RootPath}/{{id}}")]
public SettingInfoResponse Get(Guid id)
{
if (!Statics.SettingManager.Settings.TryGetValue(id, out var setting))
throw HttpException.NotFound($"Setting with id {id} not found", id);
return new(setting.Name.Humanize(), setting.Properties.Select(b =>
new SettingPropertyInfo(b.DisplayInfo?.Name ?? b.Name.Humanize(),
b.DisplayInfo?.Description, b.DisplayInfo?.Order, b.TypeId))
.ToArray());
}
}

View File

@@ -0,0 +1,62 @@
using System.Net;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using TorchRemote.Models.Responses;
using TorchRemote.Plugin.Utils;
namespace TorchRemote.Plugin.Controllers;
public class WorldsController : WebApiController
{
private const string RootPath = "/worlds";
[Route(HttpVerbs.Get, RootPath)]
public IEnumerable<Guid> Get()
{
var config = Statics.InstanceManager.DedicatedConfig;
if (config is null)
throw new HttpException(HttpStatusCode.ServiceUnavailable);
return config.Worlds.Select(b => b.FolderName.ToGuid());
}
[Route(HttpVerbs.Get, $"{RootPath}/selected")]
public Guid GetSelected()
{
if (Statics.InstanceManager.DedicatedConfig?.SelectedWorld is not { } world)
throw new HttpException(HttpStatusCode.ServiceUnavailable);
return world.FolderName.ToGuid();
}
[Route(HttpVerbs.Get, $"{RootPath}/{{id}}")]
public WorldResponse GetWorld(Guid id)
{
var config = Statics.InstanceManager.DedicatedConfig;
if (config is null)
throw new HttpException(HttpStatusCode.ServiceUnavailable);
if (config.Worlds.FirstOrDefault(b => b.FolderName.ToGuid() == id) is not { } world)
throw HttpException.NotFound($"World not found by given id {id}", id);
return new(world.FolderName, world.WorldSizeKB);
}
[Route(HttpVerbs.Post, $"{RootPath}/{{id}}/select")]
public void Select(Guid id)
{
var config = Statics.InstanceManager.DedicatedConfig;
if (config is null)
throw new HttpException(HttpStatusCode.ServiceUnavailable);
if (config.Worlds.FirstOrDefault(b => b.FolderName.ToGuid() == id) is not { } world)
throw HttpException.NotFound($"World not found by given id {id}", id);
config.Model.IgnoreLastSession = true;
config.SelectedWorld = world;
config.Save();
}
}