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));
            }
        }
Ejemplo n.º 2
0
        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));

            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);
            int maxCount       = 0;

            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>() * SnapshotHistorySize + SnapshotHistorySize * ghostChunks[chunk].Capacity * (UnsafeUtility.SizeOf <Entity>() + serializerDataSize), 16, Allocator.Persistent);

                    // Just clear snapshot index
                    UnsafeUtility.MemClear(chunkState.snapshotData, UnsafeUtility.SizeOf <int>() * 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>(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 = (SnapshotHistorySize + chunkState.snapshotWriteIndex - 1) % SnapshotHistorySize;
                    while (baseline != chunkState.snapshotWriteIndex)
                    {
                        if (snapshotAck.IsReceivedByRemote(snapshotIndex[baseline]))
                        {
                            byte *dataBase = chunkState.snapshotData +
                                             UnsafeUtility.SizeOf <int>() * SnapshotHistorySize +
                                             baseline * (dataSize + entitySize) * chunk.Capacity;
                            availableBaselines.Add(new SnapshotBaseline
                            {
                                tick     = snapshotIndex[baseline],
                                snapshot = dataBase + entitySize * chunk.Capacity,
                                entity   = (Entity *)(dataBase)
                            });
                        }

                        baseline = (SnapshotHistorySize + baseline - 1) % 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>() * 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) % 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);

            var snapshot = bufferFromEntity[connectionEntity];

            snapshot.ResizeUninitialized(dataStream.Length);
            UnsafeUtility.MemCpy(snapshot.GetUnsafePtr(), dataStream.GetUnsafePtr(), dataStream.Length);
        }
Ejemplo n.º 3
0
        public void Update()
        {
            if (!m_Socket.Listening && !conn.IsCreated)
            {
                conn = m_Socket.Connect(new IPEndPoint(IPAddress.Loopback, serverPort));
            }
            else if (!m_Socket.Listening && dataCounter == 0 && !conn.IsCreated)
            {
                using (var message = new DataStreamWriter(7, Allocator.Persistent))
                {
                    message.Write((byte)'m');
                    message.Write((byte)'e');
                    message.Write((byte)'s');
                    message.Write((byte)'s');
                    message.Write((byte)'a');
                    message.Write((byte)'g');
                    message.Write((byte)'e');

                    m_Socket.Send(conn, message);
                }
            }
            else if (!m_Socket.Listening && conn.IsCreated &&
                     UnityEngine.Random.Range(0, 1000) < 10)
            {
                m_Socket.Disconnect(conn);
                conn = default(NetworkConnection);
            }

            NetworkConnection connection;
            DataStreamReader  reader;
            var context = default(DataStreamReader.Context);
            var ev      = m_Socket.PopEvent(out connection, out reader);

            if (ev == ExperimentalEventType.Empty)
            {
                return;
            }

            using (var writer = new DataStreamWriter(reader.Length, Allocator.Temp))
            {
                unsafe
                {
                    reader.ReadBytes(ref context, writer.GetUnsafePtr(), reader.Length);
                }
                switch (ev)
                {
                case ExperimentalEventType.Connect:
                    connectCounter++;
                    break;

                case ExperimentalEventType.Disconnect:
                    conn = default(NetworkConnection);
                    disconnectCounter++;
                    break;

                case ExperimentalEventType.Data:
                    dataCounter++;
                    m_Socket.Send(connection, writer);
                    break;
                }
            }
        }