public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) { var simulator = new SimulatorUtility(m_SimulatorParams.MaxPacketCount, m_SimulatorParams.MaxPacketSize, m_SimulatorParams.PacketDelayMs); if (inboundBuffer.buffer1.Length + inboundBuffer.buffer2.Length > m_SimulatorParams.MaxPacketSize) { //UnityEngine.Debug.LogWarning("Incoming packet too large for internal storage buffer. Passing through. [buffer=" + (inboundBuffer.buffer1.Length+inboundBuffer.buffer2.Length) + " packet=" + param->MaxPacketSize + "]"); return(inboundBuffer); } var timestamp = ctx.timestamp; // Packet always delayed atm bool delayPacket = true; // Inbound buffer is empty if this is a resumed receive if (delayPacket && inboundBuffer.buffer1.Length > 0) { if (!simulator.DelayPacket(ref ctx, inboundBuffer, ref needsUpdate, timestamp)) { return(inboundBuffer); } } NativeSlice <byte> returnPacket = default(NativeSlice <byte>); if (simulator.GetDelayedPacket(ref ctx, ref returnPacket, ref needsResume, ref needsUpdate, timestamp)) { inboundBuffer.buffer1 = returnPacket; inboundBuffer.buffer2 = default(NativeSlice <byte>); return(inboundBuffer); } return(default(InboundBufferVec)); }
private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { var sequenceId = (int *)ctx.internalProcessBuffer; ctx.header.WriteUShort((ushort)*sequenceId); *sequenceId = (ushort)(*sequenceId + 1); }
private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { // Request an update to see if a queued packet needs to be resent later or if an ack packet should be sent requests = NetworkPipelineStage.Requests.Update; bool needsResume = false; var header = new ReliableUtility.PacketHeader(); var reliable = (ReliableUtility.Context *)ctx.internalProcessBuffer; needsResume = ReliableUtility.ReleaseOrResumePackets(ctx); if (needsResume) { requests |= NetworkPipelineStage.Requests.Resume; } if (inboundBuffer.bufferLength > 0) { reliable->LastSentTime = ctx.timestamp; ReliableUtility.Write(ctx, inboundBuffer, ref header); ctx.header.WriteBytes((byte *)&header, UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()); if (reliable->Resume != ReliableUtility.NullEntry) { requests |= NetworkPipelineStage.Requests.Resume; } reliable->PreviousTimestamp = ctx.timestamp; return; } if (reliable->Resume != ReliableUtility.NullEntry) { reliable->LastSentTime = ctx.timestamp; inboundBuffer = ReliableUtility.ResumeSend(ctx, out header, ref needsResume); if (needsResume) { requests |= NetworkPipelineStage.Requests.Resume; } ctx.header.Clear(); ctx.header.WriteBytes((byte *)&header, UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()); reliable->PreviousTimestamp = ctx.timestamp; return; } if (ReliableUtility.ShouldSendAck(ctx)) { reliable->LastSentTime = ctx.timestamp; ReliableUtility.WriteAckPacket(ctx, ref header); ctx.header.WriteBytes((byte *)&header, UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()); reliable->PreviousTimestamp = ctx.timestamp; // TODO: Sending dummy byte over since the pipeline won't send an empty payload (ignored on receive) inboundBuffer.bufferWithHeadersLength = inboundBuffer.headerPadding + 1; inboundBuffer.bufferWithHeaders = (byte *)UnsafeUtility.Malloc(inboundBuffer.bufferWithHeadersLength, 8, Allocator.Temp); inboundBuffer.SetBufferFrombufferWithHeaders(); return; } reliable->PreviousTimestamp = ctx.timestamp; }
private static int Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { var sequenceId = (int *)ctx.internalProcessBuffer; ctx.header.WriteUShort((ushort)*sequenceId); *sequenceId = (ushort)(*sequenceId + 1); return((int)Error.StatusCode.Success); }
public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) { // Request an update to see if a queued packet needs to be resent later or if an ack packet should be sent needsUpdate = true; var header = new ReliableUtility.PacketHeader(); unsafe { var reliable = (ReliableUtility.Context *)ctx.internalProcessBuffer.GetUnsafePtr(); needsResume = ReliableUtility.ReleaseOrResumePackets(ctx); if (inboundBuffer.buffer1.Length > 0) { reliable->LastSentTime = ctx.timestamp; ReliableUtility.Write(ctx, inboundBuffer, ref header); ctx.header.WriteBytes((byte *)&header, UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()); if (reliable->Resume != ReliableUtility.NullEntry) { needsResume = true; } reliable->PreviousTimestamp = ctx.timestamp; return(inboundBuffer); } if (reliable->Resume != ReliableUtility.NullEntry) { reliable->LastSentTime = ctx.timestamp; var slice = ReliableUtility.ResumeSend(ctx, out header, ref needsResume); ctx.header.Clear(); ctx.header.WriteBytes((byte *)&header, UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()); inboundBuffer.buffer1 = slice; inboundBuffer.buffer2 = default(NativeSlice <byte>); reliable->PreviousTimestamp = ctx.timestamp; return(inboundBuffer); } if (ReliableUtility.ShouldSendAck(ctx)) { reliable->LastSentTime = ctx.timestamp; ReliableUtility.WriteAckPacket(ctx, ref header); ctx.header.WriteBytes((byte *)&header, UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()); reliable->PreviousTimestamp = ctx.timestamp; // TODO: Sending dummy byte over since the pipeline won't send an empty payload (ignored on receive) inboundBuffer.buffer1 = new NativeSlice <byte>(ctx.internalProcessBuffer, 0, 1); return(inboundBuffer); } reliable->PreviousTimestamp = ctx.timestamp; return(inboundBuffer); } }
public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) { needsResume = false; unsafe { var sequenceId = (int *)ctx.internalProcessBuffer.GetUnsafePtr(); ctx.header.Write((ushort)*sequenceId); *sequenceId = (ushort)(*sequenceId + 1); } return(inboundBuffer); }
public unsafe NativeSlice <byte> Receive(NetworkPipelineContext ctx, NativeSlice <byte> inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) { var param = (SimulatorUtility.Context *)ctx.internalSharedProcessBuffer.GetUnsafePtr(); var simulator = new SimulatorUtility(m_SimulatorParams.MaxPacketCount, m_SimulatorParams.MaxPacketSize, m_SimulatorParams.PacketDelayMs); if (inboundBuffer.Length > m_SimulatorParams.MaxPacketSize) { //UnityEngine.Debug.LogWarning("Incoming packet too large for internal storage buffer. Passing through. [buffer=" + inboundBuffer.Length + " packet=" + param->MaxPacketSize + "]"); // TODO: Add error code for this return(inboundBuffer); } var timestamp = ctx.timestamp; if (inboundBuffer.Length > 0) { param->PacketCount++; } bool delayPacket = param->PacketDelayMs > 0; // Inbound buffer is empty if this is a resumed receive if (delayPacket && inboundBuffer.Length > 0) { var bufferVec = default(InboundBufferVec); bufferVec.buffer1 = inboundBuffer; if (!simulator.DelayPacket(ref ctx, bufferVec, ref needsUpdate, timestamp)) { return(inboundBuffer); } } if (simulator.ShouldDropPacket(param, m_SimulatorParams, timestamp)) { param->PacketDropCount++; return(new NativeSlice <byte>()); } NativeSlice <byte> returnPacket = default(NativeSlice <byte>); if (simulator.GetDelayedPacket(ref ctx, ref returnPacket, ref needsResume, ref needsUpdate, timestamp)) { return(returnPacket); } // Pass packet through, nothing was done with it. Or return empty buffer if no inbound buffer is here (then nothing is being resurrected) if (!delayPacket && inboundBuffer.Length > 0) { return(inboundBuffer); } return(new NativeSlice <byte>()); }
private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { var context = (SimulatorUtility.Context *)ctx.internalSharedProcessBuffer; var param = *(SimulatorUtility.Parameters *)ctx.staticInstanceBuffer; var simulator = new SimulatorUtility(param.MaxPacketCount, param.MaxPacketSize, param.PacketDelayMs, param.PacketJitterMs); if (inboundBuffer.bufferLength > param.MaxPacketSize) { //UnityEngine.Debug.LogWarning("Incoming packet too large for internal storage buffer. Passing through. [buffer=" + inboundBuffer.Length + " packet=" + param->MaxPacketSize + "]"); // TODO: Add error code for this return; } var timestamp = ctx.timestamp; // Inbound buffer is empty if this is a resumed receive if (inboundBuffer.bufferLength > 0) { context->PacketCount++; if (simulator.ShouldDropPacket(context, param, timestamp)) { context->PacketDropCount++; inboundBuffer = default; return; } var bufferVec = default(InboundSendBuffer); bufferVec.bufferWithHeaders = inboundBuffer.buffer; bufferVec.bufferWithHeadersLength = inboundBuffer.bufferLength; bufferVec.buffer = inboundBuffer.buffer; bufferVec.bufferLength = inboundBuffer.bufferLength; bufferVec.headerPadding = 0; if (context->PacketDelayMs == 0 || !simulator.DelayPacket(ref ctx, bufferVec, ref requests, timestamp)) { return; } } InboundSendBuffer returnPacket = default; if (simulator.GetDelayedPacket(ref ctx, ref returnPacket, ref requests, timestamp)) { inboundBuffer.buffer = returnPacket.bufferWithHeaders; inboundBuffer.bufferLength = returnPacket.bufferWithHeadersLength; return; } inboundBuffer = default; }
private static int Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { var context = (SimulatorUtility.Context *)ctx.internalSharedProcessBuffer; var param = *(SimulatorUtility.Parameters *)ctx.staticInstanceBuffer; var simulator = new SimulatorUtility(param.MaxPacketCount, param.MaxPacketSize, param.PacketDelayMs, param.PacketJitterMs); if (inboundBuffer.headerPadding + inboundBuffer.bufferLength > param.MaxPacketSize) { //UnityEngine.Debug.LogWarning("Incoming packet too large for internal storage buffer. Passing through. [buffer=" + (inboundBuffer.headerPadding+inboundBuffer.buffer.Length) + " packet=" + param.MaxPacketSize + "]"); return((int)Error.StatusCode.NetworkPacketOverflow); } var timestamp = ctx.timestamp; if (inboundBuffer.bufferLength > 0) { context->PacketCount++; if (simulator.ShouldDropPacket(context, param, timestamp)) { context->PacketDropCount++; inboundBuffer = default; return((int)Error.StatusCode.Success); } if (context->FuzzFactor > 0) { simulator.FuzzPacket(context, ref inboundBuffer); } if (context->PacketDelayMs == 0 || !simulator.DelayPacket(ref ctx, inboundBuffer, ref requests, timestamp)) { return((int)Error.StatusCode.Success); } } InboundSendBuffer returnPacket = default; if (simulator.GetDelayedPacket(ref ctx, ref returnPacket, ref requests, timestamp)) { inboundBuffer = returnPacket; return((int)Error.StatusCode.Success); } inboundBuffer = default; return((int)Error.StatusCode.Success); }
public NativeSlice <byte> Receive(NetworkPipelineContext ctx, NativeSlice <byte> inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) { needsResume = false; var reader = new DataStreamReader(inboundBuffer); var context = default(DataStreamReader.Context); unsafe { var oldSequenceId = (int *)ctx.internalProcessBuffer.GetUnsafePtr(); ushort sequenceId = reader.ReadUShort(ref context); if (SequenceHelpers.GreaterThan16(sequenceId, (ushort)*oldSequenceId)) { *oldSequenceId = sequenceId; // Skip over the part of the buffer which contains the header return(inboundBuffer.Slice(sizeof(ushort))); } } return(default(NativeSlice <byte>)); }
public InboundBufferVec InvokeSend(int pipelineStageId, NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) { switch (pipelineStageId) { case 0: return(m_SimulatorPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate)); case 1: return(m_SimulatorPipelineStageInSend.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate)); case 2: return(m_NullPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate)); case 3: return(m_UnreliableSequencedPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate)); case 4: return(m_ReliableSequencedPipelineStage.Send(ctx, inboundBuffer, ref needsResume, ref needsUpdate)); } return(inboundBuffer); }
private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { var inboundArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray <byte>(inboundBuffer.buffer, inboundBuffer.bufferLength, Allocator.Invalid); #if ENABLE_UNITY_COLLECTIONS_CHECKS var safetyHandle = AtomicSafetyHandle.GetTempMemoryHandle(); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref inboundArray, safetyHandle); #endif var reader = new DataStreamReader(inboundArray); var oldSequenceId = (int *)ctx.internalProcessBuffer; ushort sequenceId = reader.ReadUShort(); if (SequenceHelpers.GreaterThan16(sequenceId, (ushort)*oldSequenceId)) { *oldSequenceId = sequenceId; // Skip over the part of the buffer which contains the header inboundBuffer = inboundBuffer.Slice(sizeof(ushort)); return; } inboundBuffer = default; }
public NativeSlice <byte> InvokeReceive(int pipelineStageId, NetworkPipelineContext ctx, NativeSlice <byte> inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) { switch (pipelineStageId) { case 0: return(m_SimulatorPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate)); case 1: return(m_SimulatorPipelineStageInSend.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate)); case 2: return(m_NullPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate)); case 3: return(m_UnreliableSequencedPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate)); case 4: return(m_ReliableSequencedPipelineStage.Receive(ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate)); } return(inboundBuffer); }
public unsafe NativeSlice <byte> Receive(NetworkPipelineContext ctx, NativeSlice <byte> inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) { var param = (SimulatorUtility.Context *)ctx.internalSharedProcessBuffer.GetUnsafePtr(); var simulator = new SimulatorUtility(m_SimulatorParams.MaxPacketCount, m_SimulatorParams.MaxPacketSize, m_SimulatorParams.PacketDelayMs, m_SimulatorParams.PacketJitterMs); if (inboundBuffer.Length > m_SimulatorParams.MaxPacketSize) { //UnityEngine.Debug.LogWarning("Incoming packet too large for internal storage buffer. Passing through. [buffer=" + inboundBuffer.Length + " packet=" + param->MaxPacketSize + "]"); // TODO: Add error code for this return(inboundBuffer); } var timestamp = ctx.timestamp; // Inbound buffer is empty if this is a resumed receive if (inboundBuffer.Length > 0) { param->PacketCount++; if (simulator.ShouldDropPacket(param, m_SimulatorParams, timestamp)) { param->PacketDropCount++; return(default);
private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { // Request a send update to see if a queued packet needs to be resent later or if an ack packet should be sent requests = NetworkPipelineStage.Requests.SendUpdate; bool needsResume = false; var header = default(ReliableUtility.PacketHeader); var slice = default(InboundRecvBuffer); ReliableUtility.Context * reliable = (ReliableUtility.Context *)ctx.internalProcessBuffer; ReliableUtility.SharedContext *shared = (ReliableUtility.SharedContext *)ctx.internalSharedProcessBuffer; shared->errorCode = 0; if (reliable->Resume == ReliableUtility.NullEntry) { if (inboundBuffer.bufferLength <= 0) { inboundBuffer = slice; return; } var inboundArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray <byte>(inboundBuffer.buffer, inboundBuffer.bufferLength, Allocator.Invalid); #if ENABLE_UNITY_COLLECTIONS_CHECKS var safetyHandle = AtomicSafetyHandle.GetTempMemoryHandle(); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref inboundArray, safetyHandle); #endif var reader = new DataStreamReader(inboundArray); reader.ReadBytes((byte *)&header, UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()); if (header.Type == (ushort)ReliableUtility.PacketType.Ack) { ReliableUtility.ReadAckPacket(ctx, header); inboundBuffer = default; return; } var result = ReliableUtility.Read(ctx, header); if (result >= 0) { var nextExpectedSequenceId = (ushort)(reliable->Delivered + 1); if (result == nextExpectedSequenceId) { reliable->Delivered = result; slice = inboundBuffer.Slice(UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()); if (needsResume = SequenceHelpers.GreaterThan16((ushort)shared->ReceivedPackets.Sequence, (ushort)result)) { reliable->Resume = (ushort)(result + 1); } } else { ReliableUtility.SetPacket(ctx.internalProcessBuffer, result, inboundBuffer.Slice(UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>())); slice = ReliableUtility.ResumeReceive(ctx, reliable->Delivered + 1, ref needsResume); } } } else { slice = ReliableUtility.ResumeReceive(ctx, reliable->Resume, ref needsResume); } if (needsResume) { requests |= NetworkPipelineStage.Requests.Resume; } inboundBuffer = slice; }
private static int Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { var fragContext = (FragContext *)ctx.internalProcessBuffer; var dataBuffer = ctx.internalProcessBuffer + sizeof(FragContext); var param = (FragmentationUtility.Parameters *)ctx.staticInstanceBuffer; FragFlags flags = FragFlags.First; int headerCapacity = ctx.header.Capacity; var systemHeaderCapacity = sizeof(UdpCHeader) + 1 + 2; // Extra byte is for pipeline id, two bytes for footer var maxBlockLength = NetworkParameterConstants.MTU - systemHeaderCapacity - inboundBuffer.headerPadding; var maxBlockLengthFirstPacket = maxBlockLength - ctx.accumulatedHeaderCapacity; // The first packet has the headers for all pipeline stages before this one if (fragContext->endIndex > fragContext->startIndex) { var isResume = 0 == inboundBuffer.bufferLength; if (!isResume) { throw new InvalidOperationException("Internal error: we encountered data in the fragmentation buffer, but this is not a resume call."); } // We have data left over from a previous call flags &= ~FragFlags.First; var blockLength = fragContext->endIndex - fragContext->startIndex; if (blockLength > maxBlockLength) { blockLength = maxBlockLength; } var blockStart = dataBuffer + fragContext->startIndex; inboundBuffer.buffer = blockStart; inboundBuffer.bufferWithHeaders = blockStart - inboundBuffer.headerPadding; inboundBuffer.bufferLength = blockLength; inboundBuffer.bufferWithHeadersLength = blockLength + inboundBuffer.headerPadding; fragContext->startIndex += blockLength; } else if (inboundBuffer.bufferLength > maxBlockLengthFirstPacket) { var payloadCapacity = param->PayloadCapacity; var excessLength = inboundBuffer.bufferLength - maxBlockLengthFirstPacket; var excessStart = inboundBuffer.buffer + maxBlockLengthFirstPacket; if (excessLength + inboundBuffer.headerPadding > payloadCapacity) { throw new InvalidOperationException($"Fragmentation capacity exceeded. Capacity:{payloadCapacity} Payload:{excessLength + inboundBuffer.headerPadding}"); } UnsafeUtility.MemCpy(dataBuffer + inboundBuffer.headerPadding, excessStart, excessLength); fragContext->startIndex = inboundBuffer.headerPadding; // Leaving room for header fragContext->endIndex = excessLength + inboundBuffer.headerPadding; inboundBuffer.bufferWithHeadersLength -= excessLength; inboundBuffer.bufferLength -= excessLength; } if (fragContext->endIndex > fragContext->startIndex) { requests |= NetworkPipelineStage.Requests.Resume; } else { flags |= FragFlags.Last; } var sequence = fragContext->sequence++; var combined = (sequence & (int)FragFlags.SeqMask) | (int)flags; // lower 14 bits sequence, top 2 bits flags ctx.header.WriteShort((short)combined); #if FRAGMENTATION_DEBUG // For debugging - this allows WireShark to identify fragmentation packets ctx.header.WriteByte((byte)'@'); ctx.header.WriteByte((byte)'@'); ctx.header.WriteByte((byte)'@'); ctx.header.WriteByte((byte)'@'); #endif return((int)Error.StatusCode.Success); }
public InboundBufferVec Send(NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) { return(inboundBuffer); }
public NativeSlice <byte> Receive(NetworkPipelineContext ctx, NativeSlice <byte> inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) { return(new NativeSlice <byte>(inboundBuffer, 0, inboundBuffer.Length)); }
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); }
public InboundBufferVec InvokeSend(int pipelineStageId, NetworkPipelineContext ctx, InboundBufferVec inboundBuffer, ref bool needsResume, ref bool needsUpdate) { return(inboundBuffer); }
public NativeSlice <byte> InvokeReceive(int pipelineStageId, NetworkPipelineContext ctx, NativeSlice <byte> inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) { return(inboundBuffer); }
private static void Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { }
private static int Send(ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { return((int)Error.StatusCode.Success); }
private unsafe void ProcessSendStage(int startStage, int internalBufferOffset, int internalSharedBufferOffset, PipelineImpl p, ref NativeList <int> resumeQ, ref NetworkPipelineContext ctx, ref InboundSendBuffer inboundBuffer, ref bool needsUpdate) { var pipelineStage = m_StageCollection[m_StageList[p.FirstStageIndex + startStage]]; ctx.staticInstanceBuffer = (byte *)m_StaticInstanceBuffer.GetUnsafeReadOnlyPtr() + pipelineStage.StaticStateStart; ctx.staticInstanceBufferLength = pipelineStage.StaticStateCapcity; ctx.internalProcessBuffer = (byte *)sendBuffer.GetUnsafeReadOnlyPtr() + internalBufferOffset; ctx.internalProcessBufferLength = pipelineStage.SendCapacity; ctx.internalSharedProcessBuffer = (byte *)sharedBuffer.GetUnsafeReadOnlyPtr() + internalSharedBufferOffset; ctx.internalSharedProcessBufferLength = pipelineStage.SharedStateCapacity; NetworkPipelineStage.Requests requests = NetworkPipelineStage.Requests.None; pipelineStage.Send.Ptr.Invoke(ref ctx, ref inboundBuffer, ref requests); if ((requests & NetworkPipelineStage.Requests.Resume) != 0) { resumeQ.Add(startStage); } needsUpdate = (requests & NetworkPipelineStage.Requests.Update) != 0; }
internal unsafe void ProcessPipelineSend <T>(T driver, int startStage, NetworkPipeline pipeline, NetworkConnection connection, NativeSlice <byte> payloadBuffer, NativeList <UpdatePipeline> currentUpdates) where T : struct, INetworkPipelineSender { 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; ctx.header = new DataStreamWriter(p.headerCapacity, Allocator.Temp); var inboundBuffer = default(InboundBufferVec); inboundBuffer.buffer1 = payloadBuffer; var prevHeader = new DataStreamWriter(p.headerCapacity, Allocator.Temp); while (true) { 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) { for (int i = 0; i < startStage; ++i) { internalBufferOffset += m_StageCollection.GetSendCapacity(m_StageList[p.FirstStageIndex + i]); internalSharedBufferOffset += m_StageCollection.GetSharedStateCapacity(m_StageList[p.FirstStageIndex + i]); } } for (int i = startStage; i < p.NumStages; ++i) { var prevInbound = inboundBuffer; ProcessSendStage(i, internalBufferOffset, internalSharedBufferOffset, p, ref resumeQ, ref ctx, ref inboundBuffer, ref needsUpdate); if (inboundBuffer.buffer1 == prevInbound.buffer1 && inboundBuffer.buffer2 == prevInbound.buffer2) { if (ctx.header.Length > 0) { if (prevHeader.Length > 0) { ctx.header.WriteBytes(prevHeader.GetUnsafeReadOnlyPtr(), prevHeader.Length); } prevHeader.Clear(); var tempHeader = ctx.header; ctx.header = prevHeader; prevHeader = tempHeader; if (inboundBuffer.buffer2.Length == 0) { inboundBuffer.buffer2 = inboundBuffer.buffer1; } inboundBuffer.buffer1 = prevHeader.GetNativeSlice(0, prevHeader.Length); } } else { #if ENABLE_UNITY_COLLECTIONS_CHECKS if (inboundBuffer.buffer2.Length > 0) { throw new InvalidOperationException("Pipeline send stages must return either the unmodified inbound buffers or a consolidated version with a single buffer"); } #endif // Prev header is now part of payload prevHeader.Clear(); if (ctx.header.Length > 0) { var tempHeader = ctx.header; ctx.header = prevHeader; prevHeader = tempHeader; inboundBuffer.buffer2 = inboundBuffer.buffer1; inboundBuffer.buffer1 = prevHeader.GetNativeSlice(0, prevHeader.Length); } } if (needsUpdate) { AddSendUpdate(connection, i, pipeline, currentUpdates); } if (inboundBuffer.buffer1.Length == 0) { break; } needsUpdate = false; internalBufferOffset += ctx.internalProcessBuffer.Length; internalSharedBufferOffset += ctx.internalSharedProcessBuffer.Length; } if (inboundBuffer.buffer1.Length != 0) { var iov = stackalloc network_iovec[4]; var pipelineId = pipeline.Id; iov[0].buf = &pipelineId; iov[0].len = 1; iov[1].buf = ctx.header.GetUnsafePtr(); iov[1].len = ctx.header.Length; iov[2].buf = inboundBuffer.buffer1.GetUnsafeReadOnlyPtr(); iov[2].len = inboundBuffer.buffer1.Length; if (inboundBuffer.buffer2.Length > 0) { iov[3].buf = inboundBuffer.buffer2.GetUnsafeReadOnlyPtr(); iov[3].len = inboundBuffer.buffer2.Length; // FIXME: handle send errors driver.Send(connection, iov, 4); } else { driver.Send(connection, iov, 3); } } if (resumeQStart >= resumeQ.Length) { break; } startStage = resumeQ[resumeQStart++]; prevHeader.Clear(); inboundBuffer = default(InboundBufferVec); } }
private void ProcessReceiveStage(int stage, NetworkPipeline pipeline, int internalBufferOffset, int internalSharedBufferOffset, ref NetworkPipelineContext ctx, ref NativeSlice <byte> inboundBuffer, ref NativeList <int> resumeQ, ref bool needsUpdate, ref bool needsSendUpdate) { bool needsResume = false; var p = m_Pipelines[pipeline.Id - 1]; ctx.internalProcessBuffer = new NativeSlice <byte>(m_ReceiveBuffer, internalBufferOffset, m_StageCollection.GetReceiveCapacity(m_StageList[p.FirstStageIndex + stage])); ctx.internalSharedProcessBuffer = new NativeSlice <byte>(m_SharedBuffer, internalSharedBufferOffset, m_StageCollection.GetSharedStateCapacity(m_StageList[p.FirstStageIndex + stage])); var stageId = m_StageList[p.FirstStageIndex + stage]; inboundBuffer = m_StageCollection.InvokeReceive(stageId, ctx, inboundBuffer, ref needsResume, ref needsUpdate, ref needsSendUpdate); if (needsResume) { resumeQ.Add(stage); } }
private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests) { var fragContext = (FragContext *)ctx.internalProcessBuffer; var dataBuffer = ctx.internalProcessBuffer + sizeof(FragContext); var param = (FragmentationUtility.Parameters *)ctx.staticInstanceBuffer; var inboundArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray <byte>(inboundBuffer.buffer, inboundBuffer.bufferLength, Allocator.Invalid); #if ENABLE_UNITY_COLLECTIONS_CHECKS var safetyHandle = AtomicSafetyHandle.GetTempMemoryHandle(); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref inboundArray, safetyHandle); #endif var reader = new DataStreamReader(inboundArray); var combined = reader.ReadShort(); var foundSequence = combined & (int)FragFlags.SeqMask; var flags = (FragFlags)combined & ~FragFlags.SeqMask; inboundBuffer = inboundBuffer.Slice(FragHeaderCapacity); var expectedSequence = fragContext->sequence; var isFirst = 0 != (flags & FragFlags.First); var isLast = 0 != (flags & FragFlags.Last); if (isFirst) { expectedSequence = foundSequence; fragContext->packetError = false; fragContext->endIndex = 0; } if (foundSequence != expectedSequence) { // We've missed a packet. fragContext->packetError = true; fragContext->endIndex = 0; // Discard data we have already collected } if (!fragContext->packetError) { if (!isLast || fragContext->endIndex > 0) { if (fragContext->endIndex + inboundBuffer.bufferLength > param->PayloadCapacity) { throw new InvalidOperationException($"Fragmentation capacity exceeded"); } // Append the data to the end UnsafeUtility.MemCpy(dataBuffer + fragContext->endIndex, inboundBuffer.buffer, inboundBuffer.bufferLength); fragContext->endIndex += inboundBuffer.bufferLength; } if (isLast && fragContext->endIndex > 0) { // Data is complete inboundBuffer = new InboundRecvBuffer { buffer = dataBuffer, bufferLength = fragContext->endIndex }; } } if (!isLast || fragContext->packetError) { // No output if we expect more data, or if data is incomplete due to packet loss inboundBuffer = default; } fragContext->sequence = (foundSequence + 1) & (int)FragFlags.SeqMask; }
private void ProcessSendStage(int startStage, int internalBufferOffset, int internalSharedBufferOffset, PipelineImpl p, ref NativeList <int> resumeQ, ref NetworkPipelineContext ctx, ref InboundBufferVec inboundBuffer, ref bool needsUpdate) { bool needsResume = false; ctx.internalProcessBuffer = Unsafe_GetSliceFromReadOnlyArray(sendBuffer, internalBufferOffset, m_StageCollection.GetSendCapacity(m_StageList[p.FirstStageIndex + startStage])); ctx.internalSharedProcessBuffer = Unsafe_GetSliceFromReadOnlyArray(sharedBuffer, internalSharedBufferOffset, m_StageCollection.GetSharedStateCapacity(m_StageList[p.FirstStageIndex + startStage])); inboundBuffer = m_StageCollection.InvokeSend(m_StageList[p.FirstStageIndex + startStage], ctx, inboundBuffer, ref needsResume, ref needsUpdate); if (needsResume) { resumeQ.Add(startStage); } }
private void ProcessReceiveStagesFrom <T>(T driver, int startStage, NetworkPipeline pipeline, NetworkConnection connection, NativeSlice <byte> buffer) where T : struct, INetworkPipelineReceiver { var p = m_Pipelines[pipeline.Id - 1]; var connectionId = connection.m_NetworkId; var resumeQ = new NativeList <int>(16, Allocator.Temp); int resumeQStart = 0; NetworkPipelineContext ctx = default(NetworkPipelineContext); ctx.timestamp = Timestamp; var inboundBuffer = new NativeSlice <byte>(buffer, 0, buffer.Length); ctx.header = default(DataStreamWriter); NativeList <UpdatePipeline> sendUpdates = new NativeList <UpdatePipeline>(128, Allocator.Temp); while (true) { bool needsUpdate = false; bool needsSendUpdate = false; int internalBufferOffset = p.receiveBufferOffset + sizePerConnection[RecveiveSizeOffset] * connectionId; int internalSharedBufferOffset = p.sharedBufferOffset + sizePerConnection[SharedSizeOffset] * connectionId; // Adjust offset accounting for stages in front of the starting stage, since we're parsing the stages in reverse order for (int st = 0; st < startStage; ++st) { internalBufferOffset += m_StageCollection.GetReceiveCapacity(m_StageList[p.FirstStageIndex + st]); internalSharedBufferOffset += m_StageCollection.GetSharedStateCapacity(m_StageList[p.FirstStageIndex + st]); } for (int i = startStage; i >= 0; --i) { ProcessReceiveStage(i, pipeline, internalBufferOffset, internalSharedBufferOffset, ref ctx, ref inboundBuffer, ref resumeQ, ref needsUpdate, ref needsSendUpdate); if (needsUpdate) { var newUpdate = new UpdatePipeline { connection = connection, stage = i, pipeline = pipeline }; bool uniqueItem = true; for (int j = 0; j < m_ReceiveStageNeedsUpdate.Length; ++j) { if (m_ReceiveStageNeedsUpdate[j].stage == newUpdate.stage && m_ReceiveStageNeedsUpdate[j].pipeline.Id == newUpdate.pipeline.Id && m_ReceiveStageNeedsUpdate[j].connection == newUpdate.connection) { uniqueItem = false; } } if (uniqueItem) { m_ReceiveStageNeedsUpdate.Add(newUpdate); } } if (needsSendUpdate) { AddSendUpdate(connection, i, pipeline, m_SendStageNeedsUpdate); } if (inboundBuffer.Length == 0) { break; } // Offset needs to be adjusted for the next pipeline (the one in front of this one) if (i > 0) { internalBufferOffset -= m_StageCollection.GetReceiveCapacity(m_StageList[p.FirstStageIndex + i - 1]); internalSharedBufferOffset -= m_StageCollection.GetSharedStateCapacity(m_StageList[p.FirstStageIndex + i - 1]); } needsUpdate = false; } if (inboundBuffer.Length != 0) { driver.PushDataEvent(connection, inboundBuffer); } if (resumeQStart >= resumeQ.Length) { return; } startStage = resumeQ[resumeQStart++]; inboundBuffer = default(NativeSlice <byte>); } }
public NativeSlice <byte> Receive(NetworkPipelineContext ctx, NativeSlice <byte> inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate) { needsResume = false; // Request a send update to see if a queued packet needs to be resent later or if an ack packet should be sent needsSendUpdate = true; var context = default(DataStreamReader.Context); var header = default(ReliableUtility.PacketHeader); var slice = default(NativeSlice <byte>); unsafe { ReliableUtility.Context * reliable = (ReliableUtility.Context *)ctx.internalProcessBuffer.GetUnsafePtr(); ReliableUtility.SharedContext *shared = (ReliableUtility.SharedContext *)ctx.internalSharedProcessBuffer.GetUnsafePtr(); shared->errorCode = 0; if (reliable->Resume == ReliableUtility.NullEntry) { if (inboundBuffer.Length <= 0) { return(slice); } var reader = new DataStreamReader(inboundBuffer); reader.ReadBytes(ref context, (byte *)&header, UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()); if (header.Type == (ushort)ReliableUtility.PacketType.Ack) { ReliableUtility.ReadAckPacket(ctx, header); inboundBuffer = new NativeSlice <byte>(); return(inboundBuffer); } var result = ReliableUtility.Read(ctx, header); if (result >= 0) { var nextExpectedSequenceId = (ushort)(reliable->Delivered + 1); if (result == nextExpectedSequenceId) { reliable->Delivered = result; slice = inboundBuffer.Slice(UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()); if (needsResume = SequenceHelpers.GreaterThan16((ushort)shared->ReceivedPackets.Sequence, (ushort)result)) { reliable->Resume = (ushort)(result + 1); } } else { ReliableUtility.SetPacket(ctx.internalProcessBuffer, result, inboundBuffer.Slice(UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>())); slice = ReliableUtility.ResumeReceive(ctx, reliable->Delivered + 1, ref needsResume); } } } else { slice = ReliableUtility.ResumeReceive(ctx, reliable->Resume, ref needsResume); } } return(slice); }