예제 #1
0
        // This runs in a seperate thread, be careful accessing anything outside of it's thread
        // or you may get an AccessViolation/crash.
        private void ThreadWorker(Object parameters)
        {
            if (Verbosity > 0)
            {
                Debug.Log("Ignorance Client: Initializing. Please stand by...");
            }

            ThreadParamInfo      setupInfo;
            Address              clientAddress = new Address();
            Peer                 clientPeer;  // The peer object that represents the client's connection.
            Host                 clientHost;  // NOT related to Mirror "Client Host". This is the client's ENet Host Object.
            Event                clientEvent; // Used when clients get events on the network.
            IgnoranceClientStats icsu = default;
            bool                 alreadyNotifiedAboutDisconnect = false;

            // Unused for now
            // bool emergencyStop = false;

            // Grab the setup information.
            if (parameters.GetType() == typeof(ThreadParamInfo))
            {
                setupInfo = (ThreadParamInfo)parameters;
            }
            else
            {
                Debug.LogError("Ignorance Client: Startup failure; Invalid thread parameters. Aborting.");
                return;
            }

            // Attempt to initialize ENet inside the thread.
            if (Library.Initialize())
            {
                Debug.Log("Ignorance Client: ENet Native successfully initialized.");
            }
            else
            {
                Debug.LogError("Ignorance Client: Failed to initialize ENet Native. This threads' f****d.");
                return;
            }

            // Attempt to connect to our target.
            clientAddress.SetHost(setupInfo.Address);
            clientAddress.Port = (ushort)setupInfo.Port;

            using (clientHost = new Host())
            {
                // TODO: Maybe try catch this
                clientHost.Create();
                clientPeer = clientHost.Connect(clientAddress, setupInfo.Channels);

                while (Commands.TryDequeue(out IgnoranceCommandPacket commandPacket))
                {
                    switch (commandPacket.Type)
                    {
                    default:
                        break;

                    case IgnoranceCommandType.ClientWantsToStop:
                        CeaseOperation = true;
                        break;

                    case IgnoranceCommandType.ClientStatusRequest:
                        // Respond with statistics so far.
                        if (!clientPeer.IsSet)
                        {
                            break;
                        }

                        icsu.RTT = clientPeer.RoundTripTime;

                        icsu.BytesReceived = clientPeer.BytesReceived;
                        icsu.BytesSent     = clientPeer.BytesSent;

                        icsu.PacketsReceived = clientHost.PacketsReceived;
                        icsu.PacketsSent     = clientPeer.PacketsSent;
                        icsu.PacketsLost     = clientPeer.PacketsLost;

                        StatusUpdates.Enqueue(icsu);
                        break;
                    }
                }

                // Process network events as long as we're not ceasing operation.
                while (!CeaseOperation)
                {
                    bool pollComplete = false;

                    // Step 1: Sending to Server
                    while (Outgoing.TryDequeue(out IgnoranceOutgoingPacket outgoingPacket))
                    {
                        // TODO: Revise this, could we tell the Peer to disconnect right here?
                        // Stop early if we get a client stop packet.
                        // if (outgoingPacket.Type == IgnorancePacketType.ClientWantsToStop) break;

                        int ret = clientPeer.Send(outgoingPacket.Channel, ref outgoingPacket.Payload);

                        if (ret < 0 && setupInfo.Verbosity > 0)
                        {
                            Debug.LogWarning($"Ignorance Client: ENet error {ret} while sending packet to Server via Peer {outgoingPacket.NativePeerId}.");
                        }
                    }

                    // If something outside the thread has told us to stop execution, then we need to break out of this while loop.
                    // while loop to break out of is while(!CeaseOperation).
                    if (CeaseOperation)
                    {
                        break;
                    }

                    // Step 2: Receive Data packets
                    // This loops until polling is completed. It may take a while, if it's
                    // a slow networking day.
                    while (!pollComplete)
                    {
                        Packet incomingPacket;
                        Peer   incomingPeer;
                        int    incomingPacketLength;

                        // Any events worth checking out?
                        if (clientHost.CheckEvents(out clientEvent) <= 0)
                        {
                            // If service time is met, break out of it.
                            if (clientHost.Service(setupInfo.PollTime, out clientEvent) <= 0)
                            {
                                break;
                            }

                            // Poll is done.
                            pollComplete = true;
                        }

                        // Setup the packet references.
                        incomingPeer = clientEvent.Peer;

                        // Now, let's handle those events.
                        switch (clientEvent.Type)
                        {
                        case EventType.None:
                        default:
                            break;

                        case EventType.Connect:
                            if (setupInfo.Verbosity > 0)
                            {
                                Debug.Log("Ignorance Client: ENet has connected to the server.");
                            }

                            ConnectionEvents.Enqueue(new IgnoranceConnectionEvent
                            {
                                EventType    = 0x00,
                                NativePeerId = incomingPeer.ID,
                                IP           = incomingPeer.IP,
                                Port         = incomingPeer.Port
                            });
                            break;

                        case EventType.Disconnect:
                        case EventType.Timeout:
                            if (setupInfo.Verbosity > 0)
                            {
                                Debug.Log("Ignorance Client: ENet has been disconnected from the server.");
                            }

                            ConnectionEvents.Enqueue(new IgnoranceConnectionEvent {
                                EventType = 0x01
                            });
                            CeaseOperation = true;
                            alreadyNotifiedAboutDisconnect = true;
                            break;

                        case EventType.Receive:
                            // Receive event type usually includes a packet; so cache its reference.
                            incomingPacket = clientEvent.Packet;

                            if (!incomingPacket.IsSet)
                            {
                                if (setupInfo.Verbosity > 0)
                                {
                                    Debug.LogWarning($"Ignorance Client: A receive event did not supply us with a packet to work with. This should never happen.");
                                }
                                break;
                            }

                            incomingPacketLength = incomingPacket.Length;

                            // Never consume more than we can have capacity for.
                            if (incomingPacketLength > setupInfo.PacketSizeLimit)
                            {
                                if (setupInfo.Verbosity > 0)
                                {
                                    Debug.LogWarning($"Ignorance Client: Incoming packet is too big. My limit is {setupInfo.PacketSizeLimit} byte(s) whilest this packet is {incomingPacketLength} bytes.");
                                }

                                incomingPacket.Dispose();
                                break;
                            }

                            IgnoranceIncomingPacket incomingQueuePacket = new IgnoranceIncomingPacket
                            {
                                Channel      = clientEvent.ChannelID,
                                NativePeerId = incomingPeer.ID,
                                Payload      = incomingPacket
                            };

                            Incoming.Enqueue(incomingQueuePacket);
                            break;
                        }
                    }

                    // If something outside the thread has told us to stop execution, then we need to break out of this while loop.
                    // while loop to break out of is while(!CeaseOperation).
                    if (CeaseOperation)
                    {
                        break;
                    }
                }

                Debug.Log("Ignorance Client: Thread shutdown commencing. Disconnecting and flushing connection.");

                // Flush the client and disconnect.
                clientPeer.Disconnect(0);
                clientHost.Flush();

                // Fix for client stuck in limbo, since the disconnection event may not be fired until next loop.
                if (!alreadyNotifiedAboutDisconnect)
                {
                    ConnectionEvents.Enqueue(new IgnoranceConnectionEvent {
                        EventType = 0x01
                    });
                    alreadyNotifiedAboutDisconnect = true;
                }
            }

            // Fix for client stuck in limbo, since the disconnection event may not be fired until next loop, again.
            if (!alreadyNotifiedAboutDisconnect)
            {
                ConnectionEvents.Enqueue(new IgnoranceConnectionEvent {
                    EventType = 0x01
                });
            }

            // Deinitialize
            Library.Deinitialize();

            if (setupInfo.Verbosity > 0)
            {
                Debug.Log("Ignorance Client: Shutdown complete.");
            }
        }
예제 #2
0
        private void ProcessClientPackets()
        {
            IgnoranceIncomingPacket incomingPacket;
            IgnoranceClientStats    clientStats;
            Packet payload;

            // Handle connection events.
            while (Client.ConnectionEvents.TryDequeue(out IgnoranceConnectionEvent connectionEvent))
            {
                if (LogType == IgnoranceLogType.Verbose)
                {
                    Debug.Log($"Ignorance Client Debug: Processing a client ConnectionEvents queue item. Type: {connectionEvent.EventType.ToString("{0:X2}")}");
                }

                switch (connectionEvent.EventType)
                {
                case 0x00:
                    // Connected to server.
                    ClientState = ConnectionState.Connected;

                    if (LogType != IgnoranceLogType.Nothing)
                    {
                        Debug.Log($"Ignorance Client has successfully connected to server at {connectionEvent.IP}:{connectionEvent.Port}");
                    }

                    ignoreDataPackets = false;
                    OnClientConnected?.Invoke();
                    break;

                case 0x01:
                    // Disconnected from server.
                    ClientState = ConnectionState.Disconnected;

                    if (LogType != IgnoranceLogType.Nothing)
                    {
                        Debug.Log($"Ignorance Client has been disconnected from server.");
                    }

                    ignoreDataPackets = true;
                    OnClientDisconnected?.Invoke();
                    break;

                default:
                    // Unknown type.
                    if (LogType != IgnoranceLogType.Nothing)
                    {
                        Debug.LogWarning($"Ignorance Client: Unknown connection event type {connectionEvent.EventType.ToString("{0:X2}")}.");
                    }
                    break;
                }
            }

            // Handle the incoming messages.
            while (Client.Incoming.TryDequeue(out incomingPacket))
            {
                // Temporary fix: if ENet thread is too fast for Mirror, then ignore the packet.
                // This is seen sometimes if you stop the client and there's still stuff in the queue.
                if (ignoreDataPackets)
                {
                    if (LogType == IgnoranceLogType.Verbose)
                    {
                        Debug.Log("Ignorance Client: ProcessClientPackets cycle skipped; ignoring data packet");
                    }
                    break;
                }


                // Otherwise client recieved data, advise Mirror.
                // print($"Byte array: {incomingPacket.RentedByteArray.Length}. Packet Length: {incomingPacket.Length}");
                payload = incomingPacket.Payload;
                int length = payload.Length;
                ArraySegment <byte> dataSegment;

                // Copy to working buffer and dispose of it.
                if (length > InternalPacketBuffer.Length)
                {
                    // Unity's favourite: A fresh 'n' tasty GC Allocation!
                    byte[] oneFreshNTastyGcAlloc = new byte[length];

                    payload.CopyTo(oneFreshNTastyGcAlloc);
                    dataSegment = new ArraySegment <byte>(oneFreshNTastyGcAlloc, 0, length);
                }
                else
                {
                    payload.CopyTo(InternalPacketBuffer);
                    dataSegment = new ArraySegment <byte>(InternalPacketBuffer, 0, length);
                }

                payload.Dispose();

                OnClientDataReceived?.Invoke(dataSegment, incomingPacket.Channel);

#if !MIRROR_41_0_OR_NEWER
                // Some messages can disable the transport
                // If the transport was disabled by any of the messages, we have to break out of the loop and wait until we've been re-enabled.

                if (!enabled)
                {
                    break;
                }
#endif
            }

            // Step 3: Handle status updates.
            if (Client.StatusUpdates.TryDequeue(out clientStats))
            {
                ClientStatistics = clientStats;
            }
        }
        private void ProcessClientPackets()
        {
            Packet payload;

            // Handle connection events.
            while (Client.ConnectionEvents.TryDequeue(out IgnoranceConnectionEvent connectionEvent))
            {
                if (connectionEvent.WasDisconnect)
                {
                    // Disconnected from server.
                    ClientState = ConnectionState.Disconnected;

                    ignoreDataPackets = true;
                    OnClientDisconnected?.Invoke();
                }
                else
                {
                    // Connected to server.
                    ClientState = ConnectionState.Connected;

                    ignoreDataPackets = false;
                    OnClientConnected?.Invoke();
                }
            }

            // Now handle the incoming messages.
            while (Client.Incoming.TryDequeue(out IgnoranceIncomingPacket incomingPacket))
            {
                // Temporary fix: if ENet thread is too fast for Mirror, then ignore the packet.
                // This is seen sometimes if you stop the client and there's still stuff in the queue.
                if (ignoreDataPackets)
                {
                    break;
                }

                // Otherwise client recieved data, advise Mirror.
                // print($"Byte array: {incomingPacket.RentedByteArray.Length}. Packet Length: {incomingPacket.Length}");
                payload = incomingPacket.Payload;
                int length = payload.Length;
                ArraySegment <byte> dataSegment;

                // Copy to working buffer and dispose of it.
                if (length > InternalPacketBuffer.Length)
                {
                    // Unity's favourite: A fresh 'n' tasty GC Allocation!
                    byte[] oneFreshNTastyGcAlloc = new byte[length];

                    payload.CopyTo(oneFreshNTastyGcAlloc);
                    dataSegment = new ArraySegment <byte>(oneFreshNTastyGcAlloc, 0, length);
                }
                else
                {
                    payload.CopyTo(InternalPacketBuffer);
                    dataSegment = new ArraySegment <byte>(InternalPacketBuffer, 0, length);
                }

                payload.Dispose();

                OnClientDataReceived?.Invoke(dataSegment, incomingPacket.Channel);
            }

            // Step 3: Handle other commands.
            while (Client.Commands.TryDequeue(out IgnoranceCommandPacket commandPacket))
            {
                switch (commandPacket.Type)
                {
                // ...
                default:
                    break;
                }
            }

            // Step 4: Handle status updates.
            if (Client.StatusUpdates.TryDequeue(out IgnoranceClientStats clientStats))
            {
                ClientStatistics = clientStats;
            }
        }
예제 #4
0
        // This runs in a seperate thread, be careful accessing anything outside of it's thread
        // or you may get an AccessViolation/crash.
        private void ThreadWorker(Object parameters)
        {
            if (Verbosity > 0)
            {
                Debug.Log("Client Worker Thread: Startup");
            }

            ThreadParamInfo      setupInfo;
            Address              clientAddress = new Address();
            Peer                 clientPeer;
            Host                 clientENetHost;
            Event                clientENetEvent;
            IgnoranceClientStats icsu = default;

            // Grab the setup information.
            if (parameters.GetType() == typeof(ThreadParamInfo))
            {
                setupInfo = (ThreadParamInfo)parameters;
            }
            else
            {
                Debug.LogError("Thread worker startup failure: Invalid thread parameters. Aborting.");
                return;
            }

            // Attempt to initialize ENet inside the thread.
            if (Library.Initialize())
            {
                Debug.Log("Client Worker Thread: Initialized ENet.");
            }
            else
            {
                Debug.LogError("Client Worker Thread: Failed to initialize ENet. This threads' f****d.");
                return;
            }

            // Attempt to connect to our target.
            clientAddress.SetHost(setupInfo.Address);
            clientAddress.Port = (ushort)setupInfo.Port;

            using (clientENetHost = new Host())
            {
                // TODO: Maybe try catch this
                clientENetHost.Create();
                clientPeer = clientENetHost.Connect(clientAddress, setupInfo.Channels);

                while (!CeaseOperation)
                {
                    bool pollComplete = false;

                    // Step 0: Handle commands.
                    while (Commands.TryDequeue(out IgnoranceCommandPacket commandPacket))
                    {
                        switch (commandPacket.Type)
                        {
                        default:
                            break;

                        case IgnoranceCommandType.ClientWantsToStop:
                            CeaseOperation = true;
                            break;

                        case IgnoranceCommandType.ClientRequestsStatusUpdate:
                            // Respond with statistics so far.
                            if (!clientPeer.IsSet)
                            {
                                break;
                            }

                            icsu.RTT = clientPeer.RoundTripTime;

                            icsu.BytesReceived = clientPeer.BytesReceived;
                            icsu.BytesSent     = clientPeer.BytesSent;

                            icsu.PacketsReceived = clientENetHost.PacketsReceived;
                            icsu.PacketsSent     = clientPeer.PacketsSent;
                            icsu.PacketsLost     = clientPeer.PacketsLost;

                            StatusUpdates.Enqueue(icsu);
                            break;
                        }
                    }
                    // Step 1: Send out data.
                    // ---> Sending to Server
                    while (Outgoing.TryDequeue(out IgnoranceOutgoingPacket outgoingPacket))
                    {
                        // TODO: Revise this, could we tell the Peer to disconnect right here?
                        // Stop early if we get a client stop packet.
                        // if (outgoingPacket.Type == IgnorancePacketType.ClientWantsToStop) break;

                        int ret = clientPeer.Send(outgoingPacket.Channel, ref outgoingPacket.Payload);

                        if (ret < 0 && setupInfo.Verbosity > 0)
                        {
                            Debug.LogWarning($"Client Worker Thread: Failed sending a packet to Peer {outgoingPacket.NativePeerId}, error code {ret}");
                        }
                    }

                    // Step 2:
                    // <----- Receive Data packets
                    // This loops until polling is completed. It may take a while, if it's
                    // a slow networking day.
                    while (!pollComplete)
                    {
                        Packet incomingPacket;
                        Peer   incomingPeer;
                        int    incomingPacketLength;

                        // Any events worth checking out?
                        if (clientENetHost.CheckEvents(out clientENetEvent) <= 0)
                        {
                            // If service time is met, break out of it.
                            if (clientENetHost.Service(setupInfo.PollTime, out clientENetEvent) <= 0)
                            {
                                break;
                            }

                            // Poll is done.
                            pollComplete = true;
                        }

                        // Setup the packet references.
                        incomingPeer = clientENetEvent.Peer;

                        // Now, let's handle those events.
                        switch (clientENetEvent.Type)
                        {
                        case EventType.None:
                        default:
                            break;

                        case EventType.Connect:
                            ConnectionEvents.Enqueue(new IgnoranceConnectionEvent()
                            {
                                NativePeerId = incomingPeer.ID,
                                IP           = incomingPeer.IP,
                                Port         = incomingPeer.Port
                            });
                            break;

                        case EventType.Disconnect:
                        case EventType.Timeout:
                            ConnectionEvents.Enqueue(new IgnoranceConnectionEvent()
                            {
                                WasDisconnect = true
                            });
                            break;


                        case EventType.Receive:
                            // Receive event type usually includes a packet; so cache its reference.
                            incomingPacket = clientENetEvent.Packet;

                            if (!incomingPacket.IsSet)
                            {
                                if (setupInfo.Verbosity > 0)
                                {
                                    Debug.LogWarning($"Client Worker Thread: A receive event did not supply us with a packet to work with. This should never happen.");
                                }
                                break;
                            }

                            incomingPacketLength = incomingPacket.Length;

                            // Never consume more than we can have capacity for.
                            if (incomingPacketLength > setupInfo.PacketSizeLimit)
                            {
                                if (setupInfo.Verbosity > 0)
                                {
                                    Debug.LogWarning($"Client Worker Thread: Received a packet too large, {incomingPacketLength} bytes while our limit is {setupInfo.PacketSizeLimit} bytes.");
                                }

                                incomingPacket.Dispose();
                                break;
                            }

                            IgnoranceIncomingPacket incomingQueuePacket = new IgnoranceIncomingPacket
                            {
                                Channel      = clientENetEvent.ChannelID,
                                NativePeerId = incomingPeer.ID,
                                Payload      = incomingPacket
                            };

                            Incoming.Enqueue(incomingQueuePacket);
                            break;
                        }
                    }
                }

                // Flush the client and disconnect.
                clientPeer.Disconnect(0);
                clientENetHost.Flush();
            }

            // Deinitialize
            Library.Deinitialize();
            if (setupInfo.Verbosity > 0)
            {
                Debug.Log("Client Worker Thread: Shutdown.");
            }
        }
예제 #5
0
        private void ProcessClientPackets()
        {
            IgnoranceIncomingPacket incomingPacket;
            IgnoranceCommandPacket  commandPacket;
            IgnoranceClientStats    clientStats;
            Packet payload;

            while (Client.Incoming.TryDequeue(out incomingPacket))
            {
                // Temporary fix: if ENet thread is too fast for Mirror, then ignore the packet.
                // This is seen sometimes if you stop the client and there's still stuff in the queue.
                if (ignoreDataPackets)
                {
                    if (LogType == IgnoranceLogType.Verbose)
                    {
                        Debug.Log("ProcessClientPackets cycle skipped; ignoring data packet");
                    }
                    break;
                }


                // Otherwise client recieved data, advise Mirror.
                // print($"Byte array: {incomingPacket.RentedByteArray.Length}. Packet Length: {incomingPacket.Length}");
                payload = incomingPacket.Payload;
                int length = payload.Length;
                ArraySegment <byte> dataSegment;

                // Copy to working buffer and dispose of it.
                if (length > InternalPacketBuffer.Length)
                {
                    byte[] oneFreshNTastyGcAlloc = new byte[length];

                    payload.CopyTo(oneFreshNTastyGcAlloc);
                    dataSegment = new ArraySegment <byte>(oneFreshNTastyGcAlloc, 0, length);
                }
                else
                {
                    payload.CopyTo(InternalPacketBuffer);
                    dataSegment = new ArraySegment <byte>(InternalPacketBuffer, 0, length);
                }

                payload.Dispose();

                OnClientDataReceived?.Invoke(dataSegment, incomingPacket.Channel);

                // Some messages can disable the transport
                // If the transport was disabled by any of the messages, we have to break out of the loop and wait until we've been re-enabled.
                if (!enabled)
                {
                    break;
                }
            }

            // Step 3: Handle other commands.
            while (Client.Commands.TryDequeue(out commandPacket))
            {
                switch (commandPacket.Type)
                {
                // ...
                default:
                    break;
                }
            }

            // Step 4: Handle status updates.
            if (Client.StatusUpdates.TryDequeue(out clientStats))
            {
                ClientStatistics = clientStats;
            }
        }