diff --git a/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsNetworking.cs b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsNetworking.cs new file mode 100644 index 000000000..9bf1fc251 --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/LuaCs/LuaCsNetworking.cs @@ -0,0 +1,137 @@ +using Barotrauma.Networking; +using System.Collections.Generic; + +namespace Barotrauma +{ + partial class LuaCsNetworking + { + private Dictionary> receiveQueue = new Dictionary>(); + + public void SendSyncMessage() + { + WriteOnlyMessage message = new WriteOnlyMessage(); + message.WriteByte((byte)ClientPacketHeader.LUA_NET_MESSAGE); + message.WriteByte((byte)LuaCsClientToServer.RequestAllIds); + GameMain.Client.ClientPeer.Send(message, DeliveryMethod.ReliableOrdered); + } + + public void NetMessageReceived(IReadMessage netMessage, ServerPacketHeader header, Client client = null) + { + if (header != ServerPacketHeader.LUA_NET_MESSAGE) + { + GameMain.LuaCs.Hook.Call("netMessageReceived", netMessage, header, client); + return; + } + + LuaCsServerToClient luaCsHeader = (LuaCsServerToClient)netMessage.ReadByte(); + + switch (luaCsHeader) + { + case LuaCsServerToClient.NetMessageString: + HandleNetMessageString(netMessage); + break; + + case LuaCsServerToClient.NetMessageId: + HandleNetMessageId(netMessage); + break; + + case LuaCsServerToClient.ReceiveIds: + ReadIds(netMessage); + break; + } + } + + public IWriteMessage Start(string netMessageName) + { + var message = new WriteOnlyMessage(); + + message.WriteByte((byte)ClientPacketHeader.LUA_NET_MESSAGE); + + if (stringToId.ContainsKey(netMessageName)) + { + message.WriteByte((byte)LuaCsClientToServer.NetMessageId); + message.WriteUInt16(stringToId[netMessageName]); + } + else + { + message.WriteByte((byte)LuaCsClientToServer.NetMessageString); + message.WriteString(netMessageName); + } + + return message; + } + + public void Receive(string netMessageName, LuaCsAction callback) + { + RequestId(netMessageName); + + netReceives[netMessageName] = callback; + } + + public void RequestId(string netMessageName) + { + if (stringToId.ContainsKey(netMessageName)) { return; } + + WriteOnlyMessage message = new WriteOnlyMessage(); + message.WriteByte((byte)ClientPacketHeader.LUA_NET_MESSAGE); + message.WriteByte((byte)LuaCsClientToServer.RequestSingleId); + + message.WriteString(netMessageName); + + Send(message, DeliveryMethod.ReliableOrdered); + } + + public void Send(IWriteMessage netMessage, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable) + { + GameMain.Client.ClientPeer.Send(netMessage, deliveryMethod); + } + + private void HandleNetMessageId(IReadMessage netMessage, Client client = null) + { + ushort id = netMessage.ReadUInt16(); + + if (idToString.ContainsKey(id)) + { + string name = idToString[id]; + + HandleNetMessage(netMessage, name, client); + } + else + { + if (!receiveQueue.ContainsKey(id)) { receiveQueue[id] = new Queue(); } + receiveQueue[id].Enqueue(netMessage); +#if DEBUG + LuaCsLogger.LogMessage($"Received NetMessage with unknown id {id} from server, storing in queue in case we receive the id later."); +#endif + } + } + + private void ReadIds(IReadMessage netMessage) + { + ushort size = netMessage.ReadUInt16(); + + for (int i = 0; i < size; i++) + { + ushort id = netMessage.ReadUInt16(); + string name = netMessage.ReadString(); + + idToString[id] = name; + stringToId[name] = id; + + if (!receiveQueue.ContainsKey(id)) + { + continue; + } + + while (receiveQueue[id].TryDequeue(out var queueMessage)) + { + if (netReceives.ContainsKey(name)) + { + netReceives[name](netMessage, null); + } + } + } + } + } + +} diff --git a/Barotrauma/BarotraumaServer/ServerSource/LuaCs/LuaCsNetworking.cs b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/LuaCsNetworking.cs new file mode 100644 index 000000000..be3a12cc6 --- /dev/null +++ b/Barotrauma/BarotraumaServer/ServerSource/LuaCs/LuaCsNetworking.cs @@ -0,0 +1,196 @@ +using Barotrauma.Networking; +using System.Collections.Generic; +using System.Linq; + +namespace Barotrauma +{ + partial class LuaCsNetworking + { + private const int MaxRegisterPerClient = 1000; + + private Dictionary clientRegisterCount = new Dictionary(); + + private ushort currentId = 0; + + public void NetMessageReceived(IReadMessage netMessage, ClientPacketHeader header, Client client = null) + { + if (header != ClientPacketHeader.LUA_NET_MESSAGE) + { + GameMain.LuaCs.Hook.Call("netMessageReceived", netMessage, header, client); + return; + } + + LuaCsClientToServer luaCsHeader = (LuaCsClientToServer)netMessage.ReadByte(); + + switch (luaCsHeader) + { + case LuaCsClientToServer.NetMessageString: + HandleNetMessageString(netMessage, client); + break; + + case LuaCsClientToServer.NetMessageId: + HandleNetMessageId(netMessage, client); + break; + + case LuaCsClientToServer.RequestAllIds: + WriteAllIds(client); + break; + + case LuaCsClientToServer.RequestSingleId: + RequestIdSingle(netMessage, client); + break; + } + } + + private void HandleNetMessageId(IReadMessage netMessage, Client client = null) + { + ushort id = netMessage.ReadUInt16(); + + if (idToString.ContainsKey(id)) + { + string name = idToString[id]; + + HandleNetMessage(netMessage, name, client); + } + else + { + LuaCsLogger.LogError($"Received NetMessage for unknown id {id} from {GameServer.ClientLogName(client)}."); + } + } + + public IWriteMessage Start(string netMessageName) + { + var message = new WriteOnlyMessage(); + + message.WriteByte((byte)ServerPacketHeader.LUA_NET_MESSAGE); + + if (stringToId.ContainsKey(netMessageName)) + { + message.WriteByte((byte)LuaCsServerToClient.NetMessageId); + message.WriteUInt16(stringToId[netMessageName]); + } + else + { + message.WriteByte((byte)LuaCsServerToClient.NetMessageString); + message.WriteString(netMessageName); + } + + return message; + } + + public void Receive(string netMessageName, LuaCsAction callback) + { + RegisterId(netMessageName); + + netReceives[netMessageName] = callback; + } + + public ushort RegisterId(string name) + { + if (stringToId.ContainsKey(name)) + { + return stringToId[name]; + } + + if (currentId >= ushort.MaxValue) + { + LuaCsLogger.LogError($"Tried to register more than {ushort.MaxValue} network ids!"); + return 0; + } + + currentId++; + + idToString[currentId] = name; + stringToId[name] = currentId; + + WriteIdToAll(currentId, name); + + return currentId; + } + + private void RequestIdSingle(IReadMessage netMessage, Client client) + { + string name = netMessage.ReadString(); + + if (!stringToId.ContainsKey(name) && client.AccountId.TryUnwrap(out AccountId id)) + { + if (!clientRegisterCount.ContainsKey(id.StringRepresentation)) + { + clientRegisterCount[id.StringRepresentation] = 0; + } + + clientRegisterCount[id.StringRepresentation]++; + + if (clientRegisterCount[id.StringRepresentation] > MaxRegisterPerClient) + { + LuaCsLogger.Log($"{GameServer.ClientLogName(client)} Tried to register more than {MaxRegisterPerClient} Ids!"); + return; + } + } + + RegisterId(name); + } + + private void WriteIdToAll(ushort id, string name) + { + WriteOnlyMessage message = new WriteOnlyMessage(); + message.WriteByte((byte)ServerPacketHeader.LUA_NET_MESSAGE); + message.WriteByte((byte)LuaCsServerToClient.ReceiveIds); + + message.WriteUInt16(1); + message.WriteUInt16(id); + message.WriteString(name); + + Send(message, null, DeliveryMethod.ReliableOrdered); + } + + private void WriteAllIds(Client client) + { + WriteOnlyMessage message = new WriteOnlyMessage(); + message.WriteByte((byte)ServerPacketHeader.LUA_NET_MESSAGE); + message.WriteByte((byte)LuaCsServerToClient.ReceiveIds); + + message.WriteUInt16((ushort)idToString.Count()); + foreach ((ushort id, string name) in idToString) + { + message.WriteUInt16(id); + message.WriteString(name); + } + + Send(message, client.Connection, DeliveryMethod.ReliableOrdered); + } + + public void ClientWriteLobby(Client client) => GameMain.Server.ClientWriteLobby(client); + + public void Send(IWriteMessage netMessage, NetworkConnection connection = null, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable) + { + if (connection == null) + { + foreach (NetworkConnection conn in Client.ClientList.Select(c => c.Connection)) + { + GameMain.Server.ServerPeer.Send(netMessage, conn, deliveryMethod); + } + } + else + { + GameMain.Server.ServerPeer.Send(netMessage, connection, deliveryMethod); + } + } + + public void UpdateClientPermissions(Client client) + { + GameMain.Server.UpdateClientPermissions(client); + } + + public void RemovePendingClient(ServerPeer.PendingClient pendingClient, PeerDisconnectPacket peerDisconnectPacket) + { + GameMain.Server.ServerPeer.RemovePendingClient(pendingClient, peerDisconnectPacket); + } + + public int FileSenderMaxPacketsPerUpdate + { + get { return FileSender.FileTransferOut.MaxPacketsPerUpdate; } + set { FileSender.FileTransferOut.MaxPacketsPerUpdate = value; } + } + } +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsNetworking.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsNetworking.cs index 0796f511c..08e02845c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsNetworking.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsNetworking.cs @@ -1,153 +1,39 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.IO; -using System.Linq; using System.Net; -using System.Text; -using System.Threading.Tasks; using Barotrauma.Networking; -using MoonSharp.Interpreter; namespace Barotrauma { partial class LuaCsNetworking { - public class HttpListener + private enum LuaCsClientToServer { - private System.Net.HttpListener listener; - - public HttpListener(System.Net.HttpListener list) - { - listener = list; - } - - public void Close() - { - listener.Close(); - } + NetMessageId, + NetMessageString, + RequestSingleId, + RequestAllIds, } - public class IncomingHttpRequest + private enum LuaCsServerToClient { - private HttpListenerRequest request; - - public IncomingHttpRequest(HttpListenerRequest req) - { - request = req; - } - - public string ContentType => request.ContentType; - public string LocalEndPoint => request.LocalEndPoint.ToString(); - public string RemoteEndPoint => request.RemoteEndPoint.ToString(); - public string RawUrl => request.RawUrl; - public Uri Url => request.Url; - public string UserAgent => request.UserAgent; - public string UserHostName => request.UserHostName; - public string UserHostAddress => request.UserHostAddress; - public NameValueCollection Headers => request.Headers; - public string HttpMethod => request.HttpMethod; + NetMessageId, + NetMessageString, + ReceiveIds } - public class IncomingHttpResponse - { - private HttpListenerResponse response; - - public IncomingHttpResponse(HttpListenerResponse resp) - { - response = resp; - - response.ContentType = "text/html"; - } - - public string ContentType - { - get { return response.ContentType; } - set { response.ContentType = value; } - } - - public void Write(string text) - { - byte[] data = Encoding.UTF8.GetBytes(text); - response.ContentEncoding = Encoding.UTF8; - response.ContentLength64 = data.LongLength; - - response.OutputStream.Write(data, 0, data.Length); - } - } - - public bool RestrictMessageSize = true; - public Dictionary LuaCsNetReceives = new Dictionary(); -#if SERVER - [MoonSharpHidden] - public void NetMessageReceived(IReadMessage netMessage, ClientPacketHeader header, Client client = null) - { - if (header == ClientPacketHeader.LUA_NET_MESSAGE) - { - string netMessageName = netMessage.ReadString(); - if (LuaCsNetReceives.ContainsKey(netMessageName)) - { - try - { - LuaCsNetReceives[netMessageName](netMessage, client); - } - catch (Exception e) - { - // TODO: make LuaCsNetworking hold a reference to LuaCsSetup instead of using this global - LuaCsLogger.LogError($"Exception thrown inside NetMessageReceive({netMessageName})", LuaCsMessageOrigin.Unknown); - LuaCsLogger.HandleException(e, LuaCsMessageOrigin.Unknown); - } - } - } - else - { - GameMain.LuaCs.Hook.Call("netMessageReceived", netMessage, header, client); - } - } + private Dictionary netReceives = new Dictionary(); + private Dictionary idToString = new Dictionary(); + private Dictionary stringToId = new Dictionary(); -#else - [MoonSharpHidden] - public void NetMessageReceived(IReadMessage netMessage, ServerPacketHeader header, Client client = null) + public void Initialize() { - if (header == ServerPacketHeader.LUA_NET_MESSAGE) - { - string netMessageName = netMessage.ReadString(); - if (LuaCsNetReceives.ContainsKey(netMessageName)) - { - try - { - LuaCsNetReceives[netMessageName](netMessage, client); - } - catch (Exception e) - { - LuaCsLogger.LogError($"Exception thrown inside NetMessageReceive({netMessageName})", LuaCsMessageOrigin.Unknown); - LuaCsLogger.HandleException(e, LuaCsMessageOrigin.Unknown); - } - } - } - else - { - GameMain.LuaCs.Hook.Call("netMessageReceived", netMessage, header, client); - } - } +#if CLIENT + SendSyncMessage(); #endif - public void Receive(string netMessageName, LuaCsAction callback) - { - LuaCsNetReceives[netMessageName] = callback; - } - - public IWriteMessage Start(string netMessageName) - { - var message = new WriteOnlyMessage(); -#if SERVER - message.WriteByte((byte)ServerPacketHeader.LUA_NET_MESSAGE); -#else - message.WriteByte((byte)ClientPacketHeader.LUA_NET_MESSAGE); -#endif - message.WriteString(netMessageName); - return message; } public IWriteMessage Start() @@ -155,29 +41,50 @@ namespace Barotrauma return new WriteOnlyMessage(); } -#if SERVER - public void ClientWriteLobby(Client client) => GameMain.Server.ClientWriteLobby(client); - - public void Send(IWriteMessage netMessage, NetworkConnection connection = null, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable) + public string IdToString(ushort id) { - if (connection == null) + if (idToString.ContainsKey(id)) { return idToString[id]; } + + return null; + } + + public ushort StringToId(string name) + { + if (stringToId.ContainsKey(name)) { return stringToId[name]; } + + return 0; + } + + private void HandleNetMessage(IReadMessage netMessage, string name, Client client = null) + { + if (netReceives.ContainsKey(name)) { - foreach (NetworkConnection conn in Client.ClientList.Select(c => c.Connection)) + try { - GameMain.Server.ServerPeer.Send(netMessage, conn, deliveryMethod); + netReceives[name](netMessage, client); + } + catch (Exception e) + { + LuaCsLogger.LogError($"Exception thrown inside NetMessageReceive({name})", LuaCsMessageOrigin.CSharpMod); + LuaCsLogger.HandleException(e, LuaCsMessageOrigin.CSharpMod); } } else { - GameMain.Server.ServerPeer.Send(netMessage, connection, deliveryMethod); +#if SERVER + LuaCsLogger.LogError($"Received NetMessage for unknown name {name} from {GameServer.ClientLogName(client)}."); +#else + LuaCsLogger.LogError($"Received NetMessage for unknown name {name} from server."); +#endif } } -#else - public void Send(IWriteMessage netMessage, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable) + + private void HandleNetMessageString(IReadMessage netMessage, Client client = null) { - GameMain.Client.ClientPeer.Send(netMessage, deliveryMethod); + string name = netMessage.ReadString(); + + HandleNetMessage(netMessage, name, client); } -#endif public void HttpRequest(string url, LuaCsAction callback, string data = null, string method = "POST", string contentType = "application/json") { @@ -228,65 +135,11 @@ namespace Barotrauma HttpRequest(url, callback, null, "GET"); } - public static async void HandleIncomingConnections(System.Net.HttpListener listener, LuaCsAction onRequestReceived) - { - try - { - while (listener.IsListening) - { - HttpListenerContext ctx = await listener.GetContextAsync(); - - IncomingHttpRequest req = new IncomingHttpRequest(ctx.Request); - IncomingHttpResponse resp = new IncomingHttpResponse(ctx.Response); - - onRequestReceived(req, resp); - - ctx.Response.Close(); - } - - } - catch (Exception) - { - - } - } - - - public LuaCsNetworking.HttpListener StartHttpServer(string address, LuaCsAction onRequestReceived) - { - var listener = new System.Net.HttpListener(); - listener.Prefixes.Add(address); - listener.Start(); - - HandleIncomingConnections(listener, onRequestReceived); - - return new LuaCsNetworking.HttpListener(listener); - } - public void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData) { GameMain.NetworkMember.CreateEntityEvent(entity, extraData); } -#if SERVER - public void UpdateClientPermissions(Client client) - { - GameMain.Server.UpdateClientPermissions(client); - } - - public void RemovePendingClient(ServerPeer.PendingClient pendingClient, PeerDisconnectPacket peerDisconnectPacket) - { - GameMain.Server.ServerPeer.RemovePendingClient(pendingClient, peerDisconnectPacket); - } - - public int FileSenderMaxPacketsPerUpdate - { - get { return FileSender.FileTransferOut.MaxPacketsPerUpdate; } - set { FileSender.FileTransferOut.MaxPacketsPerUpdate = value; } - } -#endif - - public ushort LastClientListUpdateID { get { return GameMain.NetworkMember.LastClientListUpdateID; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs index 0293af2b1..69d95c71c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs @@ -282,6 +282,7 @@ namespace Barotrauma PerformanceCounter = new LuaCsPerformanceCounter(); Hook.Initialize(); ModStore.Initialize(); + Networking.Initialize(); UserData.RegisterType(); UserData.RegisterType();