static NetworkConnection ProcessSingleConnection(NetworkDriver.Concurrent driver, NetworkConnection connection)
    {
        DataStreamReader strm;

        NetworkEvent.Type cmd;
        // Pop all events for the connection
        while ((cmd = driver.PopEventForConnection(connection, out strm)) != NetworkEvent.Type.Empty)
        {
            if (cmd == NetworkEvent.Type.Data)
            {
                // For ping requests we reply with a pong message
                int id = strm.ReadInt();
                // Create a temporary DataStreamWriter to keep our serialized pong message
                if (driver.BeginSend(connection, out var pongData) == 0)
                {
                    pongData.WriteInt(id);
                    // Send the pong message with the same id as the ping
                    driver.EndSend(pongData);
                }
            }
            else if (cmd == NetworkEvent.Type.Disconnect)
            {
                // When disconnected we make sure the connection return false to IsCreated so the next frames
                // DriverUpdateJob will remove it
                return(default(NetworkConnection));
            }
        }

        return(connection);
    }
Exemplo n.º 2
0
            public unsafe void Execute(DynamicComponentTypeHandle *ghostChunkComponentTypesPtr,
                                       int ghostChunkComponentTypesLength)
            {
                var writer = NetDriver.BeginSend(UnreliablePipeline, ConnectionFromEntity[ConnectionEntity].Value,
                                                 GlobalConstants.TargetPacketSize);

                WriteHeader(ref writer, AckFromEntity[ConnectionEntity]);

                var lenWriter = writer;

                writer.WriteUInt(0);
                writer.WriteUInt(0);

                // 销毁的entity
                uint despawnLen = 0;
                uint updateLen  = 0;

                despawnLen = SerializeDespawnEntities(ref writer, DespawnChunks);

                if (writer.HasFailedWrites)
                {
                    NetDriver.AbortSend(writer);
                    throw new InvalidOperationException("单个快照中无法包含所有需要销毁的GhostId!");
                }

                updateLen = SerializeEntities(ref writer, GhostChunks, default, ghostChunkComponentTypesPtr,
    public void Execute(int index)
    {
        DataStreamReader stream;

        Assert.IsTrue(connections[index].IsCreated);

        NetworkEvent.Type cmd;
        while ((cmd = driver.PopEventForConnection(connections[index], out stream)) !=
               NetworkEvent.Type.Empty)
        {
            if (cmd == NetworkEvent.Type.Data)
            {
                NativeString64 message = stream.ReadString();
                if (message.ToString() != "Message Confirmed")
                {
                    Debug.Log("Received Message: " + message);

                    var writer = driver.BeginSend(connections[index]);
                    writer.WriteString("Message Confirmed");
                    driver.EndSend(writer);
                }
            }
            else if (cmd == NetworkEvent.Type.Disconnect)
            {
                Debug.Log("Client disconnected from server");
                connections[index] = default(NetworkConnection);
            }
        }
    }
Exemplo n.º 4
0
        // -- IJob --
        public void Execute(int ci)
        {
            DataStreamReader stream;

            Assert.IsTrue(mConnections[ci].IsCreated);

            NetworkEvent.Type cmd;
            while ((cmd = mDriver.PopEventForConnection(mConnections[ci], out stream)) != NetworkEvent.Type.Empty)
            {
                switch (cmd)
                {
                case NetworkEvent.Type.Data: {
                    // read number of events
                    var n = stream.ReadByte();
                    Log.D($"Host - received {n} events from client {ci}");

                    // read events out of stream
                    var events = new AnyEvent[n];

                    for (var i = 0; i < n; i++)
                    {
                        events[i] = new AnyEvent(
                            step: stream.ReadUInt(),
                            new AnyEvent.Value(
                                type: (EventType)stream.ReadByte()
                                )
                            );
                    }

                    // route events back to every client
                    for (var i = 0; i < mConnections.Length; i++)
                    {
                        var writer = mDriver.BeginSend(mConnections[i]);

                        // write the number of events
                        writer.WriteByte(n);

                        // write each event
                        foreach (var evt in events)
                        {
                            writer.WriteUInt(evt.Step);
                            writer.WriteByte((byte)evt.Val.Type);
                        }

                        mDriver.EndSend(writer);
                    }

                    break;
                }

                case NetworkEvent.Type.Disconnect: {
                    Log.I($"Host - client {ci} disconnected");
                    mConnections[ci] = default;
                    break;
                }
                }
            }
        }
Exemplo n.º 5
0
            public unsafe void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
            {
                var entities    = chunk.GetNativeArray(entityType);
                var connections = chunk.GetNativeArray(connectionType);
                var inBuffers   = chunk.GetBufferAccessor(InBufferType);
                var outBuffers  = chunk.GetBufferAccessor(OutBufferType);

                for (int i = 0; i < inBuffers.Length; i++)
                {
                    if (driver.GetConnectionState(connections[i].Value) != NetworkConnection.State.Connected)
                    {
                        continue;
                    }

                    var parameters = new RpcExecutor.Parameters
                    {
                        Reader        = inBuffers[i].AsDataStreamReader(),
                        CommandBuffer = commandBuffer,
                        Connection    = entities[i],
                        JobIndex      = chunkIndex
                    };

#if UNITY_EDITOR || DEVELOPMENT_BUILD
                    RpcMS2(parameters.Reader.Length);
#endif
                    while (parameters.Reader.GetBytesRead() < parameters.Reader.Length)
                    {
                        var rpcIndex = parameters.Reader.ReadUShort();
                        if (rpcIndex >= execute.Length)
                        {
                        }
                        else
                        {
                            //TODO 安卓调用闪退
                            execute[rpcIndex].Execute.Ptr.Invoke(ref parameters);
                        }
                    }

                    inBuffers[i].Clear();

                    var sendBuf = outBuffers[i];
                    if (sendBuf.Length > 0)
                    {
                        DataStreamWriter tmp = driver.BeginSend(reliablePipeline, connections[i].Value);
                        if (!tmp.IsCreated)
                        {
                            return;
                        }
                        tmp.WriteBytes((byte *)sendBuf.GetUnsafePtr(), sendBuf.Length);

#if UNITY_EDITOR || DEVELOPMENT_BUILD
                        RpcMS1(tmp.Length);
#endif
                        driver.EndSend(tmp);
                        sendBuf.Clear();
                    }
                }
            }
Exemplo n.º 6
0
            public void Execute([ReadOnly] ref NetworkStreamConnection connection,
                                [ReadOnly] ref NetworkSnapshotAckComponent ack, [ReadOnly] ref CommandTargetComponent state)
            {
                if (isNullCommandData && state.targetEntity != Entity.Null)
                {
                    return;
                }
                if (!isNullCommandData && !inputFromEntity.Exists(state.targetEntity))
                {
                    return;
                }
                DataStreamWriter writer = driver.BeginSend(unreliablePipeline, connection.Value);

                if (!writer.IsCreated)
                {
                    return;
                }
                writer.WriteByte((byte)NetworkStreamProtocol.Command);
                writer.WriteUInt(ack.LastReceivedSnapshotByLocal);
                writer.WriteUInt(ack.ReceivedSnapshotByLocalMask);
                writer.WriteUInt(localTime);
                uint returnTime = ack.LastReceivedRemoteTime;

                if (returnTime != 0)
                {
                    returnTime -= (localTime - ack.LastReceiveTimestamp);
                }
                writer.WriteUInt(returnTime);
                writer.WriteUInt(interpolationDelay);
                writer.WriteUInt(inputTargetTick);
                if (state.targetEntity != Entity.Null)
                {
                    var          input = inputFromEntity[state.targetEntity];
                    TCommandData baselineInputData;
                    input.GetDataAtTick(inputTargetTick, out baselineInputData);
                    baselineInputData.Serialize(ref writer);
                    for (uint inputIndex = 1; inputIndex < k_InputBufferSendSize; ++inputIndex)
                    {
                        TCommandData inputData;
                        input.GetDataAtTick(inputTargetTick - inputIndex, out inputData);
                        inputData.Serialize(ref writer, baselineInputData, compressionModel);
                    }

                    writer.Flush();
                }
#if UNITY_EDITOR || DEVELOPMENT_BUILD
                netStats[0] = inputTargetTick;
                netStats[1] = (uint)writer.Length;
#endif

                driver.EndSend(writer);
            }
Exemplo n.º 7
0
    public void Execute(int i)
    {
        SoakMessage inbound  = default(SoakMessage);
        SoakMessage outbound = default(SoakMessage);

        if (connections[i].Connection.IsCreated)
        {
            var ctx = connections[i];
            DataStreamReader  strm;
            NetworkEvent.Type cmd;
            bool close = false;
            while ((cmd = driver.PopEventForConnection(ctx.Connection, out strm)) != NetworkEvent.Type.Empty)
            {
                if (cmd == NetworkEvent.Type.Data)
                {
                    unsafe
                    {
                        strm.ReadBytes(inbound.data, strm.Length);
                        Assert.AreEqual(strm.Length, inbound.length + SoakMessage.HeaderLength);

                        outbound.id       = inbound.id;
                        outbound.sequence = ctx.NextSequenceId++;

                        if (driver.BeginSend(pipeline, connections[i].Connection, out var soakData) == 0)
                        {
                            soakData.WriteBytes(outbound.data, SoakMessage.HeaderLength);
                            driver.EndSend(soakData);
                        }
                    }
                }
                else if (cmd == NetworkEvent.Type.Disconnect)
                {
                    close = true;
                }
            }

            if (close)
            {
                ctx.Connection     = default(NetworkConnection);
                ctx.NextSequenceId = -1;
            }
            connections[i] = ctx;
        }
    }
Exemplo n.º 8
0
        public void Execute(ref PingServerConnectionComponentData connection)
        {
            DataStreamReader strm;

            NetworkEvent.Type cmd;
            while ((cmd = driver.PopEventForConnection(connection.connection, out strm)) != NetworkEvent.Type.Empty)
            {
                if (cmd == NetworkEvent.Type.Data)
                {
                    int id       = strm.ReadInt();
                    var pongData = driver.BeginSend(connection.connection);
                    pongData.WriteInt(id);
                    driver.EndSend(pongData);
                }
                else if (cmd == NetworkEvent.Type.Disconnect)
                {
                    connection = new PingServerConnectionComponentData {
                        connection = default(NetworkConnection)
                    };
                }
            }
        }
Exemplo n.º 9
0
            public unsafe void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
            {
                var entities         = chunk.GetNativeArray(entityType);
                var rpcInBuffer      = chunk.GetBufferAccessor(inBufferType);
                var rpcOutBuffer     = chunk.GetBufferAccessor(outBufferType);
                var connections      = chunk.GetNativeArray(connectionType);
                var deserializeState = new RpcDeserializerState {
                    ghostMap = ghostMap
                };

                for (int i = 0; i < rpcInBuffer.Length; ++i)
                {
                    if (driver.GetConnectionState(connections[i].Value) != NetworkConnection.State.Connected)
                    {
                        continue;
                    }

                    var dynArray   = rpcInBuffer[i];
                    var parameters = new RpcExecutor.Parameters
                    {
                        Reader        = dynArray.AsDataStreamReader(),
                        CommandBuffer = commandBuffer,
                        State         = (IntPtr)UnsafeUtility.AddressOf(ref deserializeState),
                        Connection    = entities[i],
                        JobIndex      = chunkIndex
                    };
                    int msgHeaderLen = dynamicAssemblyList ? 10 : 4;
                    while (parameters.Reader.GetBytesRead() < parameters.Reader.Length)
                    {
                        int rpcIndex = 0;
                        if (dynamicAssemblyList)
                        {
                            ulong rpcHash = parameters.Reader.ReadULong();
                            if (rpcHash == 0)
                            {
                                rpcIndex = ushort.MaxValue;
                                protocolVersion.RpcCollectionVersion       = 0;
                                protocolVersion.ComponentCollectionVersion = 0;
                            }
                            else if (rpcHash != 0 && !hashToIndex.TryGetValue(rpcHash, out rpcIndex))
                            {
                                errors.Enqueue(new RpcReceiveError
                                {
                                    connection = entities[i],
                                    error      = ErrorCodes.InvalidRpc
                                });
                                break;
                            }
                        }
                        else
                        {
                            rpcIndex = parameters.Reader.ReadUShort();
                        }

                        var rpcSize = parameters.Reader.ReadUShort();
                        if (rpcIndex == ushort.MaxValue)
                        {
                            // Special value for NetworkProtocolVersion
                            var netCodeVersion   = parameters.Reader.ReadInt();
                            var gameVersion      = parameters.Reader.ReadInt();
                            var rpcVersion       = parameters.Reader.ReadULong();
                            var componentVersion = parameters.Reader.ReadULong();
                            if (netCodeVersion != protocolVersion.NetCodeVersion ||
                                gameVersion != protocolVersion.GameVersion ||
                                rpcVersion != protocolVersion.RpcCollectionVersion ||
                                componentVersion != protocolVersion.ComponentCollectionVersion)
                            {
                                errors.Enqueue(new RpcReceiveError
                                {
                                    connection = entities[i],
                                    error      = ErrorCodes.ProtocolMismatch
                                });
                                break;
                            }
                            //The connection has received the version. RpcSystem can't accept any rpc's if the NetworkProtocolVersion
                            //has not been received first.
                            var connection = connections[i];
                            connection.ProtocolVersionReceived = 1;
                            connections[i] = connection;
                        }
                        else if (rpcIndex >= execute.Length)
                        {
                            //If this is the server, we must disconnect the connection
                            errors.Enqueue(new RpcReceiveError
                            {
                                connection = entities[i],
                                error      = ErrorCodes.InvalidRpc
                            });
                            break;
                        }
                        else if (connections[i].ProtocolVersionReceived == 0)
                        {
                            errors.Enqueue(new RpcReceiveError
                            {
                                connection = entities[i],
                                error      = ErrorCodes.VersionNotReceived
                            });
                            break;
                        }
                        else
                        {
                            execute[rpcIndex].Execute.Ptr.Invoke(ref parameters);
                        }
                    }

                    dynArray.Clear();

                    var sendBuffer = rpcOutBuffer[i];
                    while (sendBuffer.Length > 0)
                    {
                        if (driver.BeginSend(reliablePipeline, connections[i].Value, out var tmp) == 0)
                        {
                            // If sending failed we stop and wait until next frame
                            if (sendBuffer.Length > tmp.Capacity)
                            {
                                var sendArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray <byte>(sendBuffer.GetUnsafePtr(), sendBuffer.Length, Allocator.Invalid);
    #if ENABLE_UNITY_COLLECTIONS_CHECKS
                                var safety = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(sendBuffer.AsNativeArray());
                                NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref sendArray, safety);
    #endif
                                var reader = new DataStreamReader(sendArray);
                                reader.ReadByte();
                                reader.ReadUShort();
                                var len = reader.ReadUShort() + msgHeaderLen + 1;
                                if (len > tmp.Capacity)
                                {
                                    sendBuffer.Clear();
                                    // Could not fit a single message in the packet, this is a serious error
                                    throw new InvalidOperationException("An RPC was too big to be sent, reduce the size of your RPCs");
                                }
                                tmp.WriteBytes((byte *)sendBuffer.GetUnsafePtr(), len);
                                // Try to fit a few more messages in this packet
                                while (true)
                                {
                                    var subArray = sendArray.GetSubArray(tmp.Length, sendArray.Length - tmp.Length);
                                    reader = new DataStreamReader(subArray);
                                    reader.ReadUShort();
                                    len = reader.ReadUShort() + msgHeaderLen;
                                    if (tmp.Length + len > tmp.Capacity)
                                    {
                                        break;
                                    }
                                    tmp.WriteBytes((byte *)subArray.GetUnsafeReadOnlyPtr(), len);
                                }
                            }
                            else
                            {
                                tmp.WriteBytes((byte *)sendBuffer.GetUnsafePtr(), sendBuffer.Length);
                            }
                            // If sending failed we stop and wait until next frame
                            var result = 0;
                            if ((result = driver.EndSend(tmp)) <= 0)
                            {
                                UnityEngine.Debug.LogWarning($"An error occured during EndSend. ErrorCode: {result}");
                                break;
                            }
                            if (tmp.Length < sendBuffer.Length)
                            {
                                // Compact the buffer, removing the rpcs we did send
                                for (int cpy = tmp.Length; cpy < sendBuffer.Length; ++cpy)
                                {
                                    sendBuffer[1 + cpy - tmp.Length] = sendBuffer[cpy];
                                }
                                // Remove all but the first byte of the data we sent, first byte is identifying the packet as an rpc
                                sendBuffer.ResizeUninitialized(1 + sendBuffer.Length - tmp.Length);
                            }
                            else
                            {
                                sendBuffer.Clear();
                            }
                        }
                    }
                }
            }
Exemplo n.º 10
0
            public unsafe void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
            {
                var entities     = chunk.GetNativeArray(entityType);
                var rpcInBuffer  = chunk.GetBufferAccessor(inBufferType);
                var rpcOutBuffer = chunk.GetBufferAccessor(outBufferType);
                var connections  = chunk.GetNativeArray(connectionType);
                var errors       = new NativeArray <RpcReceiveError>(rpcInBuffer.Length, Allocator.Temp);
                var errorCount   = 0;

                for (int i = 0; i < rpcInBuffer.Length; ++i)
                {
                    if (driver.GetConnectionState(connections[i].Value) != NetworkConnection.State.Connected)
                    {
                        continue;
                    }

                    var dynArray   = rpcInBuffer[i];
                    var parameters = new RpcExecutor.Parameters
                    {
                        Reader        = dynArray.AsDataStreamReader(),
                        CommandBuffer = commandBuffer,
                        Connection    = entities[i],
                        JobIndex      = chunkIndex
                    };
                    while (parameters.Reader.GetBytesRead() < parameters.Reader.Length)
                    {
                        var rpcIndex = parameters.Reader.ReadUShort();
                        if (rpcIndex == ushort.MaxValue)
                        {
                            // Special value for NetworkProtocolVersion
                            var netCodeVersion = parameters.Reader.ReadInt();
                            var gameVersion    = parameters.Reader.ReadInt();
                            var rpcVersion     = parameters.Reader.ReadULong();
                            if (netCodeVersion != protocolVersion.NetCodeVersion ||
                                gameVersion != protocolVersion.GameVersion ||
                                rpcVersion != protocolVersion.RpcCollectionVersion)
                            {
                                commandBuffer.AddComponent(chunkIndex, entities[i], new NetworkStreamRequestDisconnect {
                                    Reason = NetworkStreamDisconnectReason.BadProtocolVersion
                                });
                                errors[errorCount++] = new RpcReceiveError
                                {
                                    connection = entities[i],
                                    error      = ErrorCodes.ProtocolMismatch
                                };
                                break;
                            }
                        }
                        else if (rpcIndex >= execute.Length)
                        {
                            errors[errorCount++] = new RpcReceiveError
                            {
                                connection = entities[i],
                                error      = ErrorCodes.InvalidRpc
                            };
                            break;
                        }
                        else
                        {
                            execute[rpcIndex].Execute.Ptr.Invoke(ref parameters);
                        }
                    }

                    dynArray.Clear();

                    var sendBuffer = rpcOutBuffer[i];
                    if (sendBuffer.Length > 0)
                    {
                        DataStreamWriter tmp = driver.BeginSend(reliablePipeline, connections[i].Value);
                        if (!tmp.IsCreated)
                        {
                            continue;
                        }
                        tmp.WriteBytes((byte *)sendBuffer.GetUnsafePtr(), sendBuffer.Length);
                        driver.EndSend(tmp);
                        sendBuffer.Clear();
                    }
                }

#if ENABLE_UNITY_COLLECTIONS_CHECKS
// TODO: we need to report the errors produced.
                if (errorCount > 0)
                {
                    throw new InvalidOperationException("RpcSystem received malformed packets or packets with the wrong version");
                }
#endif
            }
Exemplo n.º 11
0
            internal unsafe int ProcessPipelineSend(NetworkDriver.Concurrent driver, int startStage, NetworkPipeline pipeline, NetworkConnection connection,
                                                    NetworkInterfaceSendHandle sendHandle, int headerSize, NativeList <UpdatePipeline> currentUpdates)
            {
                int retval = sendHandle.size;
                NetworkPipelineContext ctx = default(NetworkPipelineContext);

                ctx.timestamp = m_timestamp[0];
                var p            = m_Pipelines[pipeline.Id - 1];
                var connectionId = connection.m_NetworkId;

                var resumeQ      = new NativeList <int>(16, Allocator.Temp);
                int resumeQStart = 0;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
                if (headerSize != p.headerCapacity + UnsafeUtility.SizeOf <UdpCHeader>() + 1 && sendHandle.data != IntPtr.Zero)
                {
                    throw new InvalidOperationException("Invalid header size.");
                }
#endif
                var inboundBuffer = default(InboundSendBuffer);
                if (sendHandle.data != IntPtr.Zero)
                {
                    inboundBuffer.bufferWithHeaders       = (byte *)sendHandle.data + UnsafeUtility.SizeOf <UdpCHeader>() + 1;
                    inboundBuffer.bufferWithHeadersLength = sendHandle.size - UnsafeUtility.SizeOf <UdpCHeader>() - 1;
                    inboundBuffer.buffer       = inboundBuffer.bufferWithHeaders + p.headerCapacity;
                    inboundBuffer.bufferLength = inboundBuffer.bufferWithHeadersLength - p.headerCapacity;
                }

                while (true)
                {
                    headerSize = p.headerCapacity;

                    int internalBufferOffset       = p.sendBufferOffset + sizePerConnection[SendSizeOffset] * connectionId;
                    int internalSharedBufferOffset = p.sharedBufferOffset + sizePerConnection[SharedSizeOffset] * connectionId;

                    bool needsUpdate = false;
                    // If this is not the first stage we need to fast forward the buffer offset to the correct place
                    if (startStage > 0)
                    {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                        if (inboundBuffer.bufferWithHeadersLength > 0)
                        {
                            throw new InvalidOperationException("Can't start from a stage with a buffer");
                        }
#endif
                        for (int i = 0; i < startStage; ++i)
                        {
                            internalBufferOffset       += (m_StageCollection[m_StageList[p.FirstStageIndex + i]].SendCapacity + AlignmentMinusOne) & (~AlignmentMinusOne);
                            internalSharedBufferOffset += (m_StageCollection[m_StageList[p.FirstStageIndex + i]].SharedStateCapacity + AlignmentMinusOne) & (~AlignmentMinusOne);
                            headerSize -= m_StageCollection[m_StageList[p.FirstStageIndex + i]].HeaderCapacity;
                        }
                    }

                    for (int i = startStage; i < p.NumStages; ++i)
                    {
                        int stageHeaderCapacity = m_StageCollection[m_StageList[p.FirstStageIndex + i]].HeaderCapacity;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                        if (stageHeaderCapacity > headerSize)
                        {
                            throw new InvalidOperationException("Not enough header space");
                        }
#endif
                        inboundBuffer.headerPadding = headerSize;
                        headerSize -= stageHeaderCapacity;
                        if (stageHeaderCapacity > 0 && inboundBuffer.bufferWithHeadersLength > 0)
                        {
                            var headerArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray <byte>(inboundBuffer.bufferWithHeaders + headerSize, stageHeaderCapacity, Allocator.Invalid);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                            NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref headerArray, AtomicSafetyHandle.GetTempMemoryHandle());
#endif
                            ctx.header = new DataStreamWriter(headerArray);
                        }
                        else
                        {
                            ctx.header = new DataStreamWriter(stageHeaderCapacity, Allocator.Temp);
                        }
                        var prevInbound = inboundBuffer;
                        ProcessSendStage(i, internalBufferOffset, internalSharedBufferOffset, p, ref resumeQ, ref ctx, ref inboundBuffer, ref needsUpdate);

                        if (needsUpdate)
                        {
                            AddSendUpdate(connection, i, pipeline, currentUpdates);
                        }
                        if (inboundBuffer.bufferWithHeadersLength == 0)
                        {
                            break;
                        }

#if ENABLE_UNITY_COLLECTIONS_CHECKS
                        if (inboundBuffer.headerPadding != prevInbound.headerPadding)
                        {
                            throw new InvalidOperationException("Changing the header padding in a pipeline is not supported");
                        }
#endif
                        if (inboundBuffer.buffer != prevInbound.buffer)
                        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                            if (inboundBuffer.buffer != inboundBuffer.bufferWithHeaders + inboundBuffer.headerPadding ||
                                inboundBuffer.bufferLength + inboundBuffer.headerPadding > inboundBuffer.bufferWithHeadersLength)
                            {
                                throw new InvalidOperationException("When creating an internal buffer in piplines the buffer must be a subset of the buffer width headers");
                            }
#endif

                            // Copy header to new buffer so it is part of the payload
                            UnsafeUtility.MemCpy(inboundBuffer.bufferWithHeaders + headerSize, ctx.header.AsNativeArray().GetUnsafeReadOnlyPtr(), ctx.header.Length);
                        }
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                        else
                        {
                            if (inboundBuffer.bufferWithHeaders != prevInbound.bufferWithHeaders)
                            {
                                throw new InvalidOperationException("Changing the send buffer with headers without changing the buffer is not supported");
                            }
                        }
#endif
                        if (ctx.header.Length < stageHeaderCapacity)
                        {
                            int wastedSpace = stageHeaderCapacity - ctx.header.Length;
                            // Remove wasted space in the header
                            UnsafeUtility.MemMove(inboundBuffer.buffer - wastedSpace, inboundBuffer.buffer, inboundBuffer.bufferLength);
                        }

                        // Update the inbound buffer for next iteration
                        inboundBuffer.buffer       = inboundBuffer.bufferWithHeaders + headerSize;
                        inboundBuffer.bufferLength = ctx.header.Length + inboundBuffer.bufferLength;


                        needsUpdate = false;

                        internalBufferOffset       += (ctx.internalProcessBufferLength + AlignmentMinusOne) & (~AlignmentMinusOne);
                        internalSharedBufferOffset += (ctx.internalSharedProcessBufferLength + AlignmentMinusOne) & (~AlignmentMinusOne);
                    }

                    if (inboundBuffer.bufferLength != 0)
                    {
                        if (sendHandle.data != IntPtr.Zero && inboundBuffer.bufferWithHeaders == (byte *)sendHandle.data + UnsafeUtility.SizeOf <UdpCHeader>() + 1)
                        {
                            // Actually send the data - after collapsing it again
                            if (inboundBuffer.buffer != inboundBuffer.bufferWithHeaders)
                            {
                                UnsafeUtility.MemMove(inboundBuffer.bufferWithHeaders, inboundBuffer.buffer, inboundBuffer.bufferLength);
                                inboundBuffer.buffer = inboundBuffer.bufferWithHeaders;
                            }
                            ((byte *)sendHandle.data)[UnsafeUtility.SizeOf <UdpCHeader>()] = (byte)pipeline.Id;
                            int sendSize = UnsafeUtility.SizeOf <UdpCHeader>() + 1 + inboundBuffer.bufferLength;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                            if (sendSize > sendHandle.size)
                            {
                                throw new InvalidOperationException("Pipeline increased the data in the buffer, this is not allowed");
                            }
#endif
                            sendHandle.size = sendSize;
                            retval          = driver.CompleteSend(connection, sendHandle);
                            sendHandle      = default;
                        }
                        else
                        {
                            // Sending without pipeline, the correct pipeline will be added by the default flags when this is called
                            var writer = driver.BeginSend(connection);
                            writer.WriteByte((byte)pipeline.Id);
                            writer.WriteBytes(inboundBuffer.buffer, inboundBuffer.bufferLength);
                            if (writer.HasFailedWrites)
                            {
                                driver.AbortSend(writer);
                            }
                            else
                            {
                                driver.EndSend(writer);
                            }
                        }
                    }

                    if (resumeQStart >= resumeQ.Length)
                    {
                        break;
                    }

                    startStage = resumeQ[resumeQStart++];

                    inboundBuffer = default(InboundSendBuffer);
                }
                if (sendHandle.data != IntPtr.Zero)
                {
                    driver.AbortSend(sendHandle);
                }
                return(retval);
            }