예제 #1
0
        private Thread IgnoranceClientThread()
        {
            ThreadBootstrapStruct threadBootstrap = new ThreadBootstrapStruct
            {
                hostAddress       = clientConnectionAddress,
                port              = (ushort)CommunicationPort,
                maxChannels       = Channels.Length,
                maxPacketSize     = MaxPacketSizeInKb * 1024,
                threadPumpTimeout = EnetPollTimeout
            };

            Thread t = new Thread(() => ClientWorkerThread(threadBootstrap));

            return(t);
        }
예제 #2
0
        // Server thread.
        private Thread IgnoranceServerThread()
        {
            string bindAddress = string.Empty;

            ThreadBootstrapStruct startupInformation = new ThreadBootstrapStruct()
            {
                hostAddress       = bindAddress,
                port              = (ushort)CommunicationPort,
                maxPacketSize     = MaxPacketSizeInKb * 1024,
                maxPeers          = MaximumPeerCCU,
                maxChannels       = Channels.Length,
                threadPumpTimeout = EnetPollTimeout
            };

            Thread t = new Thread(() => ServerWorkerThread(startupInformation));

            return(t);
        }
예제 #3
0
        private Thread IgnoranceClientThread()
        {
            statistics = new PeerStatistics();

            ThreadBootstrapStruct threadBootstrap = new ThreadBootstrapStruct
            {
                hostAddress        = clientConnectionAddress,
                port               = (ushort)CommunicationPort,
                maxChannels        = Channels.Length,
                maxPacketSize      = MaxPacketSizeInKb * 1024,
                threadPumpTimeout  = EnetPollTimeout,
                pingUpdateInterval = StatisticsCalculationInterval,
            };

            Thread t = new Thread(() => ClientWorkerThread(threadBootstrap));

            return(t);
        }
예제 #4
0
        private static void ServerWorkerThread(ThreadBootstrapStruct startupInformation)
        {
            // Thread Safety: Initialize ENet in it's own thread.
            if (Library.Initialize())
            {
                Debug.Log("Ignorance has initialized ENet.");
            }
            else
            {
                Debug.LogError("Ignorance has failed to initialize ENet.");
                return;
            }

            // Connection ID.
            // TODO: Change the name of this.
            int nextConnectionId = 1;

            // Server address properties
            Address eAddress = new Address()
            {
                Port = startupInformation.port,
            };

            // Bind on everything or not?
            if (!string.IsNullOrEmpty(startupInformation.hostAddress))
            {
                eAddress.SetHost(startupInformation.hostAddress);
            }

            using (Host serverWorkerHost = new Host())
            {
                try
                {
                    serverWorkerHost.Create(eAddress, startupInformation.maxPeers, startupInformation.maxChannels, 0, 0);
                    ServerStarted = true;
                }
                catch (Exception e)
                {
                    Debug.LogError("Ignorance encountered a fatal exception. You might have found a bug. Please report this on the GitHub Issue tracker and provide details as to what happened.\n" +
                                   $"The exception returned was: {e}");
                    return;
                }

                Debug.Log($"Ignorance server thread ready for connections. Listening on UDP port {startupInformation.port}.\n" +
                          $"Capacity: {startupInformation.maxPeers} peers with {startupInformation.maxChannels} channels each. Max packet size: {startupInformation.maxPacketSize} bytes");

                // The meat and potatoes.
                while (!serverShouldCeaseOperation)
                {
                    // Outgoing stuff
                    while (MirrorServerOutgoingQueue.TryDequeue(out OutgoingPacket opkt))
                    {
                        switch (opkt.commandType)
                        {
                        case CommandPacketType.ServerWantsToDisconnectClient:
                            if (ConnectionIDToPeers.TryGetValue(opkt.connectionId, out Peer bootedPeer))
                            {
                                bootedPeer.DisconnectLater(0);
                            }
                            break;

                        case CommandPacketType.Nothing:
                        default:
                            if (ConnectionIDToPeers.TryGetValue(opkt.connectionId, out Peer target))
                            {
                                // Return code from the send function
                                int returnCode = target.Send(opkt.channelId, ref opkt.payload);
                                if (returnCode != 0)
                                {
                                    print($"Error code {returnCode} returned trying to send {opkt.payload.Length} bytes to Peer {target.ID} on channel {opkt.channelId}");
                                }
                            }
                            break;
                        }
                    }

                    // Get everything out the door.
                    serverWorkerHost.Flush();

                    // Incoming stuffs now.
                    bool hasBeenPolled = false;

                    while (!hasBeenPolled)
                    {
                        if (serverWorkerHost.CheckEvents(out Event netEvent) <= 0)
                        {
                            if (serverWorkerHost.Service(startupInformation.threadPumpTimeout, out netEvent) <= 0)
                            {
                                break;
                            }

                            hasBeenPolled = true;
                        }

                        switch (netEvent.Type)
                        {
                        case EventType.None:
                            break;

                        case EventType.Connect:
                            // Add to dictionaries.
                            if (!PeersToConnectionIDs.TryAdd(netEvent.Peer, nextConnectionId))
                            {
                                Debug.LogWarning($"It seems we already know this client in our Connection ID to Peer Mapping. Replacing.");
                                PeersToConnectionIDs[netEvent.Peer] = nextConnectionId;
                            }
                            if (!ConnectionIDToPeers.TryAdd(nextConnectionId, netEvent.Peer))
                            {
                                Debug.LogWarning($"It seems we already know this client in our Peer to ConnectionID Mapping. Replacing.");
                                ConnectionIDToPeers[nextConnectionId] = netEvent.Peer;
                            }

                            // Send a message back to mirror.
                            IncomingPacket newConnectionPkt = default;
                            newConnectionPkt.connectionId = nextConnectionId;
                            newConnectionPkt.type         = MirrorPacketType.ServerClientConnected;
                            newConnectionPkt.ipAddress    = netEvent.Peer.IP;

                            MirrorServerIncomingQueue.Enqueue(newConnectionPkt);
                            nextConnectionId++;
                            break;

                        case EventType.Disconnect:
                        case EventType.Timeout:
                            if (PeersToConnectionIDs.TryGetValue(netEvent.Peer, out int deadPeer))
                            {
                                IncomingPacket disconnectionPkt = default;
                                disconnectionPkt.connectionId = deadPeer;
                                disconnectionPkt.type         = MirrorPacketType.ServerClientDisconnected;
                                disconnectionPkt.ipAddress    = netEvent.Peer.IP;

                                MirrorServerIncomingQueue.Enqueue(disconnectionPkt);
                                ConnectionIDToPeers.TryRemove(deadPeer, out _);
                            }

                            PeersToConnectionIDs.TryRemove(netEvent.Peer, out _);
                            break;

                        case EventType.Receive:
                            int dataConnID = -1;

                            if (PeersToConnectionIDs.TryGetValue(netEvent.Peer, out dataConnID))
                            {
                                if (!netEvent.Packet.IsSet)
                                {
                                    Debug.LogWarning("Ignorance: A incoming packet is not set correctly - attempting to continue!");
                                    return;
                                }

                                if (netEvent.Packet.Length > startupInformation.maxPacketSize)
                                {
                                    Debug.LogWarning($"Ignorance: Packet too large for buffer; dropping. Packet {netEvent.Packet.Length} bytes > {startupInformation.maxPacketSize} byte limit");
                                    netEvent.Packet.Dispose();
                                    return;
                                }

                                // Copy to the packet cache.
                                try
                                {
                                    IncomingPacket dataPkt = default;
                                    dataPkt.connectionId = dataConnID;
                                    dataPkt.channelId    = netEvent.ChannelID;
                                    dataPkt.type         = MirrorPacketType.ServerClientSentData;
                                    dataPkt.ipAddress    = netEvent.Peer.IP;

                                    byte[] rentedBuffer = System.Buffers.ArrayPool <byte> .Shared.Rent(netEvent.Packet.Length);

                                    netEvent.Packet.CopyTo(rentedBuffer);

                                    dataPkt.data = rentedBuffer;

                                    MirrorServerIncomingQueue.Enqueue(dataPkt);
                                }
                                catch (Exception e)
                                {
                                    Debug.LogError($"Ignorance caught an exception while trying to copy data from the unmanaged (ENet) world to managed (Mono/IL2CPP) world. Please consider reporting this to the Ignorance developer on GitHub.\n" +
                                                   $"Exception returned was: {e.Message}\n" +
                                                   $"Debug details: {(netEvent.Packet.Data == null ? "NetEvent Packet payload was NULL" : $"NetEvent Packet payload was valid")}, {netEvent.Packet.Length} byte(s) packet\n" +
                                                   $"Stack Trace: {e.StackTrace}");
                                    return;
                                }
                            }
                            else
                            {
                                // Kick the peer.
                                netEvent.Peer.DisconnectNow(0);
                            }

                            // Dispose of the packet - we're done.
                            netEvent.Packet.Dispose();
                            break;
                        }
                    }
                }

                // Disconnect everyone, we're done here.
                print($"Server thread is now kicking all connected peers...");
                foreach (KeyValuePair <int, Peer> kv in ConnectionIDToPeers)
                {
                    kv.Value.DisconnectNow(0);
                }

                print("Server thread is now flushing and cleaning up...");
                serverWorkerHost.Flush();

                // BUGFIX issue #59: "Player crash on second server client connection"
                // https://github.com/SoftwareGuy/Ignorance/issues/59
                ConnectionIDToPeers.Clear();
                PeersToConnectionIDs.Clear();

                // Server is no longer started.
                ServerStarted = false;

                Library.Deinitialize();
                Debug.Log("Ignorance has deinitialized ENet.");
            }
        }
예제 #5
0
        private static void ClientWorkerThread(ThreadBootstrapStruct startupInfo)
        {
            // Setup...
            uint    nextStatsUpdate = 0;
            Address cAddress        = new Address();

            // Drain anything in the queues...
            while (MirrorClientIncomingQueue.TryDequeue(out _))
            {
                ;
            }

            while (MirrorClientOutgoingQueue.TryDequeue(out _))
            {
                ;
            }

            // Thread Safety: Initialize ENet in its own thread.
            if (Library.Initialize())
            {
                Debug.Log("Ignorance has initialized ENet.");
            }
            else
            {
                Debug.LogError("Ignorance has failed to initialize ENet.");
                return;
            }

            // This comment was actually left blank, but now it's not. You're welcome.
            using (Host cHost = new Host())
            {
                try
                {
                    cHost.Create(null, 1, startupInfo.maxChannels, 0, 0);
                    ClientStarted = true;
                }
                catch (Exception e)
                {
                    Debug.LogError("Ignorance encountered a fatal exception. To help debug the issue, use a Debug DLL of ENet and look for a logfile in the root of your " +
                                   $"application's folder.\nIf you believe you found a bug, please report it on the GitHub issue tracker. The exception returned was: {e}");
                    return;
                }

                // Attempt to start connection...
                if (!cAddress.SetHost(startupInfo.hostAddress))
                {
                    Debug.LogError("Ignorance was unable to set the hostname or address. Was this string even valid? Please check it and try again.");
                    return;
                }

                cAddress.Port = startupInfo.port;
                Peer cPeer = cHost.Connect(cAddress, startupInfo.maxChannels);

                while (!clientShouldCeaseOperation)
                {
                    bool clientWasPolled = false;

                    if (Library.Time >= nextStatsUpdate)
                    {
                        statistics.CurrentPing   = cPeer.RoundTripTime;
                        statistics.BytesReceived = cPeer.BytesReceived;
                        statistics.BytesSent     = cPeer.BytesSent;

                        statistics.PacketsLost = cPeer.PacketsLost;
                        statistics.PacketsSent = cPeer.PacketsSent;

                        // Library.Time is milliseconds, so we need to do some quick math.
                        nextStatsUpdate = Library.Time + (uint)(startupInfo.pingUpdateInterval * 1000);
                    }

                    while (!clientWasPolled)
                    {
                        if (cHost.CheckEvents(out Event netEvent) <= 0)
                        {
                            if (cHost.Service(startupInfo.threadPumpTimeout, out netEvent) <= 0)
                            {
                                break;
                            }
                            clientWasPolled = true;
                        }

                        switch (netEvent.Type)
                        {
                        case EventType.Connect:
                            // Client connected.
                            IncomingPacket connPkt = default;
                            connPkt.type = MirrorPacketType.ClientConnected;
                            MirrorClientIncomingQueue.Enqueue(connPkt);
                            break;

                        case EventType.Timeout:
                            print("Ignorance: connection timeout");
                            // Client disconnected.
                            IncomingPacket timeoutPkt = default;
                            timeoutPkt.type = MirrorPacketType.ClientDisconnected;
                            MirrorClientIncomingQueue.Enqueue(timeoutPkt);
                            break;

                        case EventType.Disconnect:
                            // Client disconnected.
                            IncomingPacket disconnPkt = default;
                            disconnPkt.type = MirrorPacketType.ClientDisconnected;
                            MirrorClientIncomingQueue.Enqueue(disconnPkt);
                            break;

                        case EventType.Receive:
                            // Client recieving some data.
                            if (!netEvent.Packet.IsSet)
                            {
                                print("Ignorance WARNING: A incoming packet is not set correctly.");
                                break;
                            }

                            if (netEvent.Packet.Length > startupInfo.maxPacketSize)
                            {
                                print($"Ignorance: Packet dropped as it was too large from Peer {netEvent.Peer.ID}! {netEvent.Packet.Length} packet bytes > allowed size of {startupInfo.maxPacketSize} bytes.");
                                netEvent.Packet.Dispose();
                                break;
                            }
                            else
                            {
                                // invoke on the client.
                                try
                                {
                                    IncomingPacket dataPkt = default;
                                    dataPkt.type      = MirrorPacketType.ClientGotData;
                                    dataPkt.channelId = netEvent.ChannelID;

                                    byte[] rentedBuffer = System.Buffers.ArrayPool <byte> .Shared.Rent(netEvent.Packet.Length);

                                    netEvent.Packet.CopyTo(rentedBuffer);
                                    dataPkt.data = rentedBuffer;

                                    MirrorClientIncomingQueue.Enqueue(dataPkt);
                                }
                                catch (Exception e)
                                {
                                    Debug.LogError($"Ignorance caught an exception while trying to copy data from the unmanaged (ENet) world to managed (Mono/IL2CPP) world. Please consider reporting this to the Ignorance developer on GitHub.\n" +
                                                   $"Exception returned was: {e.Message}\n" +
                                                   $"Debug details: {(netEvent.Packet.Data == null ? "NetEvent Packet payload was NULL" : $"NetEvent Packet payload was valid")}, {netEvent.Packet.Length} byte(s) packet\n" +
                                                   $"Stack Trace: {e.StackTrace}");
                                    break;
                                }

                                netEvent.Packet.Dispose();
                            }
                            break;
                        }
                    }

                    // Outgoing stuff
                    while (MirrorClientOutgoingQueue.TryDequeue(out OutgoingPacket opkt))
                    {
                        if (opkt.commandType == CommandPacketType.ClientDisconnectionRequest)
                        {
                            cPeer.DisconnectNow(0);
                            return;
                        }

                        int returnCode = cPeer.Send(opkt.channelId, ref opkt.payload);
                        if (returnCode != 0)
                        {
                            print($"Ignorance: Could not send {opkt.payload.Length} bytes to server on channel {opkt.channelId}, error code {returnCode}");
                        }
                    }
                }

                cPeer.DisconnectNow(0);
                cHost.Flush();
                ClientStarted = false;
            }

            Library.Deinitialize();
            Debug.Log("Ignorance has deinitialized ENet.");
        }
예제 #6
0
        private static void ClientWorkerThread(ThreadBootstrapStruct startupInfo)
        {
            // Setup...
            uint nextStatsUpdate = 0;

            byte[]  workerPacketBuffer = new byte[startupInfo.maxPacketSize];
            Address cAddress           = new Address();

            // Drain anything in the queues...
            while (MirrorClientIncomingQueue.TryDequeue(out _))
            {
                ;
            }

            while (MirrorClientOutgoingQueue.TryDequeue(out _))
            {
                ;
            }

            // This comment was actually left blank, but now it's not. You're welcome.
            using (Host cHost = new Host())
            {
                try
                {
                    cHost.Create(null, 1, startupInfo.maxChannels, 0, 0, startupInfo.maxPacketSize);
                    ClientStarted = true;
                }
                catch (Exception e)
                {
                    Debug.LogError("Ignorance encountered a fatal exception. To help debug the issue, use a Debug DLL of ENET and look for a 'enet_log.txt' file in the root of your " +
                                   $"application folder.\nIf you believe you found a bug, please report it on the GitHub issue tracker. The exception returned was: {e.ToString()}");
                    return;
                }

                // Attempt to start connection...
                cAddress.SetHost(startupInfo.hostAddress);
                cAddress.Port = startupInfo.port;
                Peer cPeer = cHost.Connect(cAddress, startupInfo.maxChannels);

                while (!clientShouldCeaseOperation)
                {
                    bool clientWasPolled = false;

                    if (Library.Time >= nextStatsUpdate)
                    {
                        statistics.CurrentPing   = cPeer.RoundTripTime;
                        statistics.BytesReceived = cPeer.BytesReceived;
                        statistics.BytesSent     = cPeer.BytesSent;

                        statistics.PacketsLost = cPeer.PacketsLost;
                        statistics.PacketsSent = cPeer.PacketsSent;

                        // Library.Time is milliseconds, so we need to do some quick math.
                        nextStatsUpdate = Library.Time + (uint)(startupInfo.pingUpdateInterval * 1000);
                    }

                    while (!clientWasPolled)
                    {
                        if (cHost.CheckEvents(out Event networkEvent) <= 0)
                        {
                            if (cHost.Service(startupInfo.threadPumpTimeout, out networkEvent) <= 0)
                            {
                                break;
                            }
                            clientWasPolled = true;
                        }

                        switch (networkEvent.Type)
                        {
                        case EventType.Connect:
                            // Client connected.
                            IncomingPacket connPkt = default;
                            connPkt.type = MirrorPacketType.ClientConnected;
                            MirrorClientIncomingQueue.Enqueue(connPkt);
                            break;

                        case EventType.Timeout:
                        case EventType.Disconnect:
                            // Client disconnected.
                            IncomingPacket disconnPkt = default;
                            disconnPkt.type = MirrorPacketType.ClientDisconnected;
                            MirrorClientIncomingQueue.Enqueue(disconnPkt);
                            break;

                        case EventType.Receive:
                            // Client recieving some data.
                            if (!networkEvent.Packet.IsSet)
                            {
                                print("Ignorance WARNING: A incoming packet is not set correctly.");
                                break;
                            }

                            if (networkEvent.Packet.Length > workerPacketBuffer.Length)
                            {
                                print($"Ignorance: Packet too big to fit in buffer. {networkEvent.Packet.Length} packet bytes vs {workerPacketBuffer.Length} cache bytes {networkEvent.Peer.ID}.");
                                networkEvent.Packet.Dispose();
                                break;
                            }
                            else
                            {
                                // invoke on the client.
                                try
                                {
                                    networkEvent.Packet.CopyTo(workerPacketBuffer);
                                }
                                catch (Exception e)
                                {
                                    Debug.LogError($"Ignorance caught an exception while trying to copy data from the unmanaged (ENET) world to managed (Mono/IL2CPP) world. Please consider reporting this to the Ignorance developer on GitHub.\n" +
                                                   $"Exception returned was: {e.Message}\n" +
                                                   $"Debug details: {(workerPacketBuffer == null ? "packet buffer was NULL" : $"{workerPacketBuffer.Length} byte work buffer")}, {networkEvent.Packet.Length} byte(s) network packet length\n" +
                                                   $"Stack Trace: {e.StackTrace}");
                                    networkEvent.Packet.Dispose();
                                    break;
                                }

                                int spLength = networkEvent.Packet.Length;

                                IncomingPacket dataPkt = default;
                                dataPkt.type = MirrorPacketType.ClientGotData;
                                dataPkt.data = new byte[spLength];      // Grrr!!!
                                networkEvent.Packet.CopyTo(dataPkt.data);

                                MirrorClientIncomingQueue.Enqueue(dataPkt);
                            }
                            break;
                        }

                        networkEvent.Packet.Dispose();
                    }
예제 #7
0
        private static void ServerWorkerThread(ThreadBootstrapStruct startupInformation)
        {
            // Worker buffer.
            byte[] workerPacketBuffer = new byte[startupInformation.maxPacketSize];
            // Connection ID.
            int nextConnectionId = 1;
            // Server address properties
            Address eAddress = new Address()
            {
                Port = startupInformation.port,
            };

            // Bind on everything or not?
            if (!string.IsNullOrEmpty(startupInformation.hostAddress))
            {
                eAddress.SetHost(startupInformation.hostAddress);
            }

            using (Host serverWorkerHost = new Host())
            {
                try
                {
                    serverWorkerHost.Create(eAddress, startupInformation.maxPeers, startupInformation.maxChannels, 0, 0, startupInformation.maxPacketSize);
                    ServerStarted = true;
                }
                catch (Exception e)
                {
                    Debug.LogError("Ignorance encountered a fatal exception. I'm sorry, but I gotta bail - if you believe you found a bug, please report it on the GitHub.\n" +
                                   $"The exception returned was: {e.ToString()}");
                    return;
                }

                Debug.Log($"Ignorance Server worker thread is ready for connections! I'm listening on UDP port {startupInformation.port}.\n" +
                          $"Capacity: {startupInformation.maxPeers} peers with {startupInformation.maxChannels} channels. My buffer size is {startupInformation.maxPacketSize} bytes");

                // The meat and potatoes.
                while (!serverShouldCeaseOperation)
                {
                    // Outgoing stuff
                    while (MirrorServerOutgoingQueue.TryDequeue(out OutgoingPacket opkt))
                    {
                        switch (opkt.commandType)
                        {
                        case CommandPacketType.BootToTheFace:
                            if (ConnectionIDToPeers.TryGetValue(opkt.connectionId, out Peer bootedPeer))
                            {
                                bootedPeer.DisconnectLater(0);
                            }
                            break;

                        case CommandPacketType.Nothing:
                        default:
                            if (ConnectionIDToPeers.TryGetValue(opkt.connectionId, out Peer target))
                            {
                                int returnCode = target.SendAndReturnStatusCode(opkt.channelId, ref opkt.payload);
                                if (returnCode != 0)
                                {
                                    print($"Could not send {opkt.payload.Length} bytes to target peer {target.ID} on channel {opkt.channelId}, error code {returnCode}");
                                }
                            }
                            break;
                        }
                    }

                    // Flush here?
                    serverWorkerHost.Flush();

                    // Incoming stuffs now.
                    bool hasBeenPolled = false;

                    while (!hasBeenPolled)
                    {
                        if (serverWorkerHost.CheckEvents(out Event netEvent) <= 0)
                        {
                            if (serverWorkerHost.Service(startupInformation.threadPumpTimeout, out netEvent) <= 0)
                            {
                                break;
                            }

                            hasBeenPolled = true;
                        }

                        switch (netEvent.Type)
                        {
                        case EventType.None:
                            break;

                        case EventType.Connect:
                            int connectionId = nextConnectionId;
                            nextConnectionId += 1;

                            // Add to dictionaries.
                            if (!PeersToConnectionIDs.TryAdd(netEvent.Peer, connectionId))
                            {
                                Debug.LogError($"ERROR: We already know this client in our Connection ID to Peer Mapping?!");
                            }
                            if (!ConnectionIDToPeers.TryAdd(connectionId, netEvent.Peer))
                            {
                                Debug.LogError($"ERROR: We already know this client in our Peer to ConnectionID Mapping?!");
                            }

                            // Send a message back to mirror.
                            IncomingPacket newConnectionPkt = default;
                            newConnectionPkt.connectionId = connectionId;
                            newConnectionPkt.type         = MirrorPacketType.ServerClientConnected;
                            newConnectionPkt.ipAddress    = netEvent.Peer.IP;

                            MirrorServerIncomingQueue.Enqueue(newConnectionPkt);
                            break;

                        case EventType.Disconnect:
                        case EventType.Timeout:
                            if (PeersToConnectionIDs.TryGetValue(netEvent.Peer, out int deadPeer))
                            {
                                IncomingPacket disconnectionPkt = default;
                                disconnectionPkt.connectionId = deadPeer;
                                disconnectionPkt.type         = MirrorPacketType.ServerClientDisconnected;
                                disconnectionPkt.ipAddress    = netEvent.Peer.IP;

                                MirrorServerIncomingQueue.Enqueue(disconnectionPkt);
                                ConnectionIDToPeers.TryRemove(deadPeer, out Peer _);
                            }

                            PeersToConnectionIDs.TryRemove(netEvent.Peer, out int _);
                            break;

                        case EventType.Receive:
                            int dataConnID = -1;

                            if (PeersToConnectionIDs.TryGetValue(netEvent.Peer, out dataConnID))
                            {
                                if (netEvent.Packet.Length > startupInformation.maxPacketSize)
                                {
                                    Debug.LogWarning($"Ignorance WARNING: Packet too large for buffer; dropping. Packet {netEvent.Packet.Length} bytes; limit is {startupInformation.maxPacketSize} bytes.");
                                    netEvent.Packet.Dispose();
                                    return;
                                }

                                // Copy to the packet cache.
                                netEvent.Packet.CopyTo(workerPacketBuffer);
                                int spLength = netEvent.Packet.Length;

                                IncomingPacket dataPkt = default;
                                dataPkt.connectionId = dataConnID;
                                dataPkt.channelId    = (int)netEvent.ChannelID;
                                dataPkt.type         = MirrorPacketType.ServerClientSentData;

                                // TODO: Come up with a better method of doing this.
                                dataPkt.data = new byte[spLength];
                                Array.Copy(workerPacketBuffer, 0, dataPkt.data, 0, spLength);
                                // Faulty .Array on the end seems to return the rest of the buffer as well instead of just 10 bytes or whatever
                                // dataPkt.data = new ArraySegment<byte>(workerPacketBuffer, 0, spLength);
                                dataPkt.ipAddress = netEvent.Peer.IP;

                                MirrorServerIncomingQueue.Enqueue(dataPkt);
                            }
                            else
                            {
                                // Kick the peer.
                                netEvent.Peer.DisconnectNow(0);
                            }

                            netEvent.Packet.Dispose();
                            break;
                        }
                    }
                }

                // Disconnect everyone, we're done here.
                print($"Kicking all connected Peers...");
                foreach (KeyValuePair <int, Peer> kv in ConnectionIDToPeers)
                {
                    kv.Value.DisconnectNow(0);
                }

                print("Flushing...");
                serverWorkerHost.Flush();
                ServerStarted = false;
            }
        }
예제 #8
0
        private static void ClientWorkerThread(ThreadBootstrapStruct startupInfo)
        {
            // Setup...
            byte[]  workerPacketBuffer = new byte[startupInfo.maxPacketSize];
            Address cAddress           = new Address();

            // Drain anything in the queues...
            while (MirrorClientIncomingQueue.TryDequeue(out _))
            {
                ;
            }

            while (MirrorClientOutgoingQueue.TryDequeue(out _))
            {
                ;
            }

            //
            using (Host cHost = new Host())
            {
                try
                {
                    cHost.Create(null, 1, startupInfo.maxChannels, 0, 0, startupInfo.maxPacketSize);
                    ClientStarted = true;
                }
                catch (Exception e)
                {
                    Debug.LogError("Ignorance encountered a fatal exception. I'm sorry, but I gotta bail - if you believe you found a bug, please report it on the GitHub.\n" +
                                   $"The exception returned was: {e.ToString()}");
                    return;
                }

                // Attempt to start connection...
                cAddress.SetHost(startupInfo.hostAddress);
                cAddress.Port = startupInfo.port;
                Peer cPeer = cHost.Connect(cAddress, startupInfo.maxChannels);

                while (!clientShouldCeaseOperation)
                {
                    bool clientWasPolled = false;

                    while (!clientWasPolled)
                    {
                        if (cHost.CheckEvents(out Event networkEvent) <= 0)
                        {
                            if (cHost.Service(startupInfo.threadPumpTimeout, out networkEvent) <= 0)
                            {
                                break;
                            }
                            clientWasPolled = true;
                        }

                        switch (networkEvent.Type)
                        {
                        case EventType.Connect:
                            // Client connected.
                            IncomingPacket connPkt = default;
                            connPkt.type = MirrorPacketType.ClientConnected;
                            MirrorClientIncomingQueue.Enqueue(connPkt);
                            break;

                        case EventType.Timeout:
                        case EventType.Disconnect:
                            // Client disconnected.
                            IncomingPacket disconnPkt = default;
                            disconnPkt.type = MirrorPacketType.ClientDisconnected;
                            MirrorClientIncomingQueue.Enqueue(disconnPkt);
                            break;

                        case EventType.Receive:
                            // Client recieving some data.
                            if (networkEvent.Packet.Length > workerPacketBuffer.Length)
                            {
                                print($"Ignorance: Packet too big to fit in buffer. {networkEvent.Packet.Length} packet bytes vs {workerPacketBuffer.Length} cache bytes {networkEvent.Peer.ID}.");
                                networkEvent.Packet.Dispose();
                            }
                            else
                            {
                                // invoke on the client.
                                networkEvent.Packet.CopyTo(workerPacketBuffer);
                                int spLength = networkEvent.Packet.Length;
                                networkEvent.Packet.Dispose();

                                IncomingPacket dataPkt = default;
                                dataPkt.type = MirrorPacketType.ClientGotData;
                                dataPkt.data = new byte[spLength];      // Grrr!!!
                                networkEvent.Packet.CopyTo(dataPkt.data);

                                MirrorClientIncomingQueue.Enqueue(dataPkt);
                            }
                            break;
                        }
                    }

                    // Outgoing stuff
                    while (MirrorClientOutgoingQueue.TryDequeue(out OutgoingPacket opkt))
                    {
                        if (opkt.commandType == CommandPacketType.ClientDisconnectRequest)
                        {
                            cPeer.DisconnectNow(0);
                            return;
                        }

                        int returnCode = cPeer.SendAndReturnStatusCode(opkt.channelId, ref opkt.payload);
                        if (returnCode != 0)
                        {
                            print($"Ignorance: Could not send {opkt.payload.Length} bytes to server on channel {opkt.channelId}, error code {returnCode}");
                        }
                    }
                }

                cPeer.DisconnectNow(0);
                cHost.Flush();
                ClientStarted = false;
            }
        }