From 1de3ff0a4d634b5ece5d6cf166c1ac3865ebbba3 Mon Sep 17 00:00:00 2001 From: MMjr <2429824+mmjr-x@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:54:52 +0100 Subject: [PATCH 1/7] Added initial version of the gift command (only works with soil atm), made chat command argument respect double quoted strings (and first lvl double quote escaping) and added SendPacketToClient that allows clients to send packets to other clients (relayed via the host if necessary) --- NebulaAPI/GameState/INetworkProvider.cs | 5 + .../Chat/ChatCommandGiftType.cs | 8 ++ NebulaModel/NetworkProvider.cs | 3 + .../Packets/Chat/ChatCommandGiftPacket.cs | 21 ++++ .../Packets/Routers/ClientRelayPacket.cs | 15 +++ NebulaNetwork/Client.cs | 5 + .../Chat/ChatCommandGiftProcessor.cs | 72 +++++++++++++ .../Routers/ClientRelayProcessor.cs | 67 ++++++++++++ NebulaNetwork/Server.cs | 12 +++ NebulaWorld/Chat/ChatCommandRegistry.cs | 1 + .../Chat/Commands/GiftCommandHandler.cs | 100 ++++++++++++++++++ .../MonoBehaviours/Local/Chat/ChatWindow.cs | 9 +- 12 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 NebulaModel/DataStructures/Chat/ChatCommandGiftType.cs create mode 100644 NebulaModel/Packets/Chat/ChatCommandGiftPacket.cs create mode 100644 NebulaModel/Packets/Routers/ClientRelayPacket.cs create mode 100644 NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs create mode 100644 NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs create mode 100644 NebulaWorld/Chat/Commands/GiftCommandHandler.cs diff --git a/NebulaAPI/GameState/INetworkProvider.cs b/NebulaAPI/GameState/INetworkProvider.cs index d1786cbdc..3a2d2758d 100644 --- a/NebulaAPI/GameState/INetworkProvider.cs +++ b/NebulaAPI/GameState/INetworkProvider.cs @@ -46,5 +46,10 @@ public interface INetworkProvider : IDisposable /// void SendPacketToStarExclude(T packet, int starId, INebulaConnection exclude) where T : class, new(); + /// + /// Send packet to Client directly (If possible) or indirectly by relaying via host (If Client is not directly reachable) + /// + void SendPacketToClient(T packet, string clientUsername) where T : class, new (); + void Update(); } diff --git a/NebulaModel/DataStructures/Chat/ChatCommandGiftType.cs b/NebulaModel/DataStructures/Chat/ChatCommandGiftType.cs new file mode 100644 index 000000000..e31724eb9 --- /dev/null +++ b/NebulaModel/DataStructures/Chat/ChatCommandGiftType.cs @@ -0,0 +1,8 @@ +namespace NebulaModel.DataStructures.Chat; + +public enum ChatCommandGiftType +{ + Soil = 0, // TODO: Since Soil Pile (item id 1099 ) is also an item, we can probably remove this all together and just use Item + Item = 1, + Energy = 2 +} diff --git a/NebulaModel/NetworkProvider.cs b/NebulaModel/NetworkProvider.cs index bf7f68ebd..9f3510585 100644 --- a/NebulaModel/NetworkProvider.cs +++ b/NebulaModel/NetworkProvider.cs @@ -38,6 +38,9 @@ public abstract void SendPacketExclude(T packet, INebulaConnection exclude) public abstract void SendPacketToStarExclude(T packet, int starId, INebulaConnection exclude) where T : class, new(); + public abstract void SendPacketToClient(T packet, string clientUsername) + where T : class, new(); + public abstract void Update(); public abstract void Start(); diff --git a/NebulaModel/Packets/Chat/ChatCommandGiftPacket.cs b/NebulaModel/Packets/Chat/ChatCommandGiftPacket.cs new file mode 100644 index 000000000..0bd68c5b9 --- /dev/null +++ b/NebulaModel/Packets/Chat/ChatCommandGiftPacket.cs @@ -0,0 +1,21 @@ +using NebulaModel.DataStructures.Chat; + +namespace NebulaModel.Packets.Chat; + +public class ChatCommandGiftPacket +{ + public ChatCommandGiftPacket() { } + + public ChatCommandGiftPacket(string sender, string recipient, ChatCommandGiftType type, long quantity) + { + SenderUsername = sender; + RecipientUsername = recipient; + Type = type; + Quantity = quantity; + } + + public string SenderUsername { get; set; } + public string RecipientUsername { get; set; } + public ChatCommandGiftType Type { get; set; } + public long Quantity { get; set; } +} diff --git a/NebulaModel/Packets/Routers/ClientRelayPacket.cs b/NebulaModel/Packets/Routers/ClientRelayPacket.cs new file mode 100644 index 000000000..2c54e9c58 --- /dev/null +++ b/NebulaModel/Packets/Routers/ClientRelayPacket.cs @@ -0,0 +1,15 @@ +namespace NebulaModel.Packets.Routers; + +public class ClientRelayPacket +{ + public ClientRelayPacket() { } + + public ClientRelayPacket(byte[] packetObject, string clientUsername) + { + PacketObject = packetObject; + ClientUsername = clientUsername; + } + + public byte[] PacketObject { get; set; } + public string ClientUsername { get; set; } +} diff --git a/NebulaNetwork/Client.cs b/NebulaNetwork/Client.cs index 6d6960ff3..ea089bb05 100644 --- a/NebulaNetwork/Client.cs +++ b/NebulaNetwork/Client.cs @@ -188,6 +188,11 @@ public override void SendPacketToStarExclude(T packet, int starId, INebulaCon throw new NotImplementedException(); } + public override void SendPacketToClient(T packet, string clientUsername) + { + serverConnection?.SendPacket(new ClientRelayPacket(PacketProcessor.Write(packet), clientUsername)); + } + public override void Update() { PacketProcessor.ProcessPacketQueue(); diff --git a/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs b/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs new file mode 100644 index 000000000..a026799be --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs @@ -0,0 +1,72 @@ +#region + +using System; +using NebulaAPI.Packets; +using NebulaModel.DataStructures.Chat; +using NebulaModel.Logger; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Chat; +using NebulaWorld; +using NebulaWorld.Chat.Commands; +using NebulaWorld.MonoBehaviours.Local.Chat; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Chat; + +[RegisterPacketProcessor] +internal class ChatCommandGiftProcessor : PacketProcessor +{ + protected override void ProcessPacket(ChatCommandGiftPacket packet, NebulaConnection conn) + { + + //window.SendLocalChatMessage("Invalid gift type".Translate(), ChatMessageType.CommandErrorMessage); + + switch (packet.Type) + { + case ChatCommandGiftType.Soil: + var mainPlayer = GameMain.data.mainPlayer; + lock (mainPlayer) + { + mainPlayer.SetSandCount(mainPlayer.sandCount + packet.Quantity); + // TODO: Do we need to do something with soil sync? + } + ChatManager.Instance.SendChatMessage($"[{DateTime.Now:HH:mm}] {packet.SenderUsername} gifted you {packet.Quantity} soil", ChatMessageType.SystemInfoMessage); + break; + // TODO: Implement Item and Energy variants. + default: + return; + } + + // TODO: Logic for adding the soil, items, energy etc (restransmission logic no longer needed with the ClientRelayPacket + + //if (IsClient) + //{ + // WhisperCommandHandler.SendWhisperToLocalPlayer(packet.SenderUsername, packet.Message); + //} + //else + //{ + // // two cases, simplest is that whisper is meant for host + // if (Multiplayer.Session.LocalPlayer.Data.Username == packet.RecipientUsername) + // { + // WhisperCommandHandler.SendWhisperToLocalPlayer(packet.SenderUsername, packet.Message); + // return; + // } + + // // second case, relay message to recipient + // var recipient = Multiplayer.Session.Network + // .PlayerManager.GetConnectedPlayerByUsername(packet.RecipientUsername); + // if (recipient == null) + // { + // Log.Warn($"Recipient not found {packet.RecipientUsername}"); + // var sender = Multiplayer.Session.Network.PlayerManager.GetPlayer(conn); + // sender.SendPacket(new ChatCommandWhisperPacket("SYSTEM".Translate(), packet.SenderUsername, + // string.Format("User not found {0}".Translate(), packet.RecipientUsername))); + // return; + // } + + // recipient.SendPacket(packet); + //} + } +} diff --git a/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs b/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs new file mode 100644 index 000000000..cee38c246 --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs @@ -0,0 +1,67 @@ +#region + +using NebulaAPI.GameState; +using NebulaAPI.Packets; +using NebulaModel; +using NebulaModel.Logger; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Routers; +using NebulaWorld; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Routers; + +[RegisterPacketProcessor] +internal class ClientRelayProcessor : PacketProcessor +{ + private readonly IPlayerManager playerManager; + + public ClientRelayProcessor() + { + playerManager = Multiplayer.Session.Network.PlayerManager; + } + + protected override void ProcessPacket(ClientRelayPacket packet, NebulaConnection conn) + { + // TODO: check if this player null check is required + var player = playerManager.GetPlayer(conn); + if (player == null || packet.PacketObject == null) + { + return; + } + + // If the processor is either host or client and the recipient is the processor unwrap and process the packet + // (This happens in the case that either client -> host or host -> client sends this packet directly, + // as you do not want manually account for the sender recipient relation when using this packet we have to handle this case as wel) + if (Multiplayer.Session.LocalPlayer.Data.Username == packet.ClientUsername) + { + //Process the packet on the host + // NOTE: Since PacketProcessor is not part of INetworkProvider this will not work if we ever swap it out with another network provider that does not expose this + // However StarBroadcastProcessor and PlanetBroadcastProcessor also do this in a similar manner so I think it is fine for now + ((NetworkProvider)Multiplayer.Session.Network).PacketProcessor + .EnqueuePacketForProcessing(packet.PacketObject, conn); + return; + } + + // If the processor is client and not the recipient, do nothing + if (IsClient) + { + return; + } + + // If the processor is the host and not the recipient, unwrap and relay packet to recipient client + var recipient = Multiplayer.Session.Network + .PlayerManager.GetConnectedPlayerByUsername(packet.ClientUsername); + if (recipient == null) + { + Log.Warn($"Could not relay packet because client was not found {packet.ClientUsername}"); + + // TODO: We might want to communicate back to the sender that the relaying failed + return; + } + + recipient.Connection.SendRawPacket(packet.PacketObject); + } +} diff --git a/NebulaNetwork/Server.cs b/NebulaNetwork/Server.cs index 9bd113d70..a5addff1a 100644 --- a/NebulaNetwork/Server.cs +++ b/NebulaNetwork/Server.cs @@ -244,6 +244,18 @@ public override void SendPacketToStarExclude(T packet, int starId, INebulaCon PlayerManager.SendPacketToStarExcept(packet, starId, exclude); } + public override void SendPacketToClient(T packet, string clientUsername) + { + var recipient = PlayerManager.GetConnectedPlayerByUsername(clientUsername); + if (recipient == null) + { + Log.Warn($"Could not send packet to client, Recipient not found {clientUsername}"); + return; + } + + recipient.SendPacket(packet); + } + public override void Update() { PacketProcessor.ProcessPacketQueue(); diff --git a/NebulaWorld/Chat/ChatCommandRegistry.cs b/NebulaWorld/Chat/ChatCommandRegistry.cs index d271d91d4..ca3addca1 100644 --- a/NebulaWorld/Chat/ChatCommandRegistry.cs +++ b/NebulaWorld/Chat/ChatCommandRegistry.cs @@ -27,6 +27,7 @@ static ChatCommandRegistry() RegisterCommand("system", new SystemCommandHandler(), "s"); RegisterCommand("reconnect", new ReconnectCommandHandler(), "r"); RegisterCommand("server", new ServerCommandHandler()); + RegisterCommand("gift", new GiftCommandHandler(), "g"); } private static void RegisterCommand(string commandName, IChatCommandHandler commandHandlerHandler, params string[] aliases) diff --git a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs new file mode 100644 index 000000000..3529f3168 --- /dev/null +++ b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs @@ -0,0 +1,100 @@ +#region + +using NebulaModel.DataStructures.Chat; +using NebulaModel.Packets.Chat; +using NebulaWorld.MonoBehaviours.Local.Chat; + +#endregion + +namespace NebulaWorld.Chat.Commands; + +public class GiftCommandHandler : IChatCommandHandler +{ + public void Execute(ChatWindow window, string[] parameters) + { + if (parameters.Length < 3) + { + throw new ChatCommandUsageException("Not enough arguments!".Translate()); + } + + var senderUsername = Multiplayer.Session?.LocalPlayer?.Data?.Username ?? "UNKNOWN"; + if (senderUsername == "UNKNOWN" || Multiplayer.Session == null || Multiplayer.Session.LocalPlayer == null) + { + window.SendLocalChatMessage("Invalid sender (not connected), can't send gift".Translate(), ChatMessageType.CommandErrorMessage); + return; + } + + // TODO: Add support for using id instead of username + var recipientUsername = parameters[0]; + if (senderUsername == recipientUsername) + { + window.SendLocalChatMessage("Invalid recipient (self), can't send gift".Translate(), ChatMessageType.CommandErrorMessage); + return; + } + //var fullMessageBody = string.Join(" ", parameters.Skip(1)); + // first echo what the player typed so they know something actually happened + //ChatManager.Instance.SendChatMessage($"[{DateTime.Now:HH:mm}] [To: {recipientUserName}] : {fullMessageBody}", + // ChatMessageType.PlayerMessage); + + ChatCommandGiftType type; + switch (parameters[1]) + { + case "soil": + case "sand": + case "s": + type = ChatCommandGiftType.Soil; + break; + // TODO: Implement Item and Energy variants. + default: + window.SendLocalChatMessage("Invalid gift type, can't send gift".Translate(), ChatMessageType.CommandErrorMessage); + return; + } + + long quantity; + if (!long.TryParse(parameters[2], out quantity) || quantity == 0) + { + window.SendLocalChatMessage("Invalid gift quantity, can't send gift".Translate(), ChatMessageType.CommandErrorMessage); + return; + } + + // Validate that you acctually have the required soil/items/energy to gift + switch (type) + { + case ChatCommandGiftType.Soil: + var mainPlayer = GameMain.data.mainPlayer; + lock (mainPlayer) + { + if (mainPlayer.sandCount < quantity) + { + window.SendLocalChatMessage("You dont have enough soil to send, can't send gift".Translate(), ChatMessageType.CommandErrorMessage); + return; + } + + mainPlayer.SetSandCount(mainPlayer.sandCount - quantity); + // TODO: Do we need to do something with soil sync? + } + break; + // TODO: Implement Item and Energy variants. + } + + var packet = new ChatCommandGiftPacket(senderUsername, recipientUsername, type, quantity); + Multiplayer.Session.Network.SendPacketToClient(packet, recipientUsername); + } + + public string GetDescription() + { + return string.Format("Send gift to player. Use /who for valid user names. Valid types are soil (s), item (i), energy (e)".Translate()); + } + + public string[] GetUsage() + { + return [" "]; + } + + // TODO: We should add logic here that acctually adds the gifted materials (and devise something to substract the materials) + //public static void SendWhisperToLocalPlayer(string sender, string mesageBody) + //{ + // ChatManager.Instance.SendChatMessage($"[{DateTime.Now:HH:mm}] [{sender} whispered] : {mesageBody}", + // ChatMessageType.PlayerMessagePrivate); + //} +} diff --git a/NebulaWorld/MonoBehaviours/Local/Chat/ChatWindow.cs b/NebulaWorld/MonoBehaviours/Local/Chat/ChatWindow.cs index dfddf42f8..aed710a03 100644 --- a/NebulaWorld/MonoBehaviours/Local/Chat/ChatWindow.cs +++ b/NebulaWorld/MonoBehaviours/Local/Chat/ChatWindow.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using NebulaModel; using NebulaModel.DataStructures.Chat; using NebulaModel.Utils; @@ -144,7 +145,13 @@ private void TrySendMessage() if (chatBox.text.StartsWith(ChatCommandRegistry.CommandPrefix)) { - var arguments = chatBox.text.Substring(1).Split(' '); + //var arguments = chatBox.text.Substring(1).Split(' '); + // this handles quoted arguments and supports first lvl escaping using \" + var arguments = Regex.Matches(chatBox.text.Substring(1), @"(?() + .Select(m => Regex.Replace(m.Value, @"^(? 0) { var commandName = arguments[0]; From 451d8b8887a0213ed49f29228a61c7edf9601c6e Mon Sep 17 00:00:00 2001 From: MMjr <2429824+mmjr-x@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:20:40 +0100 Subject: [PATCH 2/7] Changed the sendPacketToClient to be userId based as username is not guaranteed to be unique, gifts can now also be send by specifying the id instead of username and did some refactoring --- NebulaAPI/GameState/INetworkProvider.cs | 2 +- NebulaModel/NetworkProvider.cs | 2 +- .../Packets/Chat/ChatCommandGiftPacket.cs | 10 +-- .../Packets/Routers/ClientRelayPacket.cs | 6 +- NebulaNetwork/Client.cs | 4 +- .../Chat/ChatCommandGiftProcessor.cs | 17 +++- .../Routers/ClientRelayProcessor.cs | 6 +- NebulaNetwork/Server.cs | 6 +- .../Chat/Commands/GiftCommandHandler.cs | 77 ++++++++++++++++--- 9 files changed, 99 insertions(+), 31 deletions(-) diff --git a/NebulaAPI/GameState/INetworkProvider.cs b/NebulaAPI/GameState/INetworkProvider.cs index 3a2d2758d..d0a00659f 100644 --- a/NebulaAPI/GameState/INetworkProvider.cs +++ b/NebulaAPI/GameState/INetworkProvider.cs @@ -49,7 +49,7 @@ public interface INetworkProvider : IDisposable /// /// Send packet to Client directly (If possible) or indirectly by relaying via host (If Client is not directly reachable) /// - void SendPacketToClient(T packet, string clientUsername) where T : class, new (); + void SendPacketToClient(T packet, ushort clientUserId) where T : class, new (); void Update(); } diff --git a/NebulaModel/NetworkProvider.cs b/NebulaModel/NetworkProvider.cs index 9f3510585..d87c6afcf 100644 --- a/NebulaModel/NetworkProvider.cs +++ b/NebulaModel/NetworkProvider.cs @@ -38,7 +38,7 @@ public abstract void SendPacketExclude(T packet, INebulaConnection exclude) public abstract void SendPacketToStarExclude(T packet, int starId, INebulaConnection exclude) where T : class, new(); - public abstract void SendPacketToClient(T packet, string clientUsername) + public abstract void SendPacketToClient(T packet, ushort clientUserId) where T : class, new(); public abstract void Update(); diff --git a/NebulaModel/Packets/Chat/ChatCommandGiftPacket.cs b/NebulaModel/Packets/Chat/ChatCommandGiftPacket.cs index 0bd68c5b9..1f7625096 100644 --- a/NebulaModel/Packets/Chat/ChatCommandGiftPacket.cs +++ b/NebulaModel/Packets/Chat/ChatCommandGiftPacket.cs @@ -6,16 +6,16 @@ public class ChatCommandGiftPacket { public ChatCommandGiftPacket() { } - public ChatCommandGiftPacket(string sender, string recipient, ChatCommandGiftType type, long quantity) + public ChatCommandGiftPacket(ushort senderUserId, ushort recipientUserId, ChatCommandGiftType type, long quantity) { - SenderUsername = sender; - RecipientUsername = recipient; + SenderUserId = senderUserId; + RecipientUserId = recipientUserId; Type = type; Quantity = quantity; } - public string SenderUsername { get; set; } - public string RecipientUsername { get; set; } + public ushort SenderUserId { get; set; } + public ushort RecipientUserId { get; set; } public ChatCommandGiftType Type { get; set; } public long Quantity { get; set; } } diff --git a/NebulaModel/Packets/Routers/ClientRelayPacket.cs b/NebulaModel/Packets/Routers/ClientRelayPacket.cs index 2c54e9c58..a680c52e9 100644 --- a/NebulaModel/Packets/Routers/ClientRelayPacket.cs +++ b/NebulaModel/Packets/Routers/ClientRelayPacket.cs @@ -4,12 +4,12 @@ public class ClientRelayPacket { public ClientRelayPacket() { } - public ClientRelayPacket(byte[] packetObject, string clientUsername) + public ClientRelayPacket(byte[] packetObject, ushort clientUserId) { PacketObject = packetObject; - ClientUsername = clientUsername; + ClientUserId = clientUserId; } public byte[] PacketObject { get; set; } - public string ClientUsername { get; set; } + public ushort ClientUserId { get; set; } } diff --git a/NebulaNetwork/Client.cs b/NebulaNetwork/Client.cs index ea089bb05..5e4689eef 100644 --- a/NebulaNetwork/Client.cs +++ b/NebulaNetwork/Client.cs @@ -188,9 +188,9 @@ public override void SendPacketToStarExclude(T packet, int starId, INebulaCon throw new NotImplementedException(); } - public override void SendPacketToClient(T packet, string clientUsername) + public override void SendPacketToClient(T packet, ushort clientUserId) { - serverConnection?.SendPacket(new ClientRelayPacket(PacketProcessor.Write(packet), clientUsername)); + serverConnection?.SendPacket(new ClientRelayPacket(PacketProcessor.Write(packet), clientUserId)); } public override void Update() diff --git a/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs b/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs index a026799be..f1367ad43 100644 --- a/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs +++ b/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs @@ -10,6 +10,7 @@ using NebulaWorld; using NebulaWorld.Chat.Commands; using NebulaWorld.MonoBehaviours.Local.Chat; +using static UnityEngine.Analytics.Analytics; #endregion @@ -22,6 +23,20 @@ protected override void ProcessPacket(ChatCommandGiftPacket packet, NebulaConnec { //window.SendLocalChatMessage("Invalid gift type".Translate(), ChatMessageType.CommandErrorMessage); + string senderUserName = null; + // TODO: Unify this into something + using (Multiplayer.Session.World.GetRemotePlayersModels(out var remotePlayersModels)) + { + foreach (var remotePlayerModel in remotePlayersModels) + { + var movement = remotePlayerModel.Value.Movement; + if (movement.PlayerID == packet.SenderUserId) + { + senderUserName = movement.Username; + break; + } + } + } switch (packet.Type) { @@ -32,7 +47,7 @@ protected override void ProcessPacket(ChatCommandGiftPacket packet, NebulaConnec mainPlayer.SetSandCount(mainPlayer.sandCount + packet.Quantity); // TODO: Do we need to do something with soil sync? } - ChatManager.Instance.SendChatMessage($"[{DateTime.Now:HH:mm}] {packet.SenderUsername} gifted you {packet.Quantity} soil", ChatMessageType.SystemInfoMessage); + ChatManager.Instance.SendChatMessage($"[{DateTime.Now:HH:mm}] [{packet.SenderUserId}] {senderUserName} gifted you soil ({packet.Quantity})", ChatMessageType.SystemInfoMessage); break; // TODO: Implement Item and Energy variants. default: diff --git a/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs b/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs index cee38c246..ed66ce69c 100644 --- a/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs +++ b/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs @@ -35,7 +35,7 @@ protected override void ProcessPacket(ClientRelayPacket packet, NebulaConnection // If the processor is either host or client and the recipient is the processor unwrap and process the packet // (This happens in the case that either client -> host or host -> client sends this packet directly, // as you do not want manually account for the sender recipient relation when using this packet we have to handle this case as wel) - if (Multiplayer.Session.LocalPlayer.Data.Username == packet.ClientUsername) + if (Multiplayer.Session.LocalPlayer.Data.PlayerId == packet.ClientUserId) { //Process the packet on the host // NOTE: Since PacketProcessor is not part of INetworkProvider this will not work if we ever swap it out with another network provider that does not expose this @@ -53,10 +53,10 @@ protected override void ProcessPacket(ClientRelayPacket packet, NebulaConnection // If the processor is the host and not the recipient, unwrap and relay packet to recipient client var recipient = Multiplayer.Session.Network - .PlayerManager.GetConnectedPlayerByUsername(packet.ClientUsername); + .PlayerManager.GetPlayerById(packet.ClientUserId); if (recipient == null) { - Log.Warn($"Could not relay packet because client was not found {packet.ClientUsername}"); + Log.Warn($"Could not relay packet because client was not found with clientId: {packet.ClientUserId}"); // TODO: We might want to communicate back to the sender that the relaying failed return; diff --git a/NebulaNetwork/Server.cs b/NebulaNetwork/Server.cs index a5addff1a..9273a5eb4 100644 --- a/NebulaNetwork/Server.cs +++ b/NebulaNetwork/Server.cs @@ -244,12 +244,12 @@ public override void SendPacketToStarExclude(T packet, int starId, INebulaCon PlayerManager.SendPacketToStarExcept(packet, starId, exclude); } - public override void SendPacketToClient(T packet, string clientUsername) + public override void SendPacketToClient(T packet, ushort clientUserId) { - var recipient = PlayerManager.GetConnectedPlayerByUsername(clientUsername); + var recipient = PlayerManager.GetPlayerById(clientUserId); if (recipient == null) { - Log.Warn($"Could not send packet to client, Recipient not found {clientUsername}"); + Log.Warn($"Could not send packet to client, Recipient not found with clientId: {clientUserId}"); return; } diff --git a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs index 3529f3168..0cc53017a 100644 --- a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs +++ b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs @@ -1,5 +1,7 @@ #region +using System; +using System.Linq; using NebulaModel.DataStructures.Chat; using NebulaModel.Packets.Chat; using NebulaWorld.MonoBehaviours.Local.Chat; @@ -10,6 +12,12 @@ namespace NebulaWorld.Chat.Commands; public class GiftCommandHandler : IChatCommandHandler { + private struct UserInfo + { + public ushort id; + public string name; + } + public void Execute(ChatWindow window, string[] parameters) { if (parameters.Length < 3) @@ -17,24 +25,60 @@ public void Execute(ChatWindow window, string[] parameters) throw new ChatCommandUsageException("Not enough arguments!".Translate()); } - var senderUsername = Multiplayer.Session?.LocalPlayer?.Data?.Username ?? "UNKNOWN"; - if (senderUsername == "UNKNOWN" || Multiplayer.Session == null || Multiplayer.Session.LocalPlayer == null) + UserInfo sender; + { + if ( + Multiplayer.Session?.LocalPlayer?.Data?.PlayerId is ushort senderUserId + && Multiplayer.Session?.LocalPlayer?.Data?.Username is string senderUsername + ) + { + sender = new UserInfo + { + id = senderUserId, + name = senderUsername + }; + } + else + { + window.SendLocalChatMessage("Invalid sender (not connected), can't send gift".Translate(), ChatMessageType.CommandErrorMessage); + return; + }; + } + + var userIdOrNameParameter = parameters[0]; + + var foundRecipient = false; + var recipient = new UserInfo(); + // TODO: Abstract this into a utility function to get the user data by id (that also works on clients + using (Multiplayer.Session.World.GetRemotePlayersModels(out var remotePlayersModels)) + { + foreach (var remotePlayerModel in remotePlayersModels) + { + var movement = remotePlayerModel.Value.Movement; + if ((ushort.TryParse(userIdOrNameParameter, out var recipientUserId) && movement.PlayerID == recipientUserId) || movement.Username == userIdOrNameParameter) + { + foundRecipient = true; + recipient = new UserInfo + { + id = movement.PlayerID, + name = movement.Username + }; + break; + } + } + } + + if (!foundRecipient) { - window.SendLocalChatMessage("Invalid sender (not connected), can't send gift".Translate(), ChatMessageType.CommandErrorMessage); + window.SendLocalChatMessage("Invalid recipient (user id or username not found), can't send gift".Translate(), ChatMessageType.CommandErrorMessage); return; } - // TODO: Add support for using id instead of username - var recipientUsername = parameters[0]; - if (senderUsername == recipientUsername) + if (sender.id == recipient.id) { window.SendLocalChatMessage("Invalid recipient (self), can't send gift".Translate(), ChatMessageType.CommandErrorMessage); return; } - //var fullMessageBody = string.Join(" ", parameters.Skip(1)); - // first echo what the player typed so they know something actually happened - //ChatManager.Instance.SendChatMessage($"[{DateTime.Now:HH:mm}] [To: {recipientUserName}] : {fullMessageBody}", - // ChatMessageType.PlayerMessage); ChatCommandGiftType type; switch (parameters[1]) @@ -50,6 +94,7 @@ public void Execute(ChatWindow window, string[] parameters) return; } + // Add support for scientific notation and other notation types long quantity; if (!long.TryParse(parameters[2], out quantity) || quantity == 0) { @@ -77,8 +122,16 @@ public void Execute(ChatWindow window, string[] parameters) // TODO: Implement Item and Energy variants. } - var packet = new ChatCommandGiftPacket(senderUsername, recipientUsername, type, quantity); - Multiplayer.Session.Network.SendPacketToClient(packet, recipientUsername); + var packet = new ChatCommandGiftPacket(sender.id, recipient.id, type, quantity); + Multiplayer.Session.Network.SendPacketToClient(packet, recipient.id); + + switch (type) + { + case ChatCommandGiftType.Soil: + ChatManager.Instance.SendChatMessage($"[{DateTime.Now:HH:mm}] You gifted [{recipient.id}] {recipient.name} soil ({packet.Quantity})", ChatMessageType.SystemInfoMessage); + break; + // TODO: Implement Item and Energy variants. + } } public string GetDescription() From 3511bad8933ffb8a69ef0ed252fc117b4bfefa76 Mon Sep 17 00:00:00 2001 From: MMjr <2429824+mmjr-x@users.noreply.github.com> Date: Mon, 15 Jan 2024 00:13:32 +0100 Subject: [PATCH 3/7] Made the gift command code more readable and added some helper methods (will also now report about ambiguous usernames if multiple are matching) --- .../Chat/Commands/GiftCommandHandler.cs | 101 +++++++++++++----- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs index 0cc53017a..0e8025d17 100644 --- a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs +++ b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs @@ -1,8 +1,10 @@ #region using System; +using System.Collections.Generic; using System.Linq; using NebulaModel.DataStructures.Chat; +using NebulaModel.Logger; using NebulaModel.Packets.Chat; using NebulaWorld.MonoBehaviours.Local.Chat; @@ -45,33 +47,39 @@ public void Execute(ChatWindow window, string[] parameters) }; } - var userIdOrNameParameter = parameters[0]; - - var foundRecipient = false; - var recipient = new UserInfo(); - // TODO: Abstract this into a utility function to get the user data by id (that also works on clients - using (Multiplayer.Session.World.GetRemotePlayersModels(out var remotePlayersModels)) + UserInfo recipient; { - foreach (var remotePlayerModel in remotePlayersModels) + var userIdOrNameParameter = parameters[0]; + var couldParseUserId = ushort.TryParse(userIdOrNameParameter, out var recipientUserId); + + UserInfo? recipientOrNull = null; + if (couldParseUserId) { - var movement = remotePlayerModel.Value.Movement; - if ((ushort.TryParse(userIdOrNameParameter, out var recipientUserId) && movement.PlayerID == recipientUserId) || movement.Username == userIdOrNameParameter) + recipientOrNull = getUserInfoById(recipientUserId); + } + + if (recipientOrNull is UserInfo recipientNotNull) + { + recipient = recipientNotNull; + } + else + { + var recipientsByUsername = getUserInfosByUsername(userIdOrNameParameter); + + if (recipientsByUsername.Count == 0) { - foundRecipient = true; - recipient = new UserInfo - { - id = movement.PlayerID, - name = movement.Username - }; - break; + window.SendLocalChatMessage("Invalid recipient (user id or username not found), can't send gift".Translate(), ChatMessageType.CommandErrorMessage); + return; } - } - } - if (!foundRecipient) - { - window.SendLocalChatMessage("Invalid recipient (user id or username not found), can't send gift".Translate(), ChatMessageType.CommandErrorMessage); - return; + if (recipientsByUsername.Count > 1) + { + window.SendLocalChatMessage("Ambiguous recipient (multiple recipients with same username), can't send gift".Translate(), ChatMessageType.CommandErrorMessage); + return; + } + + recipient = recipientsByUsername.First(); + } } if (sender.id == recipient.id) @@ -95,8 +103,7 @@ public void Execute(ChatWindow window, string[] parameters) } // Add support for scientific notation and other notation types - long quantity; - if (!long.TryParse(parameters[2], out quantity) || quantity == 0) + if (!long.TryParse(parameters[2], out var quantity) || quantity == 0) { window.SendLocalChatMessage("Invalid gift quantity, can't send gift".Translate(), ChatMessageType.CommandErrorMessage); return; @@ -128,7 +135,7 @@ public void Execute(ChatWindow window, string[] parameters) switch (type) { case ChatCommandGiftType.Soil: - ChatManager.Instance.SendChatMessage($"[{DateTime.Now:HH:mm}] You gifted [{recipient.id}] {recipient.name} soil ({packet.Quantity})", ChatMessageType.SystemInfoMessage); + window.SendLocalChatMessage($"[{DateTime.Now:HH:mm}] You gifted [{recipient.id}] {recipient.name} soil ({packet.Quantity})", ChatMessageType.SystemInfoMessage); break; // TODO: Implement Item and Energy variants. } @@ -150,4 +157,48 @@ public string[] GetUsage() // ChatManager.Instance.SendChatMessage($"[{DateTime.Now:HH:mm}] [{sender} whispered] : {mesageBody}", // ChatMessageType.PlayerMessagePrivate); //} + + private UserInfo? getUserInfoById(ushort userId) + { + // TODO: This does not include self, perhaps we should include this + using (Multiplayer.Session.World.GetRemotePlayersModels(out var remotePlayersModels)) + { + foreach (var remotePlayerModel in remotePlayersModels) + { + var movement = remotePlayerModel.Value.Movement; + if (movement.PlayerID == userId) + { + return new UserInfo + { + id = movement.PlayerID, + name = movement.Username + }; + } + } + } + return null; + } + + private List getUserInfosByUsername(string username) + { + List userInfos = new(); + // TODO: This does not include self, perhaps we should include this + using (Multiplayer.Session.World.GetRemotePlayersModels(out var remotePlayersModels)) + { + foreach (var remotePlayerModel in remotePlayersModels) + { + var movement = remotePlayerModel.Value.Movement; + if (movement.Username == username) + { + userInfos.Add(new UserInfo + { + id = movement.PlayerID, + name = movement.Username + }); + } + } + } + + return userInfos; + } } From 5bfed9b848efc89a74a97cbefcaf2198a1edb80e Mon Sep 17 00:00:00 2001 From: MMjr <2429824+mmjr-x@users.noreply.github.com> Date: Mon, 15 Jan 2024 20:32:49 +0100 Subject: [PATCH 4/7] Refactored some of the logic --- .../Chat/Commands/GiftCommandHandler.cs | 47 ++++++++----------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs index 0e8025d17..6773201b6 100644 --- a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs +++ b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs @@ -160,45 +160,36 @@ public string[] GetUsage() private UserInfo? getUserInfoById(ushort userId) { - // TODO: This does not include self, perhaps we should include this using (Multiplayer.Session.World.GetRemotePlayersModels(out var remotePlayersModels)) { - foreach (var remotePlayerModel in remotePlayersModels) - { - var movement = remotePlayerModel.Value.Movement; - if (movement.PlayerID == userId) + // TODO: This does not include self, perhaps we should include this + var result = remotePlayersModels + .Select(remotePlayerModel => remotePlayerModel.Value.Movement) + .Where(movement => movement.PlayerID == userId) + .Select(movement => new UserInfo { - return new UserInfo - { - id = movement.PlayerID, - name = movement.Username - }; - } - } + id = movement.PlayerID, + name = movement.Username + }); + + return result.Any() ? result.First() : null; } - return null; } private List getUserInfosByUsername(string username) { - List userInfos = new(); - // TODO: This does not include self, perhaps we should include this using (Multiplayer.Session.World.GetRemotePlayersModels(out var remotePlayersModels)) { - foreach (var remotePlayerModel in remotePlayersModels) - { - var movement = remotePlayerModel.Value.Movement; - if (movement.Username == username) + // TODO: This does not include self, perhaps we should include this + return remotePlayersModels + .Select(remotePlayerModel => remotePlayerModel.Value.Movement) + .Where(movement => movement.Username == username) + .Select(movement => new UserInfo { - userInfos.Add(new UserInfo - { - id = movement.PlayerID, - name = movement.Username - }); - } - } + id = movement.PlayerID, + name = movement.Username + }) + .ToList(); } - - return userInfos; } } From ed717ff84f82f2b17c31ff9147528a3f78c42c4d Mon Sep 17 00:00:00 2001 From: MMjr <2429824+mmjr-x@users.noreply.github.com> Date: Tue, 16 Jan 2024 21:00:23 +0100 Subject: [PATCH 5/7] Experimented with adding a ClientRelayFailurePacket --- .../LiteNetLib/NetPacketProcessor.cs | 18 +++++++++ .../Routers/ClientRelayFailurePacket.cs | 17 ++++++++ .../Packets/Routers/ClientRelayPacket.cs | 2 + .../Routers/ClientRelayFailureProcessor.cs | 40 +++++++++++++++++++ .../Routers/ClientRelayProcessor.cs | 24 +++++++++++ 5 files changed, 101 insertions(+) create mode 100644 NebulaModel/Packets/Routers/ClientRelayFailurePacket.cs create mode 100644 NebulaNetwork/PacketProcessors/Routers/ClientRelayFailureProcessor.cs diff --git a/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs b/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs index b1875cd0a..46936b589 100644 --- a/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs +++ b/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs @@ -291,5 +291,23 @@ public bool RemoveSubscription() _callbacksDebugInfo.Remove(GetHash()); return _callbacks.Remove(GetHash()); } + + public Type GetPacketTypeFromData(NetDataReader reader) + { + var originalPosition = reader.Position; + var hash = reader.GetULong(); + reader.SetPosition(originalPosition); + // TODO: Probably use TryGetValue instead + return _callbacksDebugInfo[hash]; + } + + //public T GetPacketFromData(NetDataReader reader) where T : INetSerializable, new() + //{ + // var reference = new T(); + // var originalPosition = reader.Position; + // reference.Deserialize(reader); + // reader.SetPosition(originalPosition); + // return reference; + //} } } diff --git a/NebulaModel/Packets/Routers/ClientRelayFailurePacket.cs b/NebulaModel/Packets/Routers/ClientRelayFailurePacket.cs new file mode 100644 index 000000000..ce198548d --- /dev/null +++ b/NebulaModel/Packets/Routers/ClientRelayFailurePacket.cs @@ -0,0 +1,17 @@ +namespace NebulaModel.Packets.Routers; + +public class ClientRelayFailurePacket +{ + public ClientRelayFailurePacket() { } + + public ClientRelayFailurePacket(byte[] packetObject, ushort clientUserId) + { + // TODO: We should probably rename this to PacketObjectThatFailedToBeRelayed or something + PacketObject = packetObject; + // TODO: We should probably rename this to RecipientClientUserId or something + ClientUserId = clientUserId; + } + + public byte[] PacketObject { get; set; } + public ushort ClientUserId { get; set; } +} diff --git a/NebulaModel/Packets/Routers/ClientRelayPacket.cs b/NebulaModel/Packets/Routers/ClientRelayPacket.cs index a680c52e9..4073ee540 100644 --- a/NebulaModel/Packets/Routers/ClientRelayPacket.cs +++ b/NebulaModel/Packets/Routers/ClientRelayPacket.cs @@ -6,7 +6,9 @@ public ClientRelayPacket() { } public ClientRelayPacket(byte[] packetObject, ushort clientUserId) { + // TODO: We should probably rename this to PacketObjectToRelay or something PacketObject = packetObject; + // TODO: We should probably rename this to RecipientClientUserId or something ClientUserId = clientUserId; } diff --git a/NebulaNetwork/PacketProcessors/Routers/ClientRelayFailureProcessor.cs b/NebulaNetwork/PacketProcessors/Routers/ClientRelayFailureProcessor.cs new file mode 100644 index 000000000..b690de8de --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Routers/ClientRelayFailureProcessor.cs @@ -0,0 +1,40 @@ +#region + +using NebulaAPI.GameState; +using NebulaAPI.Packets; +using NebulaModel; +using NebulaModel.Logger; +using NebulaModel.Networking; +using NebulaModel.Networking.Serialization; +using NebulaModel.Packets; +using NebulaModel.Packets.Routers; +using NebulaWorld; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Routers; + +[RegisterPacketProcessor] +internal class ClientRelayFailureProcessor : PacketProcessor +{ + private readonly IPlayerManager playerManager; + + public ClientRelayFailureProcessor() + { + playerManager = Multiplayer.Session.Network.PlayerManager; + } + + protected override void ProcessPacket(ClientRelayPacket packet, NebulaConnection conn) + { + // This if the processor is the host do nothing (this should never be recieved by the host as it is the relayer) + if (IsHost) + { + return; + } + + // Else process the wrapped packet + //((NetworkProvider)Multiplayer.Session.Network).PacketProcessor + // .EnqueuePacketForProcessing(packet.PacketObject, conn); + + } +} diff --git a/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs b/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs index ed66ce69c..ec5f101d8 100644 --- a/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs +++ b/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs @@ -1,11 +1,14 @@ #region +using System; using NebulaAPI.GameState; using NebulaAPI.Packets; using NebulaModel; using NebulaModel.Logger; using NebulaModel.Networking; +using NebulaModel.Networking.Serialization; using NebulaModel.Packets; +using NebulaModel.Packets.Chat; using NebulaModel.Packets.Routers; using NebulaWorld; @@ -58,6 +61,27 @@ protected override void ProcessPacket(ClientRelayPacket packet, NebulaConnection { Log.Warn($"Could not relay packet because client was not found with clientId: {packet.ClientUserId}"); + //var packetType = ((NetworkProvider)Multiplayer.Session.Network).PacketProcessor.GetPacketTypeFromData(new NetDataReader(packet.PacketObject)); + + //if (packetType == new ChatCommandGiftPacket().GetType()) { } + + var packetProcessor = ((NetworkProvider)Multiplayer.Session.Network).PacketProcessor; + + var reader = new NetDataReader(packet.PacketObject); + var packetType = packetProcessor.GetPacketTypeFromData(reader); + + switch (Activator.CreateInstance(packetType)) + { + case ChatCommandGiftPacket _: + //var innerPacket = packetProcessor.GetPacketFromData(reader); + Log.Warn($"Packet is ChatCommandGiftPacket"); + break; + } + + + //var failurePacket = new ClientRelayFailurePacket(packet.PacketObject, packet.ClientUserId); + //conn.SendPacket(failurePacket); + // TODO: We might want to communicate back to the sender that the relaying failed return; } From 6ea817363ae0ced74112b3db00f4a9393f516ba3 Mon Sep 17 00:00:00 2001 From: MMjr <2429824+mmjr-x@users.noreply.github.com> Date: Tue, 16 Jan 2024 22:37:36 +0100 Subject: [PATCH 6/7] Removed all the packetRelay logic and have the relaying now be done in the gift packet processor --- NebulaAPI/GameState/INetworkProvider.cs | 5 - NebulaModel/NetworkProvider.cs | 3 - .../LiteNetLib/NetPacketProcessor.cs | 18 ---- .../Routers/ClientRelayFailurePacket.cs | 17 ---- NebulaNetwork/Client.cs | 5 - .../Chat/ChatCommandGiftProcessor.cs | 20 ++++ .../Routers/ClientRelayFailureProcessor.cs | 40 -------- .../Routers/ClientRelayProcessor.cs | 91 ------------------- NebulaNetwork/Server.cs | 12 --- .../Chat/Commands/GiftCommandHandler.cs | 19 +++- 10 files changed, 38 insertions(+), 192 deletions(-) delete mode 100644 NebulaModel/Packets/Routers/ClientRelayFailurePacket.cs delete mode 100644 NebulaNetwork/PacketProcessors/Routers/ClientRelayFailureProcessor.cs delete mode 100644 NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs diff --git a/NebulaAPI/GameState/INetworkProvider.cs b/NebulaAPI/GameState/INetworkProvider.cs index d0a00659f..d1786cbdc 100644 --- a/NebulaAPI/GameState/INetworkProvider.cs +++ b/NebulaAPI/GameState/INetworkProvider.cs @@ -46,10 +46,5 @@ public interface INetworkProvider : IDisposable /// void SendPacketToStarExclude(T packet, int starId, INebulaConnection exclude) where T : class, new(); - /// - /// Send packet to Client directly (If possible) or indirectly by relaying via host (If Client is not directly reachable) - /// - void SendPacketToClient(T packet, ushort clientUserId) where T : class, new (); - void Update(); } diff --git a/NebulaModel/NetworkProvider.cs b/NebulaModel/NetworkProvider.cs index d87c6afcf..bf7f68ebd 100644 --- a/NebulaModel/NetworkProvider.cs +++ b/NebulaModel/NetworkProvider.cs @@ -38,9 +38,6 @@ public abstract void SendPacketExclude(T packet, INebulaConnection exclude) public abstract void SendPacketToStarExclude(T packet, int starId, INebulaConnection exclude) where T : class, new(); - public abstract void SendPacketToClient(T packet, ushort clientUserId) - where T : class, new(); - public abstract void Update(); public abstract void Start(); diff --git a/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs b/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs index 46936b589..b1875cd0a 100644 --- a/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs +++ b/NebulaModel/Networking/Serialization/LiteNetLib/NetPacketProcessor.cs @@ -291,23 +291,5 @@ public bool RemoveSubscription() _callbacksDebugInfo.Remove(GetHash()); return _callbacks.Remove(GetHash()); } - - public Type GetPacketTypeFromData(NetDataReader reader) - { - var originalPosition = reader.Position; - var hash = reader.GetULong(); - reader.SetPosition(originalPosition); - // TODO: Probably use TryGetValue instead - return _callbacksDebugInfo[hash]; - } - - //public T GetPacketFromData(NetDataReader reader) where T : INetSerializable, new() - //{ - // var reference = new T(); - // var originalPosition = reader.Position; - // reference.Deserialize(reader); - // reader.SetPosition(originalPosition); - // return reference; - //} } } diff --git a/NebulaModel/Packets/Routers/ClientRelayFailurePacket.cs b/NebulaModel/Packets/Routers/ClientRelayFailurePacket.cs deleted file mode 100644 index ce198548d..000000000 --- a/NebulaModel/Packets/Routers/ClientRelayFailurePacket.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace NebulaModel.Packets.Routers; - -public class ClientRelayFailurePacket -{ - public ClientRelayFailurePacket() { } - - public ClientRelayFailurePacket(byte[] packetObject, ushort clientUserId) - { - // TODO: We should probably rename this to PacketObjectThatFailedToBeRelayed or something - PacketObject = packetObject; - // TODO: We should probably rename this to RecipientClientUserId or something - ClientUserId = clientUserId; - } - - public byte[] PacketObject { get; set; } - public ushort ClientUserId { get; set; } -} diff --git a/NebulaNetwork/Client.cs b/NebulaNetwork/Client.cs index 5e4689eef..6d6960ff3 100644 --- a/NebulaNetwork/Client.cs +++ b/NebulaNetwork/Client.cs @@ -188,11 +188,6 @@ public override void SendPacketToStarExclude(T packet, int starId, INebulaCon throw new NotImplementedException(); } - public override void SendPacketToClient(T packet, ushort clientUserId) - { - serverConnection?.SendPacket(new ClientRelayPacket(PacketProcessor.Write(packet), clientUserId)); - } - public override void Update() { PacketProcessor.ProcessPacketQueue(); diff --git a/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs b/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs index f1367ad43..a4c452ba4 100644 --- a/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs +++ b/NebulaNetwork/PacketProcessors/Chat/ChatCommandGiftProcessor.cs @@ -21,6 +21,26 @@ internal class ChatCommandGiftProcessor : PacketProcessor { protected override void ProcessPacket(ChatCommandGiftPacket packet, NebulaConnection conn) { + // If you are not the intended recipient of this packet do not process this packet + if (packet.RecipientUserId != Multiplayer.Session.LocalPlayer.Data.PlayerId) + { + // However if you are the host relay the packet to the recipient + if (IsHost) + { + var recipient = Multiplayer.Session.Network.PlayerManager.GetPlayerById(packet.RecipientUserId); + if (recipient != null) + { + recipient.SendPacket(packet); + } + else + { + Log.Warn($"Could not relay packet because recipient was not found with clientId: {packet.RecipientUserId}"); + // TODO: if the recipient is not found return the failure packet + //conn.SendPacket(giftFailedPacket); // the giftFailedPacket needs the same kind of handling as that can also need to be relayed + } + } + return; + } //window.SendLocalChatMessage("Invalid gift type".Translate(), ChatMessageType.CommandErrorMessage); string senderUserName = null; diff --git a/NebulaNetwork/PacketProcessors/Routers/ClientRelayFailureProcessor.cs b/NebulaNetwork/PacketProcessors/Routers/ClientRelayFailureProcessor.cs deleted file mode 100644 index b690de8de..000000000 --- a/NebulaNetwork/PacketProcessors/Routers/ClientRelayFailureProcessor.cs +++ /dev/null @@ -1,40 +0,0 @@ -#region - -using NebulaAPI.GameState; -using NebulaAPI.Packets; -using NebulaModel; -using NebulaModel.Logger; -using NebulaModel.Networking; -using NebulaModel.Networking.Serialization; -using NebulaModel.Packets; -using NebulaModel.Packets.Routers; -using NebulaWorld; - -#endregion - -namespace NebulaNetwork.PacketProcessors.Routers; - -[RegisterPacketProcessor] -internal class ClientRelayFailureProcessor : PacketProcessor -{ - private readonly IPlayerManager playerManager; - - public ClientRelayFailureProcessor() - { - playerManager = Multiplayer.Session.Network.PlayerManager; - } - - protected override void ProcessPacket(ClientRelayPacket packet, NebulaConnection conn) - { - // This if the processor is the host do nothing (this should never be recieved by the host as it is the relayer) - if (IsHost) - { - return; - } - - // Else process the wrapped packet - //((NetworkProvider)Multiplayer.Session.Network).PacketProcessor - // .EnqueuePacketForProcessing(packet.PacketObject, conn); - - } -} diff --git a/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs b/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs deleted file mode 100644 index ec5f101d8..000000000 --- a/NebulaNetwork/PacketProcessors/Routers/ClientRelayProcessor.cs +++ /dev/null @@ -1,91 +0,0 @@ -#region - -using System; -using NebulaAPI.GameState; -using NebulaAPI.Packets; -using NebulaModel; -using NebulaModel.Logger; -using NebulaModel.Networking; -using NebulaModel.Networking.Serialization; -using NebulaModel.Packets; -using NebulaModel.Packets.Chat; -using NebulaModel.Packets.Routers; -using NebulaWorld; - -#endregion - -namespace NebulaNetwork.PacketProcessors.Routers; - -[RegisterPacketProcessor] -internal class ClientRelayProcessor : PacketProcessor -{ - private readonly IPlayerManager playerManager; - - public ClientRelayProcessor() - { - playerManager = Multiplayer.Session.Network.PlayerManager; - } - - protected override void ProcessPacket(ClientRelayPacket packet, NebulaConnection conn) - { - // TODO: check if this player null check is required - var player = playerManager.GetPlayer(conn); - if (player == null || packet.PacketObject == null) - { - return; - } - - // If the processor is either host or client and the recipient is the processor unwrap and process the packet - // (This happens in the case that either client -> host or host -> client sends this packet directly, - // as you do not want manually account for the sender recipient relation when using this packet we have to handle this case as wel) - if (Multiplayer.Session.LocalPlayer.Data.PlayerId == packet.ClientUserId) - { - //Process the packet on the host - // NOTE: Since PacketProcessor is not part of INetworkProvider this will not work if we ever swap it out with another network provider that does not expose this - // However StarBroadcastProcessor and PlanetBroadcastProcessor also do this in a similar manner so I think it is fine for now - ((NetworkProvider)Multiplayer.Session.Network).PacketProcessor - .EnqueuePacketForProcessing(packet.PacketObject, conn); - return; - } - - // If the processor is client and not the recipient, do nothing - if (IsClient) - { - return; - } - - // If the processor is the host and not the recipient, unwrap and relay packet to recipient client - var recipient = Multiplayer.Session.Network - .PlayerManager.GetPlayerById(packet.ClientUserId); - if (recipient == null) - { - Log.Warn($"Could not relay packet because client was not found with clientId: {packet.ClientUserId}"); - - //var packetType = ((NetworkProvider)Multiplayer.Session.Network).PacketProcessor.GetPacketTypeFromData(new NetDataReader(packet.PacketObject)); - - //if (packetType == new ChatCommandGiftPacket().GetType()) { } - - var packetProcessor = ((NetworkProvider)Multiplayer.Session.Network).PacketProcessor; - - var reader = new NetDataReader(packet.PacketObject); - var packetType = packetProcessor.GetPacketTypeFromData(reader); - - switch (Activator.CreateInstance(packetType)) - { - case ChatCommandGiftPacket _: - //var innerPacket = packetProcessor.GetPacketFromData(reader); - Log.Warn($"Packet is ChatCommandGiftPacket"); - break; - } - - - //var failurePacket = new ClientRelayFailurePacket(packet.PacketObject, packet.ClientUserId); - //conn.SendPacket(failurePacket); - - // TODO: We might want to communicate back to the sender that the relaying failed - return; - } - - recipient.Connection.SendRawPacket(packet.PacketObject); - } -} diff --git a/NebulaNetwork/Server.cs b/NebulaNetwork/Server.cs index 9273a5eb4..9bd113d70 100644 --- a/NebulaNetwork/Server.cs +++ b/NebulaNetwork/Server.cs @@ -244,18 +244,6 @@ public override void SendPacketToStarExclude(T packet, int starId, INebulaCon PlayerManager.SendPacketToStarExcept(packet, starId, exclude); } - public override void SendPacketToClient(T packet, ushort clientUserId) - { - var recipient = PlayerManager.GetPlayerById(clientUserId); - if (recipient == null) - { - Log.Warn($"Could not send packet to client, Recipient not found with clientId: {clientUserId}"); - return; - } - - recipient.SendPacket(packet); - } - public override void Update() { PacketProcessor.ProcessPacketQueue(); diff --git a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs index 6773201b6..338e746a5 100644 --- a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs +++ b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs @@ -130,7 +130,24 @@ public void Execute(ChatWindow window, string[] parameters) } var packet = new ChatCommandGiftPacket(sender.id, recipient.id, type, quantity); - Multiplayer.Session.Network.SendPacketToClient(packet, recipient.id); + if (Multiplayer.Session.LocalPlayer.IsHost) + { + // If you are the host, you can directly send the packet to the recipient + var recipientConnection = Multiplayer.Session.Network.PlayerManager.GetPlayerById(recipient.id); + // TODO: This determination should be done before the sand acctually gets deducted (no need to do deduct sand if this fails + if (recipientConnection == null) + { + window.SendLocalChatMessage("Player not found: ".Translate() + recipient.name, ChatMessageType.CommandErrorMessage); + return; + } + + recipientConnection.SendPacket(packet); + } + else + { + // Else send it to the host who can relay it + Multiplayer.Session.Network.SendPacket(packet); + } switch (type) { From 1be6f51ff7c9fecc98d3dd977ea88d68ba2e94c2 Mon Sep 17 00:00:00 2001 From: MMjr <2429824+mmjr-x@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:49:12 +0100 Subject: [PATCH 7/7] Some refactoring to make the flow more logical --- .../Chat/Commands/GiftCommandHandler.cs | 61 +++++++++++-------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs index 338e746a5..dd12a15a6 100644 --- a/NebulaWorld/Chat/Commands/GiftCommandHandler.cs +++ b/NebulaWorld/Chat/Commands/GiftCommandHandler.cs @@ -109,50 +109,57 @@ public void Execute(ChatWindow window, string[] parameters) return; } - // Validate that you acctually have the required soil/items/energy to gift - switch (type) - { - case ChatCommandGiftType.Soil: - var mainPlayer = GameMain.data.mainPlayer; - lock (mainPlayer) - { - if (mainPlayer.sandCount < quantity) - { - window.SendLocalChatMessage("You dont have enough soil to send, can't send gift".Translate(), ChatMessageType.CommandErrorMessage); - return; - } - - mainPlayer.SetSandCount(mainPlayer.sandCount - quantity); - // TODO: Do we need to do something with soil sync? - } - break; - // TODO: Implement Item and Energy variants. - } - - var packet = new ChatCommandGiftPacket(sender.id, recipient.id, type, quantity); + Action sendPacket; if (Multiplayer.Session.LocalPlayer.IsHost) { // If you are the host, you can directly send the packet to the recipient var recipientConnection = Multiplayer.Session.Network.PlayerManager.GetPlayerById(recipient.id); - // TODO: This determination should be done before the sand acctually gets deducted (no need to do deduct sand if this fails if (recipientConnection == null) { - window.SendLocalChatMessage("Player not found: ".Translate() + recipient.name, ChatMessageType.CommandErrorMessage); + window.SendLocalChatMessage("Invalid recipient (no connection), can't send gift".Translate(), ChatMessageType.CommandErrorMessage); return; } - recipientConnection.SendPacket(packet); + sendPacket = (packet) => + { + recipientConnection.SendPacket(packet); + }; } else { // Else send it to the host who can relay it - Multiplayer.Session.Network.SendPacket(packet); + sendPacket = (packet) => + { + Multiplayer.Session.Network.SendPacket(packet); + }; } + // Validate that you actually have the required soil/items/energy to gift switch (type) { case ChatCommandGiftType.Soil: - window.SendLocalChatMessage($"[{DateTime.Now:HH:mm}] You gifted [{recipient.id}] {recipient.name} soil ({packet.Quantity})", ChatMessageType.SystemInfoMessage); + var packet = new ChatCommandGiftPacket(sender.id, recipient.id, type, quantity); + var mainPlayer = GameMain.data.mainPlayer; + bool sufficient; + lock (mainPlayer) + { + var remainingSand = mainPlayer.sandCount - quantity; + sufficient = remainingSand >= 0; + if (sufficient) + { + sendPacket(packet); + mainPlayer.SetSandCount(remainingSand); + // TODO: Do we need to do something with soil sync? + } + } + + if (!sufficient) + { + window.SendLocalChatMessage("You dont have enough soil to send, can't send gift".Translate(), ChatMessageType.CommandErrorMessage); + return; + } + // TODO: I don't think this is translatable since it contains dynamic data, look into this + window.SendLocalChatMessage($"[{DateTime.Now:HH:mm}] You gifted [{recipient.id}] {recipient.name} soil ({quantity})".Translate(), ChatMessageType.SystemInfoMessage); break; // TODO: Implement Item and Energy variants. } @@ -165,7 +172,7 @@ public string GetDescription() public string[] GetUsage() { - return [" "]; + return [" "]; } // TODO: We should add logic here that acctually adds the gifted materials (and devise something to substract the materials)