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); }
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); } } }
// -- 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; } } } }
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(); } } }
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); }
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; } }
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) }; } } }
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(); } } } } }
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 }
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); }