//structure should be //uno: guid //dos: 0/1 (to distinguish string or int array) //tres: actual data (actual int or string array) public override SerializationResult <byte[]> SerializeData(object objData, NetClient client) { if (!(objData is SyncableStringArray)) { return(new SerializationResult <byte[]>(false)); } SyncableStringArray data = (SyncableStringArray)objData; byte[] guidData = data.ID.ToByteArray(); byte isIndexesData = (byte)(data.SendingIndexes ? 1 : 0); //todo: possibly rework this to use shorts instead of ints in packets, would be smaller byte[] itemData; if (data.SendingIndexes) { //encoding indexes itemData = new byte[data.Indexes.Length * sizeof(int)]; for (int i = 0; i < data.Indexes.Length; i++) { Array.Copy(BitConverter.GetBytes(data.Indexes[i]), 0, itemData, i * sizeof(int), sizeof(int)); } } else { //encoding strings + necessary indexes int fullLength = 0; byte[][] strData = new byte[data.Items.Length][]; for (int i = 0; i < strData.Length; i++) { strData[i] = Encoding.Unicode.GetBytes(data.Items[i].String); fullLength += strData[i].Length; } int currentPosition = 0; itemData = new byte[fullLength + (strData.Length * (sizeof(int) + sizeof(byte)))]; for (int i = 0; i < strData.Length; i++) { itemData[currentPosition] = (byte)(data.Items[i].Necessary ? 1 : 0); Array.Copy(BitConverter.GetBytes(strData[i].Length), 0, itemData, currentPosition + 1, sizeof(int)); Array.Copy(strData[i], 0, itemData, currentPosition + sizeof(int) + 1, strData[i].Length); currentPosition += sizeof(int) + strData[i].Length + 1; } } byte[] fullData = new byte[guidData.Length + sizeof(byte) + itemData.Length]; Array.Copy(guidData, 0, fullData, 0, guidData.Length); fullData[guidData.Length] = isIndexesData; Array.Copy(itemData, 0, fullData, guidData.Length + sizeof(byte), itemData.Length); return(new SerializationResult <byte[]>(true, fullData)); }
public override SerializationResult <object> DeserializeData(byte[] data, NetClient client) { if (data[16] > 1 || data.Length < 17 || (data[16] == 1 && (data.Length - 17) % sizeof(int) != 0)) { return(new SerializationResult <object>(false)); } SyncableStringArray ret = new SyncableStringArray(); byte[] guidData = new byte[16]; Array.Copy(data, 0, guidData, 0, guidData.Length); ret.ID = new Guid(guidData); ret.SendingIndexes = data[guidData.Length] == 1 ? true : false; if (ret.SendingIndexes) { ret.Indexes = new int[(data.Length - 17) / sizeof(int)]; for (int i = 0; i < ret.Indexes.Length; i++) { ret.Indexes[i] = BitConverter.ToInt32(data, (i * sizeof(int)) + 17); } } else { byte[] strData = new byte[data.Length - 17]; Array.Copy(data, 17, strData, 0, strData.Length); var strings = new List <SyncableStringArray.OptionalString>(); int i = 0; while (i < strData.Length) { bool necessary = strData[i] == 1 ? true : false; int strLen = BitConverter.ToInt32(strData, i + 1); byte[] strBuf = new byte[strLen]; Array.Copy(strData, i + sizeof(int) + 1, strBuf, 0, strBuf.Length); strings.Add(new SyncableStringArray.OptionalString(Encoding.Unicode.GetString(strBuf), necessary)); i += strLen + sizeof(int) + 1; } ret.Items = strings.ToArray(); } return(new SerializationResult <object>(true, ret)); }
public static NetManager CreateClient() { NetManager createdManager = new NetManager(); createdManager.Udp = new UdpClient(); createdManager.Side = Side.Client; //this could be done with a custom channel for serialization and validation and shit but what's even the point lol createdManager.Channels[UDP_HANDSHAKE_CHANNEL_INDEX].OnRecieveRaw += (byte[] data, NetClient sender) => { if (data.Length != sizeof(byte) + 16) { return; } UdpHandshakeUpdateEvent updateEvent; if (data[0] == 0) //not completed { byte[] guidData = new byte[16]; Array.Copy(data, 1, guidData, 0, guidData.Length); updateEvent = new UdpHandshakeUpdateEvent { Complete = false, GuidData = guidData }; } else { updateEvent = new UdpHandshakeUpdateEvent { Complete = true } }; ManagerEvent handshakeEvent = new ManagerEvent(ManagerEventType.UdpHandshakeUpdate, updateEvent); createdManager.EventQueue.Enqueue(handshakeEvent); }; createdManager.Channels[DISCONNECT_CHANNEL_INDEX].OnRecieveRaw += (byte[] data, NetClient sender) => createdManager.Disconnect(Encoding.Unicode.GetString(data)); createdManager.Channels[CHANNEL_INDEX_SYNC_CHANNEL_INDEX].OnRecieveSerialized += (object objData, NetClient sender) => { SyncableStringArray data = (SyncableStringArray)objData; if (data.ID != createdManager.ChannelSyncGuid) { return; } if (data.SendingIndexes) { for (int i = 0; i < createdManager.Channels.Count; i++) { createdManager.Channels[i].ID = (ushort)data.Indexes[i]; } createdManager.IncrementInitialisationState(); } else { foreach (SyncableStringArray.OptionalString channel in data.Items) { createdManager.CreateChannel(channel.String); } } }; return(createdManager); }
public NetEvent[] PollEvents() { List <NetEvent> events = new List <NetEvent>(); #region Client Udp Handshake if (_ConnectionState == ConnectionState.Initialising && Side == Side.Client && UdpHandshakeGuid != null && !IsConnectedToLocalServer) { SendRaw(SendMode.Udp, UdpHandshakeGuid, Channels[UDP_HANDSHAKE_CHANNEL_INDEX], Server); } #endregion #region Client Heartbeat Sending if (_HeartbeatSendRate >= 0 && Side == Side.Client && DateTime.Now.Subtract(TimeSpan.FromMilliseconds(_HeartbeatSendRate)) > HeartbeatLastSend) { HeartbeatLastSend = DateTime.Now; SendRaw(SendMode.Udp, new byte[0], Channels[HEARTBEAT_CHANNEL_INDEX], Server); } #endregion #region Server Heartbeat Checking if (Side == Side.Server && _ClientTimeoutTime >= 0) { DateTime lastValidTime = DateTime.Now.Subtract(TimeSpan.FromMilliseconds(_ClientTimeoutTime)); foreach (NetClient client in _Clients) { if (lastValidTime > client.LastHeartbeat) { DisconnectClient(client, "Client timed out"); } } } #endregion while (EventQueue.TryDequeue(out ManagerEvent managerEvent)) { switch (managerEvent.Type) { #region Client-only Events case ManagerEventType.TcpConnectionComplete: { var connectionEvent = (TcpConnectionCompleteEvent)managerEvent.Data; if (connectionEvent.Type == ConnectionResult.Success) { ConnectionState = ConnectionState.Initialising; //create channel index sync packet var channels = new SyncableStringArray.OptionalString[Channels.Count]; for (int i = 0; i < Channels.Count; i++) { channels[i] = new SyncableStringArray.OptionalString(Channels[i].StringID, Channels[i].Necessary); } ChannelSyncGuid = Guid.NewGuid(); PollingNet = true; Task.Run(ClientTcpListen); Task.Run(ClientUdpListen); //send SyncableStringArray syncArray = new SyncableStringArray(ChannelSyncGuid, channels); Channels[CHANNEL_INDEX_SYNC_CHANNEL_INDEX].SendSerialized(SendMode.Tcp, syncArray, Server); } else { ConnectionCompleteEvent failureEvent = new ConnectionCompleteEvent { Success = false, SocketErrorCode = 10060 //WSAETIMEDOUT }; ConnectionState = ConnectionState.Unconnected; if (connectionEvent.Exception != null) { failureEvent.SocketErrorCode = connectionEvent.Exception.ErrorCode; } events.Add(failureEvent); CleanupAfterDisconnect(); } break; } case ManagerEventType.UdpHandshakeUpdate: { if (ConnectionState != ConnectionState.Initialising) { continue; } var handshakeEvent = (UdpHandshakeUpdateEvent)managerEvent.Data; if (handshakeEvent.Complete) { //udp handshake successful, increment connection state IncrementInitialisationState(); break; } //got the guid if (UdpHandshakeGuid == null) { UdpHandshakeGuid = handshakeEvent.GuidData; } break; } case ManagerEventType.ConnectionComplete: { ConnectionState = ConnectionState.Connected; ConnectionCompleteEvent successEvent = new ConnectionCompleteEvent { Success = true }; events.Add(successEvent); break; } case ManagerEventType.DisconnectedSelf: { events.Add(new DisconnectedSelfEvent()); break; } #endregion #region Server-only Events case ManagerEventType.RecievedTcpConnection: { TcpClient newTcp = (TcpClient)managerEvent.Data; //create udp handshake guid Guid handshakeGuid = Guid.NewGuid(); NetClient newClient = new NetClient { Tcp = newTcp, UdpHandshakeGuid = handshakeGuid, Manager = this }; _Clients.Add(newClient); byte[] handshakeGuidPacket = new byte[17]; // 1 + sizeof(Guid) handshakeGuidPacket[0] = 0; Array.Copy(handshakeGuid.ToByteArray(), 0, handshakeGuidPacket, 1, 16); SendRaw(SendMode.Tcp, handshakeGuidPacket, Channels[UDP_HANDSHAKE_CHANNEL_INDEX], newClient); Task.Run(() => ServerTcpListenToClient(newClient)); //start listening to client break; } case ManagerEventType.RecievedLocalConnection: { NetManager manager = (NetManager)managerEvent.Data; NetClient newClient = new NetClient { IsLocal = true, HasCompletedUdpHandshake = true, LocalManager = manager, InitialisationCount = 1, Manager = this }; _Clients.Add(newClient); manager.LocalServersideClient = newClient; break; } case ManagerEventType.RecievedUdpHandshakeAttempt: { var handshakeAttempt = (UdpHandshakeAttemptEvent)managerEvent.Data; foreach (NetClient client in Clients) { if (!client.HasCompletedUdpHandshake && handshakeAttempt.Guid == client.UdpHandshakeGuid) { client.HasCompletedUdpHandshake = true; client.EP = handshakeAttempt.SenderEP; IncrementClientInitialisationCount(client); //create success packet byte[] successPacket = new byte[17]; //gotta pad to 17 or the client doesn't accept it successPacket[0] = 1; Array.Copy(new Guid().ToByteArray(), 0, successPacket, 1, 16); //notify client of success SendRaw(SendMode.Tcp, successPacket, Channels[UDP_HANDSHAKE_CHANNEL_INDEX], client); break; } } break; } case ManagerEventType.ClientConnectionComplete: { var connectedClient = (NetClient)managerEvent.Data; ClientConnectedEvent connectedEvent = new ClientConnectedEvent { ConnectedClient = connectedClient }; events.Add(connectedEvent); break; } case ManagerEventType.ClientDisconnected: { var disconnectEvent = (TcpClientDisconnectEvent)managerEvent.Data; ClientDisconnectedEvent clientDisconnectedEvent = new ClientDisconnectedEvent { DisconnectedClient = disconnectEvent.Client, DisconnectReason = disconnectEvent.Reason }; events.Add(clientDisconnectedEvent); break; } #endregion #region Shared Events case ManagerEventType.RecievedData: { Packet packet = (Packet)managerEvent.Data; byte[] data = packet.Data; ushort channelID = BitConverter.ToUInt16(data, 0); //todo: depending on how the order of channels lines up i might be able to do a direct lookup here NetChannel channel = null; for (int i = (packet.SendMode == SendMode.Tcp ? TCP_CHANNEL_SKIP_AMOUNT : UDP_CHANNEL_SKIP_AMOUNT); i < Channels.Count; i++) //skip any reserved channel indexes { if (Channels[i].ID == channelID) { channel = Channels[i]; break; } } if (channel == null) { break; } byte[] headerless = new byte[data.Length - sizeof(ushort)]; Array.Copy(data, 2, headerless, 0, headerless.Length); NetClient client = null; if (Side == Side.Client) { //only possible sender is the server if we're a client client = Server; } else { if (packet.SendMode == SendMode.Tcp) { client = packet.Client; } else { foreach (NetClient possibleClient in Clients) { if (possibleClient.EP?.Equals(packet.EP) ?? false) //null check to prevent nullrefs { client = possibleClient; break; } } } } if (client != null) { channel.RecieveData(headerless, client); } break; } #endregion } } return(events.ToArray()); }
public static NetManager CreateServer(int port) { NetManager createdManager = new NetManager(); createdManager.Side = Side.Server; createdManager.ListenPort = port; createdManager.Udp = new UdpClient(port); IPEndPoint localEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), port); createdManager.TcpListener = new TcpListener(localEP); //this could be done with a custom channel for serialization and shit but what's even the point lol createdManager.Channels[DISCONNECT_CHANNEL_INDEX].OnRecieveRaw += (byte[] data, NetClient sender) => createdManager.DisconnectClient(sender, Encoding.Unicode.GetString(data)); createdManager.Channels[HEARTBEAT_CHANNEL_INDEX].OnRecieveRaw += (byte[] data, NetClient sender) => { sender.LastHeartbeat = DateTime.Now; }; createdManager.Channels[CHANNEL_INDEX_SYNC_CHANNEL_INDEX].OnRecieveSerialized += (object objData, NetClient sender) => { SyncableStringArray data = (SyncableStringArray)objData; if (data.SendingIndexes) { return; } int[] indexes = new int[data.Items.Length]; for (int i = 0; i < data.Items.Length; i++) { NetChannel foundChannel = null; foreach (NetChannel channel in createdManager.Channels) { if (channel.StringID == data.Items[i].String) { foundChannel = channel; break; } } if (foundChannel == null) { if (data.Items[i].Necessary) { createdManager.DisconnectClient(sender, $"Server is missing client's required channel: {data.Items[i].String}"); return; } else { indexes[i] = UNKNOWN_CHANNEL_CHANNEL_INDEX; continue; } } //put index of channel into index array indexes[i] = foundChannel.ID; } List <NetChannel> serverChannelsNotOnClient = new List <NetChannel>(); foreach (NetChannel channel in createdManager.Channels) { if (Array.IndexOf(indexes, channel.ID) == -1) { if (channel.Necessary) { createdManager.DisconnectClient(sender, $"Client is missing server's required channel: {channel.StringID}"); return; } else { serverChannelsNotOnClient.Add(channel); } } } if (serverChannelsNotOnClient.Count != 0) { //create packet containing new channels var channelsNotOnClientNames = new SyncableStringArray.OptionalString[serverChannelsNotOnClient.Count]; for (int i = 0; i < serverChannelsNotOnClient.Count; i++) { channelsNotOnClientNames[i] = new SyncableStringArray.OptionalString(serverChannelsNotOnClient[i].StringID, true); } SyncableStringArray missingChannels = new SyncableStringArray(data.ID, channelsNotOnClientNames); createdManager.Channels[CHANNEL_INDEX_SYNC_CHANNEL_INDEX].SendSerialized(SendMode.Tcp, missingChannels, sender); int oldLength = indexes.Length; Array.Resize(ref indexes, indexes.Length + serverChannelsNotOnClient.Count); for (int i = oldLength; i < indexes.Length; i++) { indexes[i] = serverChannelsNotOnClient[i - oldLength].ID; } } var indexPacket = new SyncableStringArray(data.ID, indexes); createdManager.Channels[CHANNEL_INDEX_SYNC_CHANNEL_INDEX].SendSerialized(SendMode.Tcp, indexPacket, sender); createdManager.IncrementClientInitialisationCount(sender); }; return(createdManager); }