diff --git a/MinecraftDiscordBot/ClientScript.lua b/MinecraftDiscordBot/ClientScript.lua index fa04108..fe399d5 100644 --- a/MinecraftDiscordBot/ClientScript.lua +++ b/MinecraftDiscordBot/ClientScript.lua @@ -188,10 +188,51 @@ local function peripheralAttachEventListener(socket) end end +local function listAsSet(list) + local asSet = {} + for i,elem in pairs(list) do asSet[elem] = true end + return asSet +end + +local function joinSets(a, b) + local joined = {} + for elem,exists in pairs(a) do if exists then joined[elem] = true end end + for elem,exists in pairs(b) do if exists then joined[elem] = true end end + return joined +end + +local function playerStatusEventListener(socket) + local players = {} + while true do + local pd = peripheral.find("playerDetector") + if not not pd then + players = listAsSet(pd.getOnlinePlayers()) + break + end + printError("playerDetector not connected!") + end + while true do + local pd = peripheral.find("playerDetector") + if not not pd then + local newPlayers = listAsSet(pd.getOnlinePlayers()) + for player,_ in pairs(joinSets(players, newPlayers)) do + if players[player] and (not newPlayers[player]) then + sendJson(socket, {type = "playerstatus", player = player, status = false}) + elseif (not players[player]) and newPlayers[player] then + sendJson(socket, {type = "playerstatus", player = player, status = true}) + end + end + players = newPlayers + end + sleep(1) + end +end + local function eventListeners(socket) parallel.waitForAny( termWaiter, function() chatEventListener(socket) end, + function() playerStatusEventListener(socket) end, function() peripheralDetachEventListener(socket) end, function() peripheralAttachEventListener(socket) end ) diff --git a/MinecraftDiscordBot/MinecraftDiscordBot.csproj b/MinecraftDiscordBot/MinecraftDiscordBot.csproj index 3cf40ea..c025260 100644 --- a/MinecraftDiscordBot/MinecraftDiscordBot.csproj +++ b/MinecraftDiscordBot/MinecraftDiscordBot.csproj @@ -6,7 +6,7 @@ enable enable Linux - 1.1.2 + 1.1.3 Michael Chen $(Authors) https://gitlab.com/chenmichael/mcdiscordbot diff --git a/MinecraftDiscordBot/Models/Message.cs b/MinecraftDiscordBot/Models/Message.cs index 40bf9d7..064ef2d 100644 --- a/MinecraftDiscordBot/Models/Message.cs +++ b/MinecraftDiscordBot/Models/Message.cs @@ -1,6 +1,7 @@ using Discord.WebSocket; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using System.Diagnostics; namespace MinecraftDiscordBot.Models; @@ -39,14 +40,17 @@ public abstract class Message { } [MessageType(TYPE)] +[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")] public class CapabilityMessage : Message { private const string TYPE = "roles"; public override string Type => TYPE; [JsonProperty("role", Required = Required.Always)] public string[] Role { get; set; } = default!; + public override string ToString() => $"Capabilities: {string.Join(", ", Role)}"; } [MessageType(TYPE)] +[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")] public class ReplyMessage : Message { private const string TYPE = "reply"; public override string Type => TYPE; @@ -60,19 +64,35 @@ public class ReplyMessage : Message { public int Total { get; set; } = 1; [JsonProperty("success", Required = Required.DisallowNull)] public ResultState State { get; set; } = ResultState.Successful; + public override string ToString() => $"Reply [{AnswerId}] {State} ({Chunk}/{Total}) Length {Result.Length}"; } public abstract class EventMessage : Message { } [MessageType(TYPE)] +[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")] 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!; + public override string ToString() => $"Detached '{Side}'!"; } [MessageType(TYPE)] +[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")] +public class PlayerStatusEvent : EventMessage { + private const string TYPE = "playerstatus"; + public override string Type => TYPE; + [JsonProperty("player", Required = Required.Always)] + public string Player { get; set; } = default!; + [JsonProperty("status", Required = Required.Always)] + public bool Online { get; set; } + public override string ToString() => $"{Player} is now {(Online ? "on" : "off")}line!"; +} + +[MessageType(TYPE)] +[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")] public class PeripheralAttachEvent : EventMessage { private const string TYPE = "peripheral"; public override string Type => TYPE; @@ -80,6 +100,7 @@ public class PeripheralAttachEvent : EventMessage { public string Side => Peripheral.Side; [JsonProperty("peripheral", Required = Required.Always)] public Peripheral Peripheral { get; set; } = default!; + public override string ToString() => $"Attached {Peripheral}!"; } public class Peripheral { @@ -89,9 +110,11 @@ public class Peripheral { public string Type { get; set; } = default!; [JsonProperty("methods", Required = Required.Always)] public string[] Methods { get; set; } = default!; + public override string ToString() => $"{Type} at '{Side}'"; } [MessageType(TYPE)] +[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")] public class ChatEvent : EventMessage { private const string TYPE = "chat"; public override string Type => TYPE; @@ -103,9 +126,11 @@ public class ChatEvent : EventMessage { public string UUID { get; set; } = default!; [JsonProperty("hidden", Required = Required.Always)] public bool IsHidden { get; set; } + public override string ToString() => $"{(IsHidden ? "HIDDEN: " : string.Empty)}[{Username}] {Message} ({UUID})"; } [MessageType(TYPE)] +[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")] public class RequestMessage : Message { private const string TYPE = "request"; public override string Type => TYPE; @@ -121,4 +146,5 @@ public class RequestMessage : Message { public string Method { get; set; } [JsonProperty("params")] public Dictionary Parameters { get; } + public override string ToString() => $"Request [{AnswerId}] {Method}({JsonConvert.SerializeObject(Parameters)})"; } diff --git a/MinecraftDiscordBot/Program.cs b/MinecraftDiscordBot/Program.cs index 11930a7..77db653 100644 --- a/MinecraftDiscordBot/Program.cs +++ b/MinecraftDiscordBot/Program.cs @@ -60,6 +60,7 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana _computer = new(this); _computer.ChatMessageReceived += MinecraftMessageReceived; _computer.SocketChanged += ComputerConnectedChanged; + _computer.PlayerStatusChanged += PlayerStatusChanged; _computer.PeripheralAttached += PeripheralAttached; _computer.PeripheralDetached += PeripheralDetached; _administrators = config.Administrators.ToHashSet(); @@ -74,6 +75,8 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana _whitelistedChannels = config.Channels.ToHashSet(); } + private void PlayerStatusChanged(object? sender, PlayerStatusEvent e) + => _ = Task.Run(() => Broadcast(i => i.SendMessageAsync($"{e.Player} just {(e.Online ? "joined" : "left")} the server!"))); private void PeripheralAttached(object? sender, PeripheralAttachEvent e) => LogInfo("Computer", $"Peripheral {e.Peripheral.Type} was attached on side {e.Side}."); private void PeripheralDetached(object? sender, PeripheralDetachEvent e) => LogInfo("Computer", $"Peripheral on side {e.Side} was detached."); private void ComputerConnectedChanged(object? sender, IWebSocketConnection? e) diff --git a/MinecraftDiscordBot/Services/RootCommandService.cs b/MinecraftDiscordBot/Services/RootCommandService.cs index 80e055d..17a52f3 100644 --- a/MinecraftDiscordBot/Services/RootCommandService.cs +++ b/MinecraftDiscordBot/Services/RootCommandService.cs @@ -26,6 +26,7 @@ public class RootCommandService : CommandRouter, ITaskWaitSource { } public event EventHandler? ChatMessageReceived; + public event EventHandler? PlayerStatusChanged; public event EventHandler? PeripheralAttached; public event EventHandler? PeripheralDetached; public event EventHandler? SocketChanged; @@ -49,6 +50,9 @@ public class RootCommandService : CommandRouter, ITaskWaitSource { case ChatEvent msg: ChatMessageReceived?.Invoke(this, msg); break; + case PlayerStatusEvent msg: + PlayerStatusChanged?.Invoke(this, msg); + break; case PeripheralAttachEvent msg: PeripheralAttached?.Invoke(this, msg); break;