From cb26102bc1ad9142f96ff3afaef3de7c0af120c2 Mon Sep 17 00:00:00 2001 From: Lacyway <20912169+Lacyway@users.noreply.github.com> Date: Wed, 5 Jun 2024 22:09:53 +0200 Subject: [PATCH] Add text chat --- Fika.Core/Coop/Custom/FikaChat.cs | 134 ++++++++++++++++++ Fika.Core/Networking/FikaClient.cs | 61 +++++--- Fika.Core/Networking/FikaServer.cs | 54 ++++--- .../Communication/TextMessagePacket.cs | 22 +++ 4 files changed, 234 insertions(+), 37 deletions(-) create mode 100644 Fika.Core/Coop/Custom/FikaChat.cs create mode 100644 Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs diff --git a/Fika.Core/Coop/Custom/FikaChat.cs b/Fika.Core/Coop/Custom/FikaChat.cs new file mode 100644 index 00000000..2be8436e --- /dev/null +++ b/Fika.Core/Coop/Custom/FikaChat.cs @@ -0,0 +1,134 @@ +using Comfort.Common; +using EFT.UI; +using Fika.Core.Coop.Matchmaker; +using Fika.Core.Networking; +using Fika.Core.Networking.Packets.Communication; +using HarmonyLib; +using LiteNetLib.Utils; +using System; +using UnityEngine; + +namespace Fika.Core.Coop.Custom +{ + internal class FikaChat : MonoBehaviour + { + private Rect windowRect; + private string nickname; + private string chatText; + private string textField; + private bool show; + private bool isServer; + private NetDataWriter writer; + private UISoundsWrapper soundsWrapper; + + protected void Awake() + { + windowRect = new(20, Screen.height - 260, 600, 250); + nickname = MatchmakerAcceptPatches.PMCName; + chatText = string.Empty; + textField = string.Empty; + show = false; + isServer = MatchmakerAcceptPatches.IsServer; + writer = new(); + GUISounds guiSounds = Singleton.Instance; + soundsWrapper = Traverse.Create(guiSounds).Field("uisoundsWrapper_0").Value; + } + + protected void Update() + { + if (Input.GetKeyDown(KeyCode.Backspace)) + { + ToggleVisibility(); + } + } + + protected void OnGUI() + { + if (!show) + { + return; + } + + GUI.skin.label.alignment = TextAnchor.LowerLeft; + GUI.skin.window.alignment = TextAnchor.UpperLeft; + GUI.Window(0, windowRect, DrawWindow, "Fika Chat"); + + if (Event.current.isKey) + { + if (Event.current.keyCode is KeyCode.Return or KeyCode.KeypadEnter && show) + { + SendMessage(); + GUI.UnfocusWindow(); + GUI.FocusControl(null); + } + } + } + + private void ToggleVisibility() + { + show = !show; + } + + private void SendMessage() + { + if (!show) + { + return; + } + + if (!string.IsNullOrEmpty(textField)) + { + string message = textField; + textField = string.Empty; + + TextMessagePacket packet = new(nickname, message); + writer.Reset(); + + if (isServer) + { + Singleton.Instance.SendDataToAll(writer, ref packet, LiteNetLib.DeliveryMethod.ReliableUnordered); + } + else + { + Singleton.Instance.SendData(writer, ref packet, LiteNetLib.DeliveryMethod.ReliableUnordered); + } + AudioClip outgoingClip = soundsWrapper.GetSocialNetworkClip(ESocialNetworkSoundType.OutgoingMessage); + Singleton.Instance.PlaySound(outgoingClip); + AddMessage(nickname + ": " + message); + } + } + + public void ReceiveMessage(string nickname, string message) + { + AudioClip incomingClip = soundsWrapper.GetSocialNetworkClip(ESocialNetworkSoundType.IncomingMessage); + Singleton.Instance.PlaySound(incomingClip); + AddMessage(nickname + ": " + message); + } + + private void AddMessage(string message) + { + chatText = string.Concat(chatText, message, "\n"); + if (chatText.Length > 1000) + { + chatText = chatText.Substring(500); + } + } + + private void DrawWindow(int windowId) + { + Rect rect = new(5, 15, 580, 200); + GUI.Label(rect, chatText); + rect.y += rect.height; + Rect textRect = new(rect.x, rect.y, rect.width - 55, 25); + textField = GUI.TextField(textRect, textField); + rect.x += 535; + Rect buttonRect = new(rect.x, rect.y, 50, 25); + if (GUI.Button(buttonRect, "SEND")) + { + SendMessage(); + GUI.UnfocusWindow(); + GUI.FocusControl(null); + } + } + } +} diff --git a/Fika.Core/Networking/FikaClient.cs b/Fika.Core/Networking/FikaClient.cs index 17d71159..9e9264b2 100644 --- a/Fika.Core/Networking/FikaClient.cs +++ b/Fika.Core/Networking/FikaClient.cs @@ -10,11 +10,13 @@ using EFT.UI; using EFT.Weather; using Fika.Core.Coop.Components; +using Fika.Core.Coop.Custom; using Fika.Core.Coop.GameMode; using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; using Fika.Core.Modding; using Fika.Core.Modding.Events; +using Fika.Core.Networking.Packets.Communication; using Fika.Core.Networking.Packets.GameWorld; using Fika.Core.Networking.Packets.Player; using HarmonyLib; @@ -51,7 +53,7 @@ public NetManager NetClient /*public string IP { get; private set; } public int Port { get; private set; }*/ public bool SpawnPointsReceived { get; private set; } = false; - private readonly ManualLogSource clientLogger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Client"); public bool Started { get @@ -63,6 +65,7 @@ public bool Started return _netClient.IsRunning; } } + private FikaChat fikaChat; public void Init() { @@ -89,6 +92,7 @@ public void Init() packetProcessor.SubscribeNetSerializable(OnAssignNetIdPacketReceived); packetProcessor.SubscribeNetSerializable(OnSyncNetIdPacketReceived); packetProcessor.SubscribeNetSerializable(OnOperationCallbackPacketReceived); + packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); _netClient = new NetManager(this) { @@ -124,10 +128,21 @@ public void Init() FikaEventDispatcher.DispatchEvent(new FikaClientCreatedEvent(this)); } + private void OnTextMessagePacketReceived(TextMessagePacket packet) + { + logger.LogInfo($"Received message from: {packet.Nickname}, Message: {packet.Message}"); + + if (fikaChat != null) + { + fikaChat.ReceiveMessage(packet.Nickname, packet.Message); + } + } + public void SetupGameVariables(CoopPlayer coopPlayer) { coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); MyPlayer = coopPlayer; + fikaChat = gameObject.AddComponent(); } private void OnOperationCallbackPacketReceived(OperationCallbackPacket packet) @@ -277,7 +292,7 @@ private void OnWeatherPacketReceived(WeatherPacket packet) } else { - clientLogger.LogWarning("WeatherPacket2Received: WeatherControll was null!"); + logger.LogWarning("WeatherPacket2Received: WeatherControll was null!"); } } } @@ -324,7 +339,7 @@ private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) } else { - clientLogger.LogWarning($"ExfiltrationPacketPacketReceived::ExfiltrationPoints: Could not find exfil point with name '{exfilPoint.Key}'"); + logger.LogWarning($"ExfiltrationPacketPacketReceived::ExfiltrationPoints: Could not find exfil point with name '{exfilPoint.Key}'"); } } @@ -352,7 +367,7 @@ private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) } else { - clientLogger.LogWarning($"ExfiltrationPacketPacketReceived::ScavExfiltrationPoints: Could not find exfil point with name '{scavExfilPoint.Key}'"); + logger.LogWarning($"ExfiltrationPacketPacketReceived::ScavExfiltrationPoints: Could not find exfil point with name '{scavExfilPoint.Key}'"); } } @@ -364,7 +379,7 @@ private void OnExfiltrationPacketReceived(ExfiltrationPacket packet) } else { - clientLogger.LogWarning($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); + logger.LogWarning($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); } } } @@ -416,7 +431,7 @@ private void OnGenericPacketReceived(GenericPacket packet) } else { - clientLogger.LogWarning("GenericPacketReceived: Could not find locomotive!"); + logger.LogWarning("GenericPacketReceived: Could not find locomotive!"); } } break; @@ -461,16 +476,16 @@ private void OnGenericPacketReceived(GenericPacket packet) { botToDispose.Dispose(); AssetPoolObject.ReturnToPool(botToDispose.gameObject, true); - clientLogger.LogInfo("Disposing bot: " + packet.BotNetId); + logger.LogInfo("Disposing bot: " + packet.BotNetId); } else { - clientLogger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); + logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId); } } else { - clientLogger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); + logger.LogWarning("Unable to dispose of bot: " + packet.BotNetId + ", unable to find GameObject"); } } break; @@ -489,13 +504,13 @@ private void OnGenericPacketReceived(GenericPacket packet) if (!botToEnable.gameObject.activeSelf) { #if DEBUG - clientLogger.LogWarning("Enabling " + packet.BotNetId); + logger.LogWarning("Enabling " + packet.BotNetId); #endif botToEnable.gameObject.SetActive(true); } else { - clientLogger.LogWarning($"Received packet to enable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already enabled!"); + logger.LogWarning($"Received packet to enable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already enabled!"); } } } @@ -507,13 +522,13 @@ private void OnGenericPacketReceived(GenericPacket packet) if (botToEnable.gameObject.activeSelf) { #if DEBUG - clientLogger.LogWarning("Disabling " + packet.BotNetId); + logger.LogWarning("Disabling " + packet.BotNetId); #endif botToEnable.gameObject.SetActive(false); } else { - clientLogger.LogWarning($"Received packet to disable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already disabled!"); + logger.LogWarning($"Received packet to disable {botToEnable.ProfileId}, netId {packet.BotNetId} but the bot was already disabled!"); } } } @@ -537,7 +552,7 @@ private void OnAirdropLootPacketReceived(AirdropLootPacket packet) } else { - clientLogger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); + logger.LogError("OnAirdropLootPacketReceived: Received loot package but manager is not instantiated!"); } } @@ -564,7 +579,7 @@ private void OnAirdropPacketReceived(AirdropPacket packet) } else { - clientLogger.LogError("OnAirdropPacketReceived: Received package but manager is not instantiated!"); + logger.LogError("OnAirdropPacketReceived: Received package but manager is not instantiated!"); } } @@ -581,7 +596,7 @@ private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packe { if (!packet.IsRequest) { - clientLogger.LogInfo($"Received CharacterRequest! ProfileID: {packet.PlayerInfo.Profile.ProfileId}, Nickname: {packet.PlayerInfo.Profile.Nickname}"); + logger.LogInfo($"Received CharacterRequest! ProfileID: {packet.PlayerInfo.Profile.ProfileId}, Nickname: {packet.PlayerInfo.Profile.Nickname}"); if (packet.ProfileId != MyPlayer.ProfileId) { coopHandler.QueueProfile(packet.PlayerInfo.Profile, packet.Position, packet.NetId, packet.IsAlive, packet.IsAI); @@ -589,7 +604,7 @@ private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packe } else if (packet.IsRequest) { - clientLogger.LogInfo($"Received CharacterRequest from server, send my Profile."); + logger.LogInfo($"Received CharacterRequest from server, send my Profile."); AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId) { IsRequest = false, @@ -689,6 +704,12 @@ protected void Update() protected void OnDestroy() { _netClient?.Stop(); + + if (fikaChat != null) + { + Destroy(fikaChat); + } + FikaEventDispatcher.DispatchEvent(new FikaClientDestroyedEvent(this)); } @@ -706,7 +727,7 @@ public void OnPeerConnected(NetPeer peer) public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) { - clientLogger.LogInfo("[CLIENT] We received error " + socketErrorCode); + logger.LogInfo("[CLIENT] We received error " + socketErrorCode); } public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) @@ -718,7 +739,7 @@ public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketRead { if (messageType == UnconnectedMessageType.BasicMessage && _netClient.ConnectedPeersCount == 0 && reader.GetInt() == 1) { - clientLogger.LogInfo("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint); + logger.LogInfo("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint); _netClient.Connect(remoteEndPoint, "fika.core"); } } @@ -737,7 +758,7 @@ public void OnConnectionRequest(ConnectionRequest request) public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { - clientLogger.LogInfo("[CLIENT] We disconnected because " + disconnectInfo.Reason); + logger.LogInfo("[CLIENT] We disconnected because " + disconnectInfo.Reason); if (disconnectInfo.Reason is DisconnectReason.Timeout) { NotificationManagerClass.DisplayWarningNotification("Lost connection to host!"); diff --git a/Fika.Core/Networking/FikaServer.cs b/Fika.Core/Networking/FikaServer.cs index 4e49f296..8766249d 100644 --- a/Fika.Core/Networking/FikaServer.cs +++ b/Fika.Core/Networking/FikaServer.cs @@ -8,6 +8,7 @@ using EFT.InventoryLogic; using EFT.UI; using Fika.Core.Coop.Components; +using Fika.Core.Coop.Custom; using Fika.Core.Coop.GameMode; using Fika.Core.Coop.Matchmaker; using Fika.Core.Coop.Players; @@ -15,6 +16,7 @@ using Fika.Core.Modding.Events; using Fika.Core.Networking.Http; using Fika.Core.Networking.Http.Models; +using Fika.Core.Networking.Packets.Communication; using Fika.Core.Networking.Packets.GameWorld; using Fika.Core.Networking.Packets.Player; using LiteNetLib; @@ -54,7 +56,7 @@ public NetManager NetServer } public DateTime timeSinceLastPeerDisconnected = DateTime.Now.AddDays(1); public bool hasHadPeer = false; - private readonly ManualLogSource serverLogger = BepInEx.Logging.Logger.CreateLogSource("Fika.Server"); + private readonly ManualLogSource logger = BepInEx.Logging.Logger.CreateLogSource("Fika.Server"); private int _currentNetId; public bool Started { @@ -67,14 +69,13 @@ public bool Started return _netServer.IsRunning; } } + private FikaChat fikaChat; public async Task Init() { // Start at 1 to avoid having 0 and making us think it's working when it's not _currentNetId = 1; - NetDebug.Logger = this; - packetProcessor.SubscribeNetSerializable(OnPlayerStatePacketReceived); packetProcessor.SubscribeNetSerializable(OnGameTimerPacketReceived); packetProcessor.SubscribeNetSerializable(OnFirearmPacketReceived); @@ -93,6 +94,7 @@ public async Task Init() packetProcessor.SubscribeNetSerializable(OnMinePacketReceived); packetProcessor.SubscribeNetSerializable(OnBorderZonePacketReceived); packetProcessor.SubscribeNetSerializable(OnSendCharacterPacketReceived); + packetProcessor.SubscribeNetSerializable(OnTextMessagePacketReceived); _netServer = new NetManager(this) { @@ -122,7 +124,7 @@ public async Task Init() } catch (Exception ex) { - serverLogger.LogError($"Error when attempting to map UPnP. Make sure the selected port is not already open! Error message: {ex.Message}"); + logger.LogError($"Error when attempting to map UPnP. Make sure the selected port is not already open! Error message: {ex.Message}"); upnpFailed = true; } @@ -159,7 +161,7 @@ public async Task Init() _netServer.Start(Port); } - serverLogger.LogInfo("Started Fika Server"); + logger.LogInfo("Started Fika Server"); NotificationManagerClass.DisplayMessageNotification($"Server started on port {_netServer.LocalPort}.", EFT.Communications.ENotificationDurationType.Default, EFT.Communications.ENotificationIconType.EntryPoint); @@ -186,6 +188,19 @@ public async Task Init() FikaEventDispatcher.DispatchEvent(new FikaServerCreatedEvent(this)); } + private void OnTextMessagePacketReceived(TextMessagePacket packet, NetPeer peer) + { + logger.LogInfo($"Received message from: {packet.Nickname}, Message: {packet.Message}"); + + if (fikaChat != null) + { + fikaChat.ReceiveMessage(packet.Nickname, packet.Message); + } + + _dataWriter.Reset(); + SendDataToAll(_dataWriter, ref packet, DeliveryMethod.ReliableUnordered, peer); + } + public int PopNetId() { int netId = _currentNetId; @@ -198,6 +213,7 @@ public void SetupGameVariables(CoopPlayer coopPlayer) { coopHandler = CoopHandler.CoopHandlerParent.GetComponent(); MyPlayer = coopPlayer; + fikaChat = gameObject.AddComponent(); } private void OnSendCharacterPacketReceived(SendCharacterPacket packet, NetPeer peer) @@ -348,7 +364,7 @@ private void OnExfiltrationPacketReceived(ExfiltrationPacket packet, NetPeer pee } else { - serverLogger.LogError($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); + logger.LogError($"ExfiltrationPacketPacketReceived: ExfiltrationController was null"); } } } @@ -476,14 +492,14 @@ private void OnAllCharacterRequestPacketReceived(AllCharacterRequestPacket packe if (!Players.ContainsKey(packet.NetId) && !PlayersMissing.Contains(packet.ProfileId) && !coopHandler.ExtractedPlayers.Contains(packet.NetId)) { PlayersMissing.Add(packet.ProfileId); - serverLogger.LogInfo($"Requesting missing player from server."); + logger.LogInfo($"Requesting missing player from server."); AllCharacterRequestPacket requestPacket = new(MyPlayer.ProfileId); _dataWriter.Reset(); SendDataToPeer(peer, _dataWriter, ref requestPacket, DeliveryMethod.ReliableOrdered); } if (!packet.IsRequest && PlayersMissing.Contains(packet.ProfileId)) { - serverLogger.LogInfo($"Received CharacterRequest from client: ProfileID: {packet.PlayerInfo.Profile.ProfileId}, Nickname: {packet.PlayerInfo.Profile.Nickname}"); + logger.LogInfo($"Received CharacterRequest from client: ProfileID: {packet.PlayerInfo.Profile.ProfileId}, Nickname: {packet.PlayerInfo.Profile.Nickname}"); if (packet.ProfileId != MyPlayer.ProfileId) { coopHandler.QueueProfile(packet.PlayerInfo.Profile, new Vector3(packet.Position.x, packet.Position.y + 0.5f, packet.Position.y), packet.NetId, packet.IsAlive); @@ -630,7 +646,7 @@ private void OnGameTimerPacketReceived(GameTimerPacket packet, NetPeer peer) } else { - serverLogger.LogError("OnGameTimerPacketReceived: Game was null!"); + logger.LogError("OnGameTimerPacketReceived: Game was null!"); } } @@ -652,9 +668,13 @@ protected void Update() protected void OnDestroy() { - NetDebug.Logger = null; _netServer?.Stop(); + if (fikaChat != null) + { + Destroy(fikaChat); + } + FikaEventDispatcher.DispatchEvent(new FikaServerDestroyedEvent(this)); } @@ -685,21 +705,21 @@ public void SendDataToPeer(NetPeer peer, NetDataWriter writer, ref T packet, public void OnPeerConnected(NetPeer peer) { NotificationManagerClass.DisplayMessageNotification($"Peer connected to server on port {peer.Port}.", iconType: EFT.Communications.ENotificationIconType.Friend); - serverLogger.LogInfo($"Connection established with {peer.Address}:{peer.Port}, id: {peer.Id}."); + logger.LogInfo($"Connection established with {peer.Address}:{peer.Port}, id: {peer.Id}."); hasHadPeer = true; } public void OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) { - serverLogger.LogError("[SERVER] error " + socketErrorCode); + logger.LogError("[SERVER] error " + socketErrorCode); } public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) { if (messageType == UnconnectedMessageType.Broadcast) { - serverLogger.LogInfo("[SERVER] Received discovery request. Send discovery response"); + logger.LogInfo("[SERVER] Received discovery request. Send discovery response"); NetDataWriter resp = new(); resp.Put(1); _netServer.SendUnconnectedMessage(resp, remoteEndPoint); @@ -713,16 +733,16 @@ public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketRead NetDataWriter resp = new(); resp.Put(data); _netServer.SendUnconnectedMessage(resp, remoteEndPoint); - serverLogger.LogInfo("PingingRequest: Correct ping query, sending response"); + logger.LogInfo("PingingRequest: Correct ping query, sending response"); } else { - serverLogger.LogError("PingingRequest: Data was not as expected"); + logger.LogError("PingingRequest: Data was not as expected"); } } else { - serverLogger.LogError("PingingRequest: Could not parse string"); + logger.LogError("PingingRequest: Could not parse string"); } } } @@ -738,7 +758,7 @@ public void OnConnectionRequest(ConnectionRequest request) public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { - serverLogger.LogInfo("Peer disconnected " + peer.Port + ", info: " + disconnectInfo.Reason); + logger.LogInfo("Peer disconnected " + peer.Port + ", info: " + disconnectInfo.Reason); NotificationManagerClass.DisplayMessageNotification("Peer disconnected " + peer.Port + ", info: " + disconnectInfo.Reason, iconType: EFT.Communications.ENotificationIconType.Alert); if (_netServer.ConnectedPeersCount == 0) { diff --git a/Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs b/Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs new file mode 100644 index 00000000..5eeb787d --- /dev/null +++ b/Fika.Core/Networking/Packets/Communication/TextMessagePacket.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +namespace Fika.Core.Networking.Packets.Communication +{ + public struct TextMessagePacket(string nickname, string message) : INetSerializable + { + public string Nickname = nickname; + public string Message = message; + + public void Deserialize(NetDataReader reader) + { + Nickname = reader.GetString(); + Message = reader.GetString(); + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(Nickname); + writer.Put(Message); + } + } +}