public unsafe void Assert_GotConnectionRequest(NetworkEndPoint from, bool accept = false) { int length; NetworkEndPoint remote; m_LocalDataStream.Clear(); Assert.True( IPCManager.Instance.PeekNext(Address, m_LocalDataStream.GetUnsafePtr(), out length, out remote) >= sizeof(UdpCHeader)); m_LocalDataStream.WriteBytesWithUnsafePointer(length); UdpCHeader header = new UdpCHeader(); var reader = new DataStreamReader(m_LocalDataStream, 0, sizeof(UdpCHeader)); var readerCtx = default(DataStreamReader.Context); Assert.True(reader.IsCreated); reader.ReadBytes(ref readerCtx, header.Data, sizeof(UdpCHeader)); Assert.True(header.Type == (int)UdpCProtocol.ConnectionRequest); Assert.True(remote.Family == NetworkFamily.IPC); //Assert.True(remote.ipc_handle == from.ipc_handle); Assert.True(remote.Port == from.Port); if (accept) { m_LocalDriver.ScheduleUpdate().Complete(); var con = m_LocalDriver.Accept(); ClientConnections.Add(con); Assert.True(con != default(NetworkConnection)); } }
public IEnumerator ServerAndClient_PingPong_Successfully() { SetupServerAndClientAndConnectThem(0); //send data from client DataStreamWriter m_OutStream = new DataStreamWriter(16, Allocator.Persistent); m_OutStream.Clear(); m_OutStream.Write(SharedConstants.ping); clientToServerConnection.Send(client_driver, m_OutStream); //handle sent data server_driver.ScheduleUpdate().Complete(); ev = server_driver.PopEventForConnection(connectionToClient, out stream); Assert.IsTrue(ev == NetworkEvent.Type.Data, "Expected to get Type.Data"); var readerCtx = default(DataStreamReader.Context); var msg = stream.ReadBytesAsArray(ref readerCtx, stream.Length); if (msg.Length == SharedConstants.ping.Length) { for (var i = 0; i < msg.Length; i++) { if (SharedConstants.ping[i] != msg[i]) { Assert.Fail("Data reading error"); } } } client_driver.ScheduleUpdate().Complete(); //send data from server m_OutStream.Clear(); m_OutStream.Write(SharedConstants.pong); connectionToClient.Send(server_driver, m_OutStream); m_OutStream.Dispose(); //handle sent data server_driver.ScheduleUpdate().Complete(); client_driver.ScheduleUpdate().Complete(); ev = clientToServerConnection.PopEvent(client_driver, out stream); Assert.IsTrue(ev == NetworkEvent.Type.Data, "Expected to get Type.Data"); readerCtx = default(DataStreamReader.Context); msg = stream.ReadBytesAsArray(ref readerCtx, stream.Length); if (msg.Length == SharedConstants.pong.Length) { for (var i = 0; i < msg.Length; i++) { if (SharedConstants.pong[i] != msg[i]) { Assert.Fail("Data reading error"); } } } DisconnectAndCleanup(); yield return(null); }
public IEnumerator ServerAndClient_SendMessageWithMoreThenMaxLength_OverflowsIncomingDriverBuffer() { SetupServerAndClientAndConnectThem(0); //send data from client DataStreamWriter m_OutStream = new DataStreamWriter(1500, Allocator.Persistent); m_OutStream.Clear(); int messageLength = 1401; byte[] messageToSend = new byte[messageLength]; for (int i = 0; i < messageLength; i++) { messageToSend[i] = (byte)(33 + (i % 93)); } m_OutStream.Write(messageToSend); clientToServerConnection.Send(client_driver, m_OutStream); LogAssert.Expect(LogType.Error, "Error on receive 10040"); m_OutStream.Dispose(); //handle sent data server_driver.ScheduleUpdate().Complete(); client_driver.ScheduleUpdate().Complete(); Assert.AreEqual(10040, server_driver.ReceiveErrorCode); DisconnectAndCleanup(); yield return(null); }
public void SendDataToRemoteEndPoint() { using (var host = new LocalDriverHelper(default(NetworkEndPoint))) using (var stream = new DataStreamWriter(64, Allocator.Persistent)) { host.Host(); var driver = new LocalNetworkDriver(new NetworkDataStreamParameter { size = 64 }); // Need to be connected in order to be able to send a disconnect packet. NetworkConnection connectionId = driver.Connect(host.Address); Assert.True(connectionId != default(NetworkConnection)); driver.ScheduleUpdate().Complete(); var local = driver.LocalEndPoint(); host.Assert_GotConnectionRequest(local, true); NetworkConnection con; DataStreamReader slice; // Pump so we get the accept message back. driver.ScheduleUpdate().Complete(); Assert.AreEqual(NetworkEvent.Type.Connect, driver.PopEvent(out con, out slice)); stream.Clear(); var data = Encoding.ASCII.GetBytes("data to send"); stream.Write(data); driver.Send(connectionId, stream); driver.ScheduleUpdate().Complete(); host.Assert_GotDataRequest(local, data); driver.Dispose(); } }
public IEnumerator ServerAndClient_SendMessageWithMaxLength_SentAndReceivedWithoutErrors() { SetupServerAndClientAndConnectThem(0); //send data from client DataStreamWriter m_OutStream = new DataStreamWriter(1500, Allocator.Persistent); m_OutStream.Clear(); int messageLength = 1400; byte[] messageToSend = new byte[messageLength]; for (int i = 0; i < messageLength; i++) { messageToSend[i] = (byte)(33 + (i % 93)); } m_OutStream.Write(messageToSend); clientToServerConnection.Send(client_driver, m_OutStream); m_OutStream.Dispose(); server_driver.ScheduleUpdate().Complete(); ev = server_driver.PopEventForConnection(connectionToClient, out stream); Assert.IsTrue(ev == NetworkEvent.Type.Data, "Expected to get Type.Data"); var readerCtx = default(DataStreamReader.Context); var msg = stream.ReadBytesAsArray(ref readerCtx, stream.Length); Assert.IsTrue(msg.Length == messageLength, "Lenghts of sent and received messages are different"); DisconnectAndCleanup(); yield return(null); }
public IEnumerator ServerAndClient_PingPong_Successfully() { SetupServerAndClientAndConnectThem(0); //send data from client DataStreamWriter m_OutStream = client_driver.BeginSend(clientToServerConnection); m_OutStream.Clear(); m_OutStream.WriteBytes(new NativeArray <byte>(SharedConstants.ping, Allocator.Temp)); client_driver.EndSend(m_OutStream); client_driver.ScheduleFlushSend(default).Complete();
private void TestPipeline(int packetCount, NetworkPipeline serverPipe, int packetDelay = 100) { // Connect to server var clientToServer = m_ClientDriver.Connect(m_ServerDriver.LocalEndPoint()); var clientToServer2 = m_ClientDriver2.Connect(m_ServerDriver.LocalEndPoint()); Assert.AreNotEqual(default(NetworkConnection), clientToServer); Assert.AreNotEqual(default(NetworkConnection), clientToServer2); m_ClientDriver.ScheduleUpdate().Complete(); m_ClientDriver2.ScheduleUpdate().Complete(); // Driver only updates time in update, so must read start time before update var startTime = Stopwatch.GetTimestamp() / TimeSpan.TicksPerMillisecond; // Handle incoming connection from client m_ServerDriver.ScheduleUpdate().Complete(); var serverToClient = m_ServerDriver.Accept(); Assert.AreNotEqual(default(NetworkConnection), serverToClient); var serverToClient2 = m_ServerDriver.Accept(); Assert.AreNotEqual(default(NetworkConnection), serverToClient2); // Send given packetCount number of packets in a row in one update // Write 1's for packet 1, 2's for packet 2 and so on and verify they're received in same order var strm = new DataStreamWriter(64, Allocator.Temp); for (int i = 0; i < packetCount; i++) { strm.Clear(); for (int j = 0; j < 16; j++) { strm.Write((int)i + 1); } m_ServerDriver.Send(serverPipe, serverToClient, strm); m_ServerDriver.Send(serverPipe, serverToClient2, strm); } m_ServerDriver.ScheduleUpdate().Complete(); // Receive incoming message from server m_ClientDriver.ScheduleUpdate().Complete(); m_ClientDriver2.ScheduleUpdate().Complete(); DataStreamReader readStrm; Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver, out readStrm)); Assert.AreEqual(NetworkEvent.Type.Connect, clientToServer.PopEvent(m_ClientDriver2, out readStrm)); ClientReceivePackets(m_ClientDriver, packetCount, clientToServer, startTime, packetDelay); ClientReceivePackets(m_ClientDriver2, packetCount, clientToServer2, startTime, packetDelay); }
public unsafe void SendPacket(ref SoakStatisticsPoint stats, ref SoakJobContext ctx) { var message = new SoakMessage { id = ctx.FrameId, time = fixedTime, sequence = ctx.NextSequenceNumber++, length = packetData.Length }; streamWriter.Clear(); streamWriter.WriteBytes(message.data, SoakMessage.HeaderLength); streamWriter.WriteBytes((byte *)packetData.GetUnsafeReadOnlyPtr(), packetData.Length); stats.SentBytes += connection[0].Send(driver, streamWriter); stats.SentPackets++; pendingSoaks[message.id % pendingSoaks.Length] = message; }
public IEnumerator ServerAndClient_SendMessageWithoutReadingIt_GivesErrorOnDriverUpdate() { SetupServerAndClientAndConnectThem(0); //send data from client DataStreamWriter m_OutStream = new DataStreamWriter(16, Allocator.Persistent); m_OutStream.Clear(); m_OutStream.Write(SharedConstants.ping); clientToServerConnection.Send(client_driver, m_OutStream); m_OutStream.Dispose(); server_driver.ScheduleUpdate().Complete(); client_driver.ScheduleUpdate().Complete(); LogAssert.Expect(LogType.Error, "Resetting event queue with pending events (Count=1, ConnectionID=0) Listening: 1"); server_driver.ScheduleUpdate().Complete(); DisconnectAndCleanup(); yield return(null); }
public IEnumerator ServerAndClient_SendBigMessage_OverflowsIncomingDriverBuffer() { SetupServerAndClientAndConnectThem(8); //send data from client DataStreamWriter m_OutStream = new DataStreamWriter(16, Allocator.Persistent); m_OutStream.Clear(); m_OutStream.Write(SharedConstants.ping); clientToServerConnection.Send(client_driver, m_OutStream); LogAssert.Expect(LogType.Error, "Error on receive 10040"); m_OutStream.Dispose(); //handle sent data server_driver.ScheduleUpdate().Complete(); client_driver.ScheduleUpdate().Complete(); Assert.AreEqual(10040, server_driver.ReceiveErrorCode); DisconnectAndCleanup(); yield return(null); }
public unsafe void Execute() { var snapshotAck = ackFromEntity[connectionEntity]; var ackTick = snapshotAck.LastReceivedSnapshotByRemote; DataStreamWriter dataStream = new DataStreamWriter(2048, Allocator.Temp); dataStream.Clear(); dataStream.Write((byte)NetworkStreamProtocol.Snapshot); dataStream.Write(localTime); // dataStream.Write(snapshotAck.LastReceivedRemoteTime - (localTime - snapshotAck.LastReceiveTimestamp)); // TODO: LZ: // to be confirmed // we should send "t0 + (T1 - T0)", but not "t0 - (T1 - T0)" // // because: // RTT should equals to : (t1 - t0) - (T1 - T0) = t1 - [t0 + (T1 - T0)] // t0: A send time // snapshotAck.LastReceivedRemoteTime // T0: B receive time // snapshotAck.LastReceiveTimestamp // T1: B send time // localTime // t1: A receive time dataStream.Write(snapshotAck.LastReceivedRemoteTime + (localTime - snapshotAck.LastReceiveTimestamp)); dataStream.Write(currentTick); int entitySize = UnsafeUtility.SizeOf <Entity>(); var despawnLenWriter = dataStream.Write((uint)0); var updateLenWriter = dataStream.Write((uint)0); uint despawnLen = 0; // TODO: if not all despawns fit, sort them based on age and maybe time since last send // TODO: only resend despawn on nack // FIXME: the TargetPacketSize cannot be used since CleanupGhostJob relies on all ghosts being sent every frame for (var chunk = 0; chunk < despawnChunks.Length /*&& dataStream.Length < TargetPacketSize*/; ++chunk) { var entities = despawnChunks[chunk].GetNativeArray(entityType); var ghosts = despawnChunks[chunk].GetNativeArray(ghostSystemStateType); for (var ent = 0; ent < entities.Length /*&& dataStream.Length < TargetPacketSize*/; ++ent) { if (ackTick == 0 || SequenceHelpers.IsNewer(ghosts[ent].despawnTick, ackTick)) { dataStream.WritePackedUInt((uint)ghosts[ent].ghostId, compressionModel); ++despawnLen; } } } uint updateLen = 0; var serialChunks = new NativeList <PrioChunk>(ghostChunks.Length + serialSpawnChunks.Length, Allocator.Temp); serialChunks.AddRange(serialSpawnChunks); var existingChunks = new NativeHashMap <ArchetypeChunk, int>(ghostChunks.Length, Allocator.Temp); // TODO: LZ: // temp hack, fix me int maxCount = serialSpawnChunks.Length; for (int chunk = 0; chunk < ghostChunks.Length; ++chunk) { SerializationState chunkState; var addNew = !chunkSerializationData.TryGetValue(ghostChunks[chunk], out chunkState); // FIXME: should be using chunk sequence number instead of this hack if (!addNew && chunkState.arch != ghostChunks[chunk].Archetype) { UnsafeUtility.Free(chunkState.snapshotData, Allocator.Persistent); chunkSerializationData.Remove(ghostChunks[chunk]); addNew = true; } if (addNew) { chunkState.lastUpdate = currentTick - 1; chunkState.startIndex = 0; chunkState.ghostType = serializers.FindSerializer(ghostChunks[chunk].Archetype); chunkState.arch = ghostChunks[chunk].Archetype; chunkState.snapshotWriteIndex = 0; int serializerDataSize = serializers.GetSnapshotSize(chunkState.ghostType); chunkState.snapshotData = (byte *)UnsafeUtility.Malloc(UnsafeUtility.SizeOf <int>() * GhostSystemConstants.SnapshotHistorySize + GhostSystemConstants.SnapshotHistorySize * ghostChunks[chunk].Capacity * (UnsafeUtility.SizeOf <Entity>() + serializerDataSize), 16, Allocator.Persistent); // Just clear snapshot index UnsafeUtility.MemClear(chunkState.snapshotData, UnsafeUtility.SizeOf <int>() * GhostSystemConstants.SnapshotHistorySize); chunkSerializationData.TryAdd(ghostChunks[chunk], chunkState); } existingChunks.TryAdd(ghostChunks[chunk], 1); // FIXME: only if modified or force sync var ghostType = chunkState.ghostType; var pc = new PrioChunk { chunk = ghostChunks[chunk], ghostState = null, priority = serializers.CalculateImportance(ghostType, ghostChunks[chunk]) * (int)(currentTick - chunkState.lastUpdate), startIndex = chunkState.startIndex, ghostType = ghostType }; serialChunks.Add(pc); if (ghostChunks[chunk].Count > maxCount) { maxCount = ghostChunks[chunk].Count; } } var oldChunks = chunkSerializationData.GetKeyArray(Allocator.Temp); for (int i = 0; i < oldChunks.Length; ++i) { int val; if (!existingChunks.TryGetValue(oldChunks[i], out val)) { SerializationState chunkState; chunkSerializationData.TryGetValue(oldChunks[i], out chunkState); UnsafeUtility.Free(chunkState.snapshotData, Allocator.Persistent); chunkSerializationData.Remove(oldChunks[i]); } } NativeArray <PrioChunk> serialChunkArray = serialChunks; serialChunkArray.Sort(); var availableBaselines = new NativeList <SnapshotBaseline>(GhostSystemConstants.SnapshotHistorySize, Allocator.Temp); var baselinePerEntity = new NativeArray <int>(maxCount * 3, Allocator.Temp); for (int pc = 0; pc < serialChunks.Length && dataStream.Length < TargetPacketSize; ++pc) { var chunk = serialChunks[pc].chunk; var ghostType = serialChunks[pc].ghostType; Entity * currentSnapshotEntity = null; byte * currentSnapshotData = null; SerializationState chunkState; int dataSize = 0; availableBaselines.Clear(); if (chunkSerializationData.TryGetValue(chunk, out chunkState)) { dataSize = serializers.GetSnapshotSize(chunkState.ghostType); uint *snapshotIndex = (uint *)chunkState.snapshotData; snapshotIndex[chunkState.snapshotWriteIndex] = currentTick; int baseline = (GhostSystemConstants.SnapshotHistorySize + chunkState.snapshotWriteIndex - 1) % GhostSystemConstants.SnapshotHistorySize; while (baseline != chunkState.snapshotWriteIndex) { if (snapshotAck.IsReceivedByRemote(snapshotIndex[baseline])) { byte *dataBase = chunkState.snapshotData + UnsafeUtility.SizeOf <int>() * GhostSystemConstants.SnapshotHistorySize + baseline * (dataSize + entitySize) * chunk.Capacity; availableBaselines.Add(new SnapshotBaseline { tick = snapshotIndex[baseline], snapshot = dataBase + entitySize * chunk.Capacity, entity = (Entity *)(dataBase) }); } baseline = (GhostSystemConstants.SnapshotHistorySize + baseline - 1) % GhostSystemConstants.SnapshotHistorySize; } // Find the acked snapshot to delta against, setup pointer to current and previous entity* and data* // Remember to bump writeIndex when done currentSnapshotData = chunkState.snapshotData + UnsafeUtility.SizeOf <int>() * GhostSystemConstants.SnapshotHistorySize; currentSnapshotData += chunkState.snapshotWriteIndex * (dataSize + entitySize) * chunk.Capacity; currentSnapshotEntity = (Entity *)currentSnapshotData; currentSnapshotData += entitySize * chunk.Capacity; } var ghosts = serialChunks[pc].ghostState; if (ghosts == null) { ghosts = (GhostSystemStateComponent *)chunk.GetNativeArray(ghostSystemStateType).GetUnsafeReadOnlyPtr(); } var ghostEntities = chunk.GetNativeArray(entityType); int ent; if (serialChunks[pc].startIndex < chunk.Count) { dataStream.WritePackedUInt((uint)ghostType, compressionModel); dataStream.WritePackedUInt((uint)(chunk.Count - serialChunks[pc].startIndex), compressionModel); } // First figure out the baselines to use per entity so they can be sent as baseline + maxCount instead of one per entity int targetBaselines = serializers.WantsPredictionDelta(ghostType) ? 3 : 1; for (ent = serialChunks[pc].startIndex; ent < chunk.Count; ++ent) { int foundBaselines = 0; for (int baseline = 0; baseline < availableBaselines.Length; ++baseline) { if (availableBaselines[baseline].entity[ent] == ghostEntities[ent]) { baselinePerEntity[ent * 3 + foundBaselines] = baseline; ++foundBaselines; if (foundBaselines == targetBaselines) { break; } } // Only way an entity can be missing from a snapshot but be available in an older is if last snapshot was partial else if (availableBaselines[baseline].entity[ent] != Entity.Null) { break; } } if (foundBaselines == 2) { foundBaselines = 1; } while (foundBaselines < 3) { baselinePerEntity[ent * 3 + foundBaselines] = -1; ++foundBaselines; } } ent = serializers.Serialize(ghostType, chunk, serialChunks[pc].startIndex, currentTick, currentSnapshotEntity, currentSnapshotData, ghosts, ghostEntities, baselinePerEntity, availableBaselines, dataStream, compressionModel); updateLen += (uint)(ent - serialChunks[pc].startIndex); // Spawn chunks are temporary and should not be added to the state data cache if (serialChunks[pc].ghostState == null) { // Only append chunks which contain data if (ent > serialChunks[pc].startIndex) { if (serialChunks[pc].startIndex > 0) { UnsafeUtility.MemClear(currentSnapshotEntity, entitySize * serialChunks[pc].startIndex); } if (ent < chunk.Capacity) { UnsafeUtility.MemClear(currentSnapshotEntity + ent, entitySize * (chunk.Capacity - ent)); } chunkState.snapshotWriteIndex = (chunkState.snapshotWriteIndex + 1) % GhostSystemConstants.SnapshotHistorySize; } if (ent >= chunk.Count) { chunkState.lastUpdate = currentTick; chunkState.startIndex = 0; } else { // TODO: should this always be run or should partial chunks only be allowed for the highest priority chunk? //if (pc == 0) chunkState.startIndex = ent; } chunkSerializationData.Remove(chunk); chunkSerializationData.TryAdd(chunk, chunkState); } } dataStream.Flush(); despawnLenWriter.Update(despawnLen); updateLenWriter.Update(updateLen); driver.Send(unreliablePipeline, connectionFromEntity[connectionEntity].Value, dataStream); }
public void SendNetObjectsUpdate() { if (State == ServerState.Debug) { return; } if (State != ServerState.Started) { throw new InvalidOperationException("Cannot set NetObject update: Server not running"); } Profiler.BeginSample("NetObject Update"); NetObject[] netObjects = NetObjectManager.Instance.NetObjects; const int headerSizeInBytes = 8; var streamWriter = new DataStreamWriter(MaxBytesPerMessage, Allocator.Temp); var objectWriter = new DataStreamWriter(MaxBytesPerMessage - headerSizeInBytes, Allocator.Temp); var objectIndex = 0; // compose new message if objects left to send or serialize while (objectIndex < netObjects.Length || objectWriter.Length > 0) { // header streamWriter.Clear(); streamWriter.WriteInt(Commands.UpdateNetObjects); DataStreamWriter objectCountWriter = streamWriter; streamWriter.WriteInt(0); // add items as long as they fit var objectsInMessage = 0; while (streamWriter.Length + objectWriter.Length <= MaxBytesPerMessage) { if (objectWriter.Length > 0) { streamWriter.WriteBytes(objectWriter.AsNativeArray()); objectWriter.Clear(); objectsInMessage++; } // next object. Write if dirty if (objectIndex < netObjects.Length) { NetObject netObject = netObjects[objectIndex++]; if (netObject.IsDirty) { WriteNetObject(netObject, ref objectWriter); } } else { break; } } objectCountWriter.WriteInt(objectsInMessage); // message complete. Send if payload exists if (objectsInMessage == 0) { break; } for (var connectionIndex = 0; connectionIndex < _connections.Length; connectionIndex++) { DataStreamWriter writer = _serverDriver.BeginSend(_reliablePipeline, _connections[connectionIndex]); writer.WriteBytes(streamWriter.AsNativeArray()); _serverDriver.EndSend(writer); } } Profiler.EndSample(); }
private void SendNetAssetUpdate(bool fullLoad, NativeList <NetworkConnection> connections) { if (State == ServerState.Debug) { return; } if (State != ServerState.Started) { throw new InvalidOperationException("Cannot send NetAsset update: Server not running"); } Profiler.BeginSample("NetAsset Update"); NetAsset[] netAssets = NetAssetManager.Instance.GetAll(); const int headerSizeInBytes = 8; var streamWriter = new DataStreamWriter(MaxBytesPerMessage, Allocator.Temp); var assetWriter = new DataStreamWriter(MaxBytesPerMessage - headerSizeInBytes, Allocator.Temp); var assetIndex = 0; // compose new message if assets left to send or serialize while (assetIndex < netAssets.Length || assetWriter.Length > 0) { streamWriter.Clear(); // write header streamWriter.WriteInt(Commands.UpdateNetAssets); DataStreamWriter netAssetCountWriter = streamWriter; streamWriter.WriteInt(0); // add assets as long as they fit var assetsInMessage = 0; while (streamWriter.Length + assetWriter.Length <= MaxBytesPerMessage) { if (assetWriter.Length > 0) { streamWriter.WriteBytes(assetWriter.AsNativeArray()); assetWriter.Clear(); assetsInMessage++; } // next asset. Serialize if dirty if (assetIndex < netAssets.Length) { NetAsset netAsset = netAssets[assetIndex++]; if (fullLoad || netAsset.IsDirty()) { SerializeNetAsset(netAsset, ref assetWriter, fullLoad); } } else { break; } } netAssetCountWriter.WriteInt(assetsInMessage); // message complete. Send if payload exists if (assetsInMessage == 0) { break; } for (var connectionIndex = 0; connectionIndex < connections.Length; connectionIndex++) { DataStreamWriter writer = _serverDriver.BeginSend(_reliablePipeline, connections[connectionIndex]); writer.WriteBytes(streamWriter.AsNativeArray()); _serverDriver.EndSend(writer); } } Profiler.EndSample(); }
private void SendSpawnMessage(NetObject[] netObjects, NativeList <NetworkConnection> connections) { if (connections.Length == 0) { return; } AssertActive(); const int headerSizeInBytes = 8; var streamWriter = new DataStreamWriter(MaxBytesPerMessage, Allocator.Temp); var objectWriter = new DataStreamWriter(MaxBytesPerMessage - headerSizeInBytes, Allocator.Temp); var objectIndex = 0; // compose new message if objects left to send or copy to message stream while (objectIndex < netObjects.Length || objectWriter.Length > 0) { streamWriter.Clear(); // write header streamWriter.WriteInt(Commands.SpawnNetObjects); DataStreamWriter objectCountWriter = streamWriter; streamWriter.WriteInt(0); // copy data over to message stream and write to object stream var objectsInMessage = 0; while (streamWriter.Length + objectWriter.Length <= MaxBytesPerMessage) { if (objectWriter.Length > 0) { streamWriter.WriteBytes(objectWriter.AsNativeArray()); objectWriter.Clear(); objectsInMessage++; } if (objectIndex < netObjects.Length) { NetObject netObject = netObjects[objectIndex++]; objectWriter.WriteInt(netObject.ID); objectWriter.WriteUShort(netObject.PrefabIndex); objectWriter.WriteInt(netObject.OwnerActorNumber); objectWriter.WriteVector3(netObject.transform.position); objectWriter.WriteQuaternion(netObject.transform.rotation); objectWriter.WriteInt(netObject.gameObject.scene.buildIndex); DataStreamWriter objectSizeWriter = objectWriter; objectWriter.WriteInt(0); int length = objectWriter.Length; netObject.Serialize(ref objectWriter, true); objectSizeWriter.WriteInt(objectWriter.Length - length); } else { break; } } objectCountWriter.WriteInt(objectsInMessage); // message complete. Send if payload present if (objectsInMessage == 0) { return; } for (var connectionIndex = 0; connectionIndex < connections.Length; connectionIndex++) { DataStreamWriter writer = _serverDriver.BeginSend(_reliablePipeline, connections[connectionIndex]); writer.WriteBytes(streamWriter.AsNativeArray()); _serverDriver.EndSend(writer); } } }
public void SendBatchedNetObjectsUpdate() { if (State == ClientState.Debug) { return; } if (State != ClientState.Connected) { Debug.LogWarning($"Cannot send messages in client state {State}"); return; } if (IsHost) { return; } NetObject[] netObjects = NetObjectManager.Instance.NetObjects; const int headerSizeInBytes = 8; var streamWriter = new DataStreamWriter(MaxBytesPerMessage, Allocator.Temp); var objectWriter = new DataStreamWriter(MaxBytesPerMessage - headerSizeInBytes, Allocator.Temp); var objectIndex = 0; // compose new message if objects left to send or serialize while (objectIndex < netObjects.Length || objectWriter.Length > 0) { // header streamWriter.Clear(); streamWriter.WriteInt(Commands.UpdateNetObjects); DataStreamWriter objectCountWriter = streamWriter; streamWriter.WriteInt(0); // add items as long as they fit var objectsInMessage = 0; while (streamWriter.Length + objectWriter.Length <= MaxBytesPerMessage) { if (objectWriter.Length > 0) { streamWriter.WriteBytes(objectWriter.AsNativeArray()); objectWriter.Clear(); objectsInMessage++; } // next object. Write if dirty and controlled by this client if (objectIndex < netObjects.Length) { NetObject netObject = netObjects[objectIndex++]; if (netObject.IsDirty && netObject.IsMine) { WriteNetObject(netObject, ref objectWriter); } } else { break; } } objectCountWriter.WriteInt(objectsInMessage); // message complete. Send if payload exists if (objectsInMessage > 0) { DataStreamWriter writer = _clientDriver.BeginSend(_reliablePipeline, _clientToServerConnection); writer.WriteBytes(streamWriter.AsNativeArray()); _clientDriver.EndSend(writer); DataSent?.Invoke(writer.Length); } } }
public void FillInternalBitStreamBuffer() { const int k_InternalBufferSize = 1000; const int k_PacketCount = 21; // Exactly enough to fill the receive buffer + 1 too much const int k_PacketSize = 50; using (var host = new LocalNetworkDriver(new NetworkDataStreamParameter { size = k_InternalBufferSize })) using (var client = new LocalNetworkDriver(new NetworkDataStreamParameter { size = 64 })) using (var stream = new DataStreamWriter(64, Allocator.Persistent)) { host.Bind(IPCManager.Instance.CreateEndPoint(Utilities.Random.String(32))); host.Listen(); NetworkConnection connectionId = client.Connect(host.LocalEndPoint()); client.ScheduleUpdate().Complete(); host.ScheduleUpdate().Complete(); NetworkConnection poppedId; DataStreamReader reader; host.Accept(); client.ScheduleUpdate().Complete(); var retval = client.PopEvent(out poppedId, out reader); Assert.AreEqual(retval, NetworkEvent.Type.Connect); var dataBlob = new Dictionary <int, byte[]>(); for (int i = 0; i < k_PacketCount; ++i) { // Scramble each packet contents so you can't match reading the same data twice as success dataBlob.Add(i, Encoding.ASCII.GetBytes(Utilities.Random.String(k_PacketSize))); } for (int i = 0; i < k_PacketCount; ++i) { stream.Clear(); stream.Write(dataBlob[i]); client.Send(connectionId, stream); } // Process the pending events client.ScheduleUpdate().Complete(); host.ScheduleUpdate().Complete(); for (int i = 0; i < k_PacketCount; ++i) { retval = host.PopEvent(out poppedId, out reader); if (i == k_PacketCount - 1) { Assert.AreEqual(retval, NetworkEvent.Type.Empty); Assert.IsFalse(reader.IsCreated); host.ScheduleUpdate().Complete(); retval = host.PopEvent(out poppedId, out reader); } Assert.AreEqual(retval, NetworkEvent.Type.Data); Assert.AreEqual(k_PacketSize, reader.Length); var readerCtx = default(DataStreamReader.Context); for (int j = 0; j < k_PacketSize; ++j) { Assert.AreEqual(dataBlob[i][j], reader.ReadByte(ref readerCtx)); } } } }
protected override void OnUpdate() { if (!m_ConnectionWithoutSnapshotBufferGroup.IsEmptyIgnoreFilter) { using (var entities = m_ConnectionWithoutSnapshotBufferGroup.ToEntityArray(Allocator.TempJob)) { foreach (var entity in entities) { var buffer = EntityManager.AddBuffer <ClientSnapshotBuffer>(entity); buffer.ResizeUninitialized(1200); buffer.Clear(); } } } using (var deleteKeys = new NativeList <Entity>(8, Allocator.Temp)) { foreach (var kvp in m_SerializeLookup) { if (!EntityManager.Exists(kvp.Key)) { deleteKeys.Add(kvp.Key); } } foreach (var key in deleteKeys) { m_SerializeLookup[key].Data.Dispose(); m_SerializeLookup.Remove(key); } } var connectionEntities = m_ConnectionGroup.ToEntityArray(Allocator.TempJob); var networkStreamConnectionArray = m_ConnectionGroup.ToComponentDataArray <NetworkStreamConnection>(Allocator.TempJob); var ackComponentArray = m_ConnectionGroup.ToComponentDataArray <NetworkSnapshotAckComponent>(Allocator.TempJob); foreach (var entity in connectionEntities) { if (m_SerializeLookup.ContainsKey(entity)) { continue; } m_SerializeLookup[entity] = new ReferencableSerializeClientData { Data = new SerializeClientData(Allocator.Persistent) }; } m_CreateSnapshotSystem.CreateSnapshot(m_ServerSimulationSystemGroup.ServerTick, m_SerializeLookup); var localTime = NetworkTimeSystem.TimestampMS; for (var ent = 0; ent < connectionEntities.Length; ent++) { var entity = connectionEntities[ent]; var connection = networkStreamConnectionArray[ent]; var ack = ackComponentArray[ent]; var buffer = EntityManager.GetBuffer <ClientSnapshotBuffer>(entity); m_DataStream.Clear(); m_DataStream.Write((byte)NetworkStreamProtocol.Snapshot); m_DataStream.Write(localTime); var returnTime = ack.LastReceivedRemoteTime; if (returnTime != 0) { returnTime -= (localTime - ack.LastReceiveTimestamp); } m_DataStream.Write(returnTime); m_DataStream.Write(ack.ServerCommandAge); m_DataStream.Write(byte.MaxValue); m_DataStream.Write(m_ServerSimulationSystemGroup.ServerTick); Profiler.BeginSample("Compressing"); var compressed = UnsafeUtility.Malloc(LZ4Codec.MaximumOutputSize(buffer.Length), UnsafeUtility.AlignOf <byte>(), Allocator.Temp); var compressedLength = LZ4Codec.MaximumOutputSize(buffer.Length); { var encoder = LZ4Level.L04_HC; // default encoder //encoder = LZ4Level.L12_MAX; var size = LZ4Codec.Encode((byte *)buffer.GetUnsafePtr(), buffer.Length, (byte *)compressed, compressedLength, encoder); Profiler.EndSample(); m_DataStream.Write(size); m_DataStream.Write(buffer.Length); if (size > 1000) { Debug.Log($"s={size} b={buffer.Length}"); } m_DataStream.WriteBytes((byte *)compressed, size); } UnsafeUtility.Free(compressed, Allocator.Temp); m_ReceiveSystem.Driver.Send(m_ReceiveSystem.ReliablePipeline, connection.Value, m_DataStream); } connectionEntities.Dispose(); networkStreamConnectionArray.Dispose(); ackComponentArray.Dispose(); }
public void Execute() { DataStreamReader stream; NetworkEvent.Type cmd; //前フレームで解決できなかったbufferedからuncheckedに登録. if (!savedUncheckedReliableDataStream.IsCreated) { stream = new DataStreamReader(savedUncheckedReliableDataStream, 0, savedUncheckedReliableDataStream.Length); int offset = 0; while (offset < savedUncheckedReliableDataStream.Length) { var readerCtx = default(DataStreamReader.Context); ushort length = stream.ReadUShort(ref readerCtx); if (0 < length && length <= savedUncheckedReliableDataStream.Length - offset - 2) { uncheckedreliableStreams.Add( new DataStreamReader(savedUncheckedReliableDataStream, offset + 2, length)); offset += length + 2; } else { break; } } } while ((cmd = connection.PopEvent(driver, out stream)) != NetworkEvent.Type.Empty) { if (cmd == NetworkEvent.Type.Connect) { flags[(int)FlagDef.IsConnected] = 1; Debug.Log("Connect : " + connection.InternalId); } else if (cmd == NetworkEvent.Type.Disconnect) { flags[(int)FlagDef.IsDisconnected] = 1; Debug.Log("Disconnect : " + connection.InternalId); } else if (cmd == NetworkEvent.Type.Data) { if (!stream.IsCreated) { continue; } var readerCtx = default(DataStreamReader.Context); byte qosType = stream.ReadByte(ref readerCtx); ushort seqNum = stream.ReadUShort(ref readerCtx); ushort ackNum = stream.ReadUShort(ref readerCtx); //if (qosType != (byte)QosType.MeasureLatency) { // Debug.Log ("Recieve Data Len=" + stream.Length + ",QoS=" + qosType + ",Seq=" + seqNum + ",Ack=" + ackNum); //} //最初のregister packetはseqNumに bool isInitialUpdate = flags[(int)FlagDef.IsNotInitialUpdate] == 0; //ackNumの更新 if (seqNumbers[(int)SeqNumberDef.OtherAck] != ackNum) { seqNumbers[(int)SeqNumberDef.OtherAck] = ackNum; //Debug.Log ("update OtherAck = " + ackNum + " first=" + isInitialUpdate); } switch ((QosType)qosType) { case QosType.MeasureLatency: long currentUnixTime = System.DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); long otherUnixTime = stream.ReadLong(ref readerCtx); seqNumbers[(int)SeqNumberDef.Latency] = (ushort)(currentUnixTime - otherUnixTime); break; case QosType.Unreliable: dataStreams.Add(stream); break; case QosType.Reliable: int seqNumberDiff = (int)seqNum - seqNumbers[(int)SeqNumberDef.OtherSeq]; if (!isInitialUpdate && seqNumberDiff > 1) { //Debug.Log ("Reliable seqNumberDiff > 1 recieve"); //順番が入れ替わってるからバッファに貯める if (seqNumberDiff - 2 < uncheckedreliableStreams.Length) { if (uncheckedreliableStreams[seqNumberDiff - 2].IsCreated) { uncheckedreliableStreams[seqNumberDiff - 2] = stream; } } else { uncheckedreliableStreams.Add(stream); } } else if (isInitialUpdate || (seqNumberDiff == 1 || seqNumberDiff == -ushort.MaxValue)) { flags[(int)FlagDef.IsNotInitialUpdate] = 1; //次の順のパケットなら確定する seqNumbers[(int)SeqNumberDef.OtherSeq] = seqNum; //Debug.Log ("update OtherSeq = " + seqNumbers[(int)SeqNumberDef.OtherSeq] + " first=" + isInitialUpdate); //AddChunksInDataStream (stream, ref readerCtx); dataStreams.Add(stream); //順番待ちのパケットを確定する while (uncheckedreliableStreams.Length != 0) { if (!uncheckedreliableStreams[0].IsCreated) { IncrementSequenceNumber(SeqNumberDef.OtherSeq); //Debug.Log ("update OtherSeq = " + seqNumbers[(int)SeqNumberDef.OtherSeq]); //AddChunksInDataStream (uncheckedreliableStreams[0], ref readerCtx); dataStreams.Add(uncheckedreliableStreams[0]); uncheckedreliableStreams.RemoveAtSwapBack(0); } else { break; } } } else { //受信済みのSeqNumのパケットなら無視 //Debug.Log ("Reliable same recieve"); } break; } } } //uncheckedreliableStreamsに残ったパケットはnetworkdriverから実態が消される前にコピーしておく unsafe { //uncheckedreliableStreamsはsavedUncheckedReliableDataStreamに実態を持つ場合があるので //直接savedUncheckedReliableDataStream書き込むと実態が消えてしまうのでtempまず書く tempUncheckedReliableDataStream.Clear(); for (int i = 0; i < uncheckedreliableStreams.Length; i++) { int dataLength = uncheckedreliableStreams[i].Length; if (tempUncheckedReliableDataStream.Capacity - tempUncheckedReliableDataStream.Length < dataLength + 2) { tempUncheckedReliableDataStream.Capacity *= 2; } byte *dataPtr = DataStreamUnsafeUtility.GetUnsafeReadOnlyPtr(uncheckedreliableStreams[i]); tempUncheckedReliableDataStream.Write(dataLength); tempUncheckedReliableDataStream.WriteBytes(dataPtr, dataLength); } savedUncheckedReliableDataStream.Clear(); if (savedUncheckedReliableDataStream.Capacity < tempUncheckedReliableDataStream.Capacity) { savedUncheckedReliableDataStream.Capacity *= tempUncheckedReliableDataStream.Capacity; } savedUncheckedReliableDataStream.WriteBytes( tempUncheckedReliableDataStream.GetUnsafeReadOnlyPtr(), tempUncheckedReliableDataStream.Length); } //次に自分が送るパケットでどこまで受け取ったか伝える. seqNumbers[(int)SeqNumberDef.SelfAck] = seqNumbers[(int)SeqNumberDef.OtherSeq]; }