first
This commit is contained in:
118
TorchRemote.Plugin/Controllers/ChatController.cs
Normal file
118
TorchRemote.Plugin/Controllers/ChatController.cs
Normal 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));
|
||||
}
|
||||
}
|
76
TorchRemote.Plugin/Controllers/ServerController.cs
Normal file
76
TorchRemote.Plugin/Controllers/ServerController.cs
Normal 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);
|
||||
}
|
||||
}
|
24
TorchRemote.Plugin/Controllers/SettingsController.cs
Normal file
24
TorchRemote.Plugin/Controllers/SettingsController.cs
Normal 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());
|
||||
}
|
||||
}
|
62
TorchRemote.Plugin/Controllers/WorldsController.cs
Normal file
62
TorchRemote.Plugin/Controllers/WorldsController.cs
Normal 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();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user