155 lines
6.2 KiB
C#
155 lines
6.2 KiB
C#
using System.Net;
|
|
using System.Reflection;
|
|
using System.Text.Json;
|
|
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.Abstractions.Controllers;
|
|
using TorchRemote.Plugin.Modules;
|
|
using TorchRemote.Plugin.Utils;
|
|
using VRage.Network;
|
|
namespace TorchRemote.Plugin.Controllers;
|
|
|
|
public class ChatController : WebApiController, IChatController
|
|
{
|
|
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 ?? Statics.Torch.Config.ChatName,
|
|
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 &&
|
|
request.Channel is ChatChannel.Global or ChatChannel.GlobalScripted)
|
|
manager.DisplayMessageOnSelf(msg.CustomAuthorName, msg.Text);
|
|
}
|
|
|
|
[Route(HttpVerbs.Post, $"{RootPath}/command")]
|
|
public async Task 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 = Guid.NewGuid();
|
|
|
|
CommandContext context = request.Streamed ? new StreamedCommandContext(Statics.Torch, command.Plugin, argText, argsList, id) :
|
|
new WebSocketCommandContext(Statics.Torch, command.Plugin, argText, argsList, Statics.ChatModule, id);
|
|
|
|
if (!await Statics.Torch.InvokeAsync(() => command.TryInvoke(context)))
|
|
throw HttpException.BadRequest("Invalid syntax", request.Command);
|
|
|
|
Response.StatusCode = 200;
|
|
Response.ContentType = "application/json";
|
|
|
|
if (request.Streamed)
|
|
{
|
|
await Task.Delay(request.StreamingDuration ?? TimeSpan.FromSeconds(15));
|
|
await JsonSerializer.SerializeAsync(Response.OutputStream, ((StreamedCommandContext)context).Answers, Statics.SerializerOptions);
|
|
}
|
|
else
|
|
{
|
|
await JsonSerializer.SerializeAsync(Response.OutputStream, id, Statics.SerializerOptions);
|
|
}
|
|
|
|
Response.Close();
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
internal class StreamedCommandContext : CommandContext
|
|
{
|
|
private readonly Guid _id;
|
|
private readonly List<ChatCommandResponse> _answers = new();
|
|
|
|
public IEnumerable<ChatCommandResponse> Answers => _answers;
|
|
|
|
public StreamedCommandContext(ITorchBase torch, ITorchPlugin plugin, string rawArgs, List<string> args, Guid id) : base(
|
|
torch, plugin, Sync.MyId, rawArgs, args)
|
|
{
|
|
_id = id;
|
|
}
|
|
|
|
public override void Respond(string message, string? sender = null, string? font = null)
|
|
{
|
|
_answers.Add(new ChatCommandResponse(_id, sender ?? Torch.Config.ChatName, message));
|
|
}
|
|
} |