Added player detector implementation
Added computer event listeners
This commit is contained in:
parent
92aafcde70
commit
e7b056342f
@ -73,6 +73,12 @@ local function getResponse(parsed)
|
|||||||
return textutils.serializeJSON(item)
|
return textutils.serializeJSON(item)
|
||||||
elseif parsed.method == "command" then
|
elseif parsed.method == "command" then
|
||||||
return runRsCommand(parsed.params)
|
return runRsCommand(parsed.params)
|
||||||
|
elseif parsed.method == "getonline" then
|
||||||
|
return textutils.serializeJSON(getPeripheral("playerDetector").getOnlinePlayers())
|
||||||
|
elseif parsed.method == "whereis" then
|
||||||
|
local pos = getPeripheral("playerDetector").getPlayerPos(parsed.params.username)
|
||||||
|
if not pos then return "null" end
|
||||||
|
return textutils.serializeJSON(pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
error({message = "No message handler for method: "..parsed.method.."!"})
|
error({message = "No message handler for method: "..parsed.method.."!"})
|
||||||
@ -119,13 +125,7 @@ local function handleMessage(socket, message)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local function socketClient()
|
local function responder(socket)
|
||||||
print("Connecting to the socket server at "..connectionUri.."...")
|
|
||||||
local socket, reason = http.websocket(connectionUri)
|
|
||||||
if not socket then error("Socket server could not be reached: "..reason) end
|
|
||||||
print("Connection successful!")
|
|
||||||
|
|
||||||
socket.send("login="..secretToken)
|
|
||||||
while true do
|
while true do
|
||||||
local message, binary = socket.receive()
|
local message, binary = socket.receive()
|
||||||
if not not message and not binary then
|
if not not message and not binary then
|
||||||
@ -142,15 +142,60 @@ local function termWaiter()
|
|||||||
os.pullEvent("terminate")
|
os.pullEvent("terminate")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function services()
|
local function chatEventListener(socket)
|
||||||
parallel.waitForAny(termWaiter, function()
|
while true do
|
||||||
parallel.waitForAll(socketClient)
|
event, username, message, uuid, hidden = os.pullEvent("chat")
|
||||||
end)
|
sendJson(socket, {type = "chat", username = username, message = message, uuid = uuid, hidden = hidden})
|
||||||
|
print("Chat event relayed!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getPeripheralInfo(side)
|
||||||
|
return {type = peripheral.getType(side), methods = peripheral.getMethods(side), side = side}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function peripheralDetachEventListener(socket)
|
||||||
|
while true do
|
||||||
|
event, side = os.pullEvent("peripheral_detach")
|
||||||
|
sendJson(socket, {type = "peripheral_detach", side = side})
|
||||||
|
print("Peripheral was detached!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function peripheralAttachEventListener(socket)
|
||||||
|
while true do
|
||||||
|
event, side = os.pullEvent("peripheral")
|
||||||
|
sendJson(socket, {type = "peripheral", peripheral = getPeripheralInfo(side) })
|
||||||
|
print("Peripheral was attached!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function eventListeners(socket)
|
||||||
|
parallel.waitForAny(
|
||||||
|
termWaiter,
|
||||||
|
function() chatEventListener(socket) end,
|
||||||
|
function() peripheralDetachEventListener(socket) end,
|
||||||
|
function() peripheralAttachEventListener(socket) end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function socketClient()
|
||||||
|
print("Connecting to the socket server at "..connectionUri.."...")
|
||||||
|
local socket, reason = http.websocket(connectionUri)
|
||||||
|
if not socket then error("Socket server could not be reached: "..reason) end
|
||||||
|
print("Connection successful!")
|
||||||
|
|
||||||
|
socket.send("login="..secretToken)
|
||||||
|
parallel.waitForAny(
|
||||||
|
function() responder(socket) end,
|
||||||
|
function() eventListeners(socket) end
|
||||||
|
)
|
||||||
|
socket.close()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function main()
|
local function main()
|
||||||
while true do
|
while true do
|
||||||
local status, error = pcall(services)
|
local status, error = pcall(socketClient)
|
||||||
if status then break end
|
if status then break end
|
||||||
printError("An uncaught exception was raised:", error)
|
printError("An uncaught exception was raised:", error)
|
||||||
printError("Restarting in", waitSeconds, "seconds...")
|
printError("Restarting in", waitSeconds, "seconds...")
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
|
using MinecraftDiscordBot.Models;
|
||||||
using MinecraftDiscordBot.Services;
|
using MinecraftDiscordBot.Services;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -8,6 +9,7 @@ namespace MinecraftDiscordBot.Commands;
|
|||||||
public abstract class CommandRouter : ICommandHandler<ResponseType> {
|
public abstract class CommandRouter : ICommandHandler<ResponseType> {
|
||||||
public record struct HandlerStruct(HandleCommandDelegate<ResponseType> Delegate, CommandHandlerAttribute Attribute);
|
public record struct HandlerStruct(HandleCommandDelegate<ResponseType> Delegate, CommandHandlerAttribute Attribute);
|
||||||
private readonly Dictionary<string, HandlerStruct> _handlers = new();
|
private readonly Dictionary<string, HandlerStruct> _handlers = new();
|
||||||
|
|
||||||
public abstract string HelpTextPrefix { get; }
|
public abstract string HelpTextPrefix { get; }
|
||||||
public CommandRouter() {
|
public CommandRouter() {
|
||||||
foreach (var method in GetType().GetMethods())
|
foreach (var method in GetType().GetMethods())
|
||||||
|
@ -64,12 +64,37 @@ public class ReplyMessage : Message {
|
|||||||
|
|
||||||
public abstract class EventMessage : Message { }
|
public abstract class EventMessage : Message { }
|
||||||
|
|
||||||
|
[MessageType(TYPE)]
|
||||||
|
public class PeripheralDetachEvent : EventMessage {
|
||||||
|
private const string TYPE = "peripheral_detach";
|
||||||
|
public override string Type => TYPE;
|
||||||
|
[JsonProperty("side", Required = Required.Always)]
|
||||||
|
public string Side { get; set; } = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MessageType(TYPE)]
|
||||||
|
public class PeripheralAttachEvent : EventMessage {
|
||||||
|
private const string TYPE = "peripheral";
|
||||||
|
public override string Type => TYPE;
|
||||||
|
[JsonIgnore]
|
||||||
|
public string Side => Peripheral.Side;
|
||||||
|
[JsonProperty("peripheral", Required = Required.Always)]
|
||||||
|
public Peripheral Peripheral { get; set; } = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Peripheral {
|
||||||
|
[JsonProperty("side", Required = Required.Always)]
|
||||||
|
public string Side { get; set; } = default!;
|
||||||
|
[JsonProperty("type", Required = Required.Always)]
|
||||||
|
public string Type { get; set; } = default!;
|
||||||
|
[JsonProperty("methods", Required = Required.Always)]
|
||||||
|
public string[] Methods { get; set; } = default!;
|
||||||
|
}
|
||||||
|
|
||||||
[MessageType(TYPE)]
|
[MessageType(TYPE)]
|
||||||
public class ChatEvent : EventMessage {
|
public class ChatEvent : EventMessage {
|
||||||
private const string TYPE = "chat";
|
private const string TYPE = "chat";
|
||||||
public override string Type => TYPE;
|
public override string Type => TYPE;
|
||||||
[JsonProperty("name", Required = Required.Always)]
|
|
||||||
public string Name { get; set; } = default!;
|
|
||||||
[JsonProperty("username", Required = Required.Always)]
|
[JsonProperty("username", Required = Required.Always)]
|
||||||
public string Username { get; set; } = default!;
|
public string Username { get; set; } = default!;
|
||||||
[JsonProperty("message", Required = Required.Always)]
|
[JsonProperty("message", Required = Required.Always)]
|
||||||
|
13
MinecraftDiscordBot/Models/PlayerPosition.cs
Normal file
13
MinecraftDiscordBot/Models/PlayerPosition.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace MinecraftDiscordBot.Models;
|
||||||
|
|
||||||
|
public class PlayerPosition {
|
||||||
|
[JsonProperty("dimension", Required = Required.Always)] public string Dimension { get; set; } = default!;
|
||||||
|
[JsonProperty("eyeHeight", Required = Required.Always)] public double EyeHeight { get; set; }
|
||||||
|
[JsonProperty("pitch", Required = Required.Always)] public double Pitch { get; set; }
|
||||||
|
[JsonProperty("yaw", Required = Required.Always)] public double Yaw { get; set; }
|
||||||
|
[JsonProperty("x", Required = Required.Always)] public int X { get; set; }
|
||||||
|
[JsonProperty("y", Required = Required.Always)] public int Y { get; set; }
|
||||||
|
[JsonProperty("z", Required = Required.Always)] public int Z { get; set; }
|
||||||
|
}
|
@ -27,8 +27,9 @@ public class Program : IDisposable, ICommandHandler<ResponseType>, IUserRoleMana
|
|||||||
private readonly ConcurrentDictionary<Guid, RootCommandService> _connections = new();
|
private readonly ConcurrentDictionary<Guid, RootCommandService> _connections = new();
|
||||||
private static readonly char[] WhiteSpace = new char[] { '\t', '\n', ' ', '\r' };
|
private static readonly char[] WhiteSpace = new char[] { '\t', '\n', ' ', '\r' };
|
||||||
public ITextChannel[] _channels = Array.Empty<ITextChannel>();
|
public ITextChannel[] _channels = Array.Empty<ITextChannel>();
|
||||||
private RootCommandService? _rsSystem = null;
|
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
|
private readonly RootCommandService _computer;
|
||||||
|
|
||||||
public static bool OnlineNotifications => true;
|
public static bool OnlineNotifications => true;
|
||||||
private const string ClientScriptName = "MinecraftDiscordBot.ClientScript.lua";
|
private const string ClientScriptName = "MinecraftDiscordBot.ClientScript.lua";
|
||||||
public readonly string ClientScript;
|
public readonly string ClientScript;
|
||||||
@ -46,21 +47,10 @@ public class Program : IDisposable, ICommandHandler<ResponseType>, IUserRoleMana
|
|||||||
.Replace("$HOST", $"ws://{config.SocketHost}:{config.Port}");
|
.Replace("$HOST", $"ws://{config.SocketHost}:{config.Port}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public RootCommandService? Computer {
|
|
||||||
get => _rsSystem; set {
|
|
||||||
if (_rsSystem != value) {
|
|
||||||
_rsSystem = value;
|
|
||||||
if (OnlineNotifications)
|
|
||||||
_ = Task.Run(() => Broadcast(i => i.SendMessageAsync(value is null
|
|
||||||
? $"The Minecraft client has gone offline!"
|
|
||||||
: $"The Minecraft client is now online!")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Broadcast(Func<ITextChannel, Task<IUserMessage>> message) => _ = await Task.WhenAll(_channels.Select(message));
|
private async Task Broadcast(Func<ITextChannel, Task<IUserMessage>> message) => _ = await Task.WhenAll(_channels.Select(message));
|
||||||
public Program(BotConfiguration config) {
|
public Program(BotConfiguration config) {
|
||||||
_config = config;
|
_config = config;
|
||||||
|
_computer = new(this);
|
||||||
_administrators = config.Administrators.ToHashSet();
|
_administrators = config.Administrators.ToHashSet();
|
||||||
ClientScript = GetClientScript(config);
|
ClientScript = GetClientScript(config);
|
||||||
_client.Log += LogAsync;
|
_client.Log += LogAsync;
|
||||||
@ -142,17 +132,21 @@ public class Program : IDisposable, ICommandHandler<ResponseType>, IUserRoleMana
|
|||||||
await (message switch {
|
await (message switch {
|
||||||
"getcode" => SendClientCode(socket),
|
"getcode" => SendClientCode(socket),
|
||||||
string s when s.StartsWith("login=") => ClientComputerConnected(socket, s[6..]),
|
string s when s.StartsWith("login=") => ClientComputerConnected(socket, s[6..]),
|
||||||
|
string s when s.StartsWith("error=") => ClientComputerError(socket, s[6..]),
|
||||||
_ => DisruptClientConnection(socket, "Protocol violation!")
|
_ => DisruptClientConnection(socket, "Protocol violation!")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task ClientComputerError(IWebSocketConnection socket, string message)
|
||||||
|
=> await LogWarningAsync("Client", $"Computer failed to run the script: {message}");
|
||||||
|
|
||||||
private async Task ClientComputerConnected(IWebSocketConnection socket, string token) {
|
private async Task ClientComputerConnected(IWebSocketConnection socket, string token) {
|
||||||
if (!_tokenProvider.VerifyToken(token)) {
|
if (!_tokenProvider.VerifyToken(token)) {
|
||||||
await DisruptClientConnection(socket, "outdated");
|
await DisruptClientConnection(socket, "outdated");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await LogInfoAsync(WebSocketSource, $"[{socket.ConnectionInfo.Id}] Client logged in with valid script!");
|
await LogInfoAsync(WebSocketSource, $"[{socket.ConnectionInfo.Id}] Client logged in with valid script!");
|
||||||
AddComputerSocket(socket, new(socket, this));
|
AddComputerSocket(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task DisruptClientConnection(IWebSocketConnection socket, string reason) {
|
private static async Task DisruptClientConnection(IWebSocketConnection socket, string reason) {
|
||||||
@ -166,10 +160,10 @@ public class Program : IDisposable, ICommandHandler<ResponseType>, IUserRoleMana
|
|||||||
await LogInfoAsync(WebSocketSource, $"[{socket.ConnectionInfo.Id}] Script sent to client!");
|
await LogInfoAsync(WebSocketSource, $"[{socket.ConnectionInfo.Id}] Script sent to client!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddComputerSocket(IWebSocketConnection socket, RootCommandService pc) => Computer = pc;
|
private void AddComputerSocket(IWebSocketConnection socket) => _computer.Socket = socket;
|
||||||
|
|
||||||
private void RemoveComputerSocket(IWebSocketConnection socket) {
|
private void RemoveComputerSocket(IWebSocketConnection socket) {
|
||||||
if (Computer is { ConnectionInfo.Id: Guid id } && id == socket.ConnectionInfo.Id) Computer = null;
|
if (_computer.Socket is { ConnectionInfo.Id: Guid id } && id == socket.ConnectionInfo.Id) _computer.Socket = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SocketClosed(IWebSocketConnection socket) {
|
private async Task SocketClosed(IWebSocketConnection socket) {
|
||||||
@ -232,7 +226,7 @@ public class Program : IDisposable, ICommandHandler<ResponseType>, IUserRoleMana
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ResponseType> HandleCommand(SocketUserMessage message, string[] parameters, CancellationToken ct) {
|
public async Task<ResponseType> HandleCommand(SocketUserMessage message, string[] parameters, CancellationToken ct) {
|
||||||
if (Computer is ICommandHandler<ResponseType> handler)
|
if (_computer is ICommandHandler<ResponseType> handler)
|
||||||
try {
|
try {
|
||||||
return await handler.HandleCommand(message, parameters, ct);
|
return await handler.HandleCommand(message, parameters, ct);
|
||||||
} catch (TaskCanceledException) {
|
} catch (TaskCanceledException) {
|
||||||
|
37
MinecraftDiscordBot/Services/PlayerDetectorService.cs
Normal file
37
MinecraftDiscordBot/Services/PlayerDetectorService.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using Discord.WebSocket;
|
||||||
|
using MinecraftDiscordBot.Commands;
|
||||||
|
using MinecraftDiscordBot.Models;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace MinecraftDiscordBot.Services;
|
||||||
|
|
||||||
|
internal class PlayerDetectorService : CommandRouter {
|
||||||
|
private readonly ITaskWaitSource _taskSource;
|
||||||
|
public PlayerDetectorService(ITaskWaitSource taskSource) => _taskSource = taskSource;
|
||||||
|
|
||||||
|
public override string HelpTextPrefix => "!pd ";
|
||||||
|
public override Task<ResponseType> FallbackHandler(SocketUserMessage message, string method, string[] parameters, CancellationToken ct)
|
||||||
|
=> throw new ReplyException($"The player detector cannot do '{method}'!");
|
||||||
|
|
||||||
|
private Task<T> Method<T>(string methodName, Func<string, T> parser, CancellationToken ct, Dictionary<string, object>? parameters = null)
|
||||||
|
=> RootCommandService.Method(_taskSource, methodName, parser, ct, parameters);
|
||||||
|
|
||||||
|
public Task<string[]> GetOnlinePlayers(CancellationToken ct) => Method("getonline", RootCommandService.Deserialize<string[]>(), ct);
|
||||||
|
public async Task<PlayerPosition> GetPlayerPosition(string username, CancellationToken ct)
|
||||||
|
=> (await FindPlayer(username, ct)) ?? throw new ReplyException($"User '{username}' is not online!");
|
||||||
|
private Task<PlayerPosition?> FindPlayer(string username, CancellationToken ct) => Method("whereis", i => JsonConvert.DeserializeObject<PlayerPosition?>(i), ct, new() {
|
||||||
|
["username"] = username
|
||||||
|
});
|
||||||
|
|
||||||
|
[CommandHandler("getonline", HelpText ="Get a list of online players.")]
|
||||||
|
public async Task<ResponseType> HandleOnlinePlayers(SocketUserMessage message, string[] parameters, CancellationToken ct)
|
||||||
|
=> ResponseType.AsString($"The following players are currently online:\n{string.Join("\n", await GetOnlinePlayers(ct))}");
|
||||||
|
[CommandHandler("whereis", HelpText = "Find a player in the world.")]
|
||||||
|
public async Task<ResponseType> HandleFindPlayers(SocketUserMessage message, string[] parameters, CancellationToken ct) {
|
||||||
|
if (parameters is not { Length: 1 }) throw new ReplyException($"Give me only one username!");
|
||||||
|
var username = parameters[0];
|
||||||
|
var player = await FindPlayer(username, ct);
|
||||||
|
if (player is null) throw new ReplyException($"{username} is currently offline!");
|
||||||
|
return ResponseType.AsString($"{username} is at coordinates {player.X} {player.Y} {player.Z} in dimension {player.Dimension}.");
|
||||||
|
}
|
||||||
|
}
|
@ -18,11 +18,8 @@ public class RefinedStorageService : CommandRouter {
|
|||||||
public override Task<ResponseType> FallbackHandler(SocketUserMessage message, string method, string[] parameters, CancellationToken ct)
|
public override Task<ResponseType> FallbackHandler(SocketUserMessage message, string method, string[] parameters, CancellationToken ct)
|
||||||
=> throw new ReplyException($"The RS system has no command '{method}'!");
|
=> throw new ReplyException($"The RS system has no command '{method}'!");
|
||||||
|
|
||||||
private async Task<T> Method<T>(string methodName, Func<string, T> parser, CancellationToken ct, Dictionary<string, object>? parameters = null) {
|
private Task<T> Method<T>(string methodName, Func<string, T> parser, CancellationToken ct, Dictionary<string, object>? parameters = null)
|
||||||
var waiter = _taskSource.GetWaiter(parser, ct);
|
=> RootCommandService.Method<T>(_taskSource, methodName, parser, ct, parameters);
|
||||||
await _taskSource.Send(new RequestMessage(waiter.ID, methodName, parameters));
|
|
||||||
return await waiter.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string CmdEnergyUsage = "energyusage";
|
private const string CmdEnergyUsage = "energyusage";
|
||||||
private const string CmdEnergyStorage = "energystorage";
|
private const string CmdEnergyStorage = "energystorage";
|
||||||
|
@ -10,16 +10,45 @@ public delegate Task<TResponse> HandleCommandDelegate<TResponse>(SocketUserMessa
|
|||||||
public delegate Task HandleCommandDelegate(SocketUserMessage message, string[] parameters, CancellationToken ct);
|
public delegate Task HandleCommandDelegate(SocketUserMessage message, string[] parameters, CancellationToken ct);
|
||||||
|
|
||||||
public class RootCommandService : CommandRouter, ITaskWaitSource {
|
public class RootCommandService : CommandRouter, ITaskWaitSource {
|
||||||
protected readonly IWebSocketConnection _socket;
|
protected IWebSocketConnection? _socketField;
|
||||||
public override string HelpTextPrefix => "!";
|
public override string HelpTextPrefix => "!";
|
||||||
public RootCommandService(IWebSocketConnection socket, IUserRoleManager roleManager) : base() {
|
public RootCommandService(IUserRoleManager roleManager) : base() {
|
||||||
socket.OnMessage = OnMessage;
|
|
||||||
_socket = socket;
|
|
||||||
_rs = new RefinedStorageService(this, roleManager);
|
_rs = new RefinedStorageService(this, roleManager);
|
||||||
|
_pd = new PlayerDetectorService(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<T> Method<T>(ITaskWaitSource taskSource, string methodName, Func<string, T> parser, CancellationToken ct, Dictionary<string, object>? parameters) {
|
||||||
|
var waiter = taskSource.GetWaiter(parser, ct);
|
||||||
|
await taskSource.Send(new RequestMessage(waiter.ID, methodName, parameters));
|
||||||
|
return await waiter.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<ChatEvent>? ChatMessageReceived;
|
||||||
|
public event EventHandler<PeripheralAttachEvent>? PeripheralAttached;
|
||||||
|
public event EventHandler<PeripheralDetachEvent>? PeripheralDetached;
|
||||||
|
public event EventHandler<IWebSocketConnection?>? SocketChanged;
|
||||||
|
|
||||||
|
public IWebSocketConnection? Socket {
|
||||||
|
get => _socketField; set {
|
||||||
|
if (_socketField != value) {
|
||||||
|
_socketField = value;
|
||||||
|
if (value is not null) value.OnMessage = OnMessage;
|
||||||
|
SocketChanged?.Invoke(this, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMessage(string message) {
|
private void OnMessage(string message) {
|
||||||
switch (Message.Deserialize(message)) {
|
switch (Message.Deserialize(message)) {
|
||||||
|
case ChatEvent msg:
|
||||||
|
ChatMessageReceived?.Invoke(this, msg);
|
||||||
|
break;
|
||||||
|
case PeripheralAttachEvent msg:
|
||||||
|
PeripheralAttached?.Invoke(this, msg);
|
||||||
|
break;
|
||||||
|
case PeripheralDetachEvent msg:
|
||||||
|
PeripheralDetached?.Invoke(this, msg);
|
||||||
|
break;
|
||||||
case ReplyMessage msg:
|
case ReplyMessage msg:
|
||||||
IChunkWaiter? waiter;
|
IChunkWaiter? waiter;
|
||||||
lock (_syncRoot) if (!_waits.TryGetValue(msg.AnswerId, out waiter)) {
|
lock (_syncRoot) if (!_waits.TryGetValue(msg.AnswerId, out waiter)) {
|
||||||
@ -38,12 +67,11 @@ public class RootCommandService : CommandRouter, ITaskWaitSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Send(string message) => _socket.Send(message);
|
public Task Send(string message) => (Socket ?? throw new ReplyException("Minecraft server is not available!")).Send(message);
|
||||||
public Task Send(Message message) => Send(JsonConvert.SerializeObject(message));
|
public Task Send(Message message) => Send(JsonConvert.SerializeObject(message));
|
||||||
private readonly object _syncRoot = new();
|
private readonly object _syncRoot = new();
|
||||||
private readonly Dictionary<int, IChunkWaiter> _waits = new();
|
private readonly Dictionary<int, IChunkWaiter> _waits = new();
|
||||||
private readonly Random _rnd = new();
|
private readonly Random _rnd = new();
|
||||||
public IWebSocketConnectionInfo ConnectionInfo => _socket.ConnectionInfo;
|
|
||||||
|
|
||||||
private int GetFreeId() {
|
private int GetFreeId() {
|
||||||
var attempts = 0;
|
var attempts = 0;
|
||||||
@ -65,9 +93,14 @@ public class RootCommandService : CommandRouter, ITaskWaitSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly ICommandHandler<ResponseType> _rs;
|
private readonly ICommandHandler<ResponseType> _rs;
|
||||||
|
private readonly ICommandHandler<ResponseType> _pd;
|
||||||
|
|
||||||
[CommandHandler("rs", HelpText = "Provides some commands for interacting with the Refined Storage system.")]
|
[CommandHandler("rs", HelpText = "Provides some commands for interacting with the Refined Storage system.")]
|
||||||
public Task<ResponseType> RefinedStorageHandler(SocketUserMessage message, string[] parameters, CancellationToken ct)
|
public Task<ResponseType> RefinedStorageHandler(SocketUserMessage message, string[] parameters, CancellationToken ct)
|
||||||
=> _rs.HandleCommand(message, parameters, ct);
|
=> _rs.HandleCommand(message, parameters, ct);
|
||||||
|
[CommandHandler("pd", HelpText = "Provides some commands for interacting with the Player Detector.")]
|
||||||
|
public Task<ResponseType> PlayerDetectorHandler(SocketUserMessage message, string[] parameters, CancellationToken ct)
|
||||||
|
=> _pd.HandleCommand(message, parameters, ct);
|
||||||
|
|
||||||
public static Func<string, T> Deserialize<T>() => msg
|
public static Func<string, T> Deserialize<T>() => msg
|
||||||
=> JsonConvert.DeserializeObject<T>(msg) ?? throw new InvalidProgramException("Empty response!");
|
=> JsonConvert.DeserializeObject<T>(msg) ?? throw new InvalidProgramException("Empty response!");
|
||||||
|
Loading…
Reference in New Issue
Block a user