Beispiel #1
0
    public void Update(INetworkClientCallbacks clientNetworkConsumer, ISnapshotConsumer snapshotConsumer)
    {
        _transport.Update();

        TransportEvent e = new TransportEvent();

        while (_transport.NextEvent(ref e))
        {
            switch (e.type)
            {
            case TransportEvent.Type.Connect:
                OnConnect(e.ConnectionId);
                break;

            case TransportEvent.Type.Disconnect:
                OnDisconnect(e.ConnectionId);
                break;

            case TransportEvent.Type.Data:
                OnData(e.Data, clientNetworkConsumer, snapshotConsumer);
                break;
            }
        }

        if (clientConnection != null)
        {
            clientConnection.ProcessMapUpdate(clientNetworkConsumer);
        }
    }
Beispiel #2
0
        public void ProcessSnapshot(ISnapshotConsumer consumer)
        {
            // Should we prune list for stuff that is also in despawns?
            foreach (var id in spawns)
            {
                GameDebug.Assert(entities[id].type != null, "Processing spawn of id {0} but type is null", id);
                consumer.ProcessEntitySpawn(serverTime, id, entities[id].type.typeId);
            }

            spawns.Clear();

            foreach (var id in updates)
            {
                var info = entities[id];
                GameDebug.Assert(info.type != null, "Processing update of id {0} but type is null", id);
                var reader = new NetworkReader(info.lastUpdate, info.type.schema);
                consumer.ProcessEntityUpdate(serverTime, id, ref reader);
            }

            updates.Clear();

            foreach (var id in despawns)
            {
                consumer.ProcessEntityDespawn(serverTime, id);
            }

            despawns.Clear();
        }
Beispiel #3
0
    public void OnData(byte[] data, INetworkClientCallbacks clientNetworkConsumer, ISnapshotConsumer snapshotConsumer)
    {
        if (clientConnection == null)
        {
            return;
        }

        clientConnection.ReadPackage(data, snapshotConsumer, clientNetworkConsumer);
    }
Beispiel #4
0
    public void ProcessSnapshot(ISnapshotConsumer consumer)
    {
        if (m_Connection == null)
        {
            return;
        }

        m_Connection.ProcessSnapshot(consumer);
    }
Beispiel #5
0
    public void Update(INetworkClientCallbacks clientNetworkConsumer, ISnapshotConsumer snapshotConsumer)
    {
        Profiler.BeginSample("NetworkClient.Update");

        m_Transport.Update();

        // Debug tracking of outstanding events
        if (NetworkConfig.netDebug.IntValue > 1 && m_Connection != null)
        {
            var outstandingPackages = m_Connection.outstandingPackages;
            for (var i = 0; i < outstandingPackages.m_Elements.Length; i++)
            {
                if (outstandingPackages.m_Sequences[i] != -1 && outstandingPackages.m_Elements[i].events.Count > 0)
                {
                    GameDebug.Log("Outstanding Package: " + i + " (idx), " + outstandingPackages.m_Sequences[i] + " (seq), " + outstandingPackages.m_Elements[i].events.Count + " (numevs), " + ((GameNetworkEvents.EventType)outstandingPackages.m_Elements[i].events[0].type.typeId) + " (ev0)");
                }
            }
        }

        TransportEvent e = new TransportEvent();

        while (m_Transport.NextEvent(ref e))
        {
            switch (e.type)
            {
            case TransportEvent.Type.Connect:
                OnConnect(e.connectionId);
                break;

            case TransportEvent.Type.Disconnect:
                OnDisconnect(e.connectionId);
                break;

            case TransportEvent.Type.Data:
                OnData(e.connectionId, e.data, e.dataSize, clientNetworkConsumer, snapshotConsumer);
                break;
            }
        }

        if (m_Connection != null)
        {
            m_Connection.ProcessMapUpdate(clientNetworkConsumer);
        }

        Profiler.EndSample();
    }
Beispiel #6
0
        public void ReadPackage(byte[] packageData, ISnapshotConsumer snapshotConsumer, INetworkCallbacks networkClientConsumer)
        {
            counters.bytesIn += packageData.Length;

            NetworkUtils.MemCopy(packageData, 0, packageBuffer, 0, packageData.Length);

            NetworkMessage content;
            int            headerSize;
            var            packageSequence = ProcessPackageHeader(packageBuffer, out content, out headerSize);

            // The package was dropped (duplicate or too old) or if it was a fragment not yet assembled, bail out here
            if (packageSequence == 0)
            {
                return;
            }

            var input = new RawInputStream();

            input.Initialize(packageBuffer, headerSize);

            if ((content & NetworkMessage.ClientInfo) != 0)
            {
                ReadClientInfo(ref input);
            }

            if ((content & NetworkMessage.MapInfo) != 0)
            {
                ReadMapInfo(ref input);
            }

            if ((content & NetworkMessage.Snapshot) != 0)
            {
                ReadSnapshot(packageSequence, ref input, snapshotConsumer);

                // Make sure the callback actually picked up the snapshot data. It is important that
                // every snapshot gets processed by the game so that the spawns, despawns and updates lists
                // does not end up containing stuff from different snapshots
                //GameDebug.Assert(spawns.Count == 0 && despawns.Count == 0 && updates.Count == 0, "Game did not consume snapshots");
            }

            if ((content & NetworkMessage.Events) != 0)
            {
                ReadEvents(ref input, networkClientConsumer);
            }
        }
Beispiel #7
0
        unsafe void ReadSnapshot <TInputStream>(int sequence, ref TInputStream input, ISnapshotConsumer consumer) where TInputStream : NetworkCompression.IInputStream
        {
            //input.SetStatsType(NetworkCompressionReader.Type.SnapshotSchema);
            counters.snapshotsIn++;

            // Snapshot may be delta compressed against one or more baselines
            // Baselines are indicated by sequence number of the package it was in
            var haveBaseline = input.ReadRawBits(1) == 1;
            var baseSequence = (int)input.ReadPackedIntDelta(sequence - 1, NetworkConfig.baseSequenceContext);

            bool enableNetworkPrediction = input.ReadRawBits(1) != 0;
            bool enableHashing           = input.ReadRawBits(1) != 0;

            int baseSequence1 = 0;
            int baseSequence2 = 0;

            if (enableNetworkPrediction)
            {
                baseSequence1 = (int)input.ReadPackedIntDelta(baseSequence - 1, NetworkConfig.baseSequence1Context);
                baseSequence2 = (int)input.ReadPackedIntDelta(baseSequence1 - 1, NetworkConfig.baseSequence2Context);
            }

            if (clientDebug.IntValue > 2)
            {
                if (enableNetworkPrediction)
                {
                    GameDebug.Log((haveBaseline ? "Snap [BL]" : "Snap [  ]") + "(" + sequence + ")  " + baseSequence + " - " + baseSequence1 + " - " + baseSequence2);
                }
                else
                {
                    GameDebug.Log((haveBaseline ? "Snap [BL]" : "Snap [  ]") + "(" + sequence + ")  " + baseSequence);
                }
            }

            if (!haveBaseline)
            {
                counters.fullSnapshotsIn++;
            }

            GameDebug.Assert(!haveBaseline ||
                             (sequence > baseSequence && sequence - baseSequence < NetworkConfig.snapshotDeltaCacheSize), "Attempting snapshot encoding with invalid baseline: {0}:{1}", sequence, baseSequence);

            var snapshotInfo = snapshots.Acquire(sequence);

            snapshotInfo.serverTime = (int)input.ReadPackedIntDelta(haveBaseline ? snapshots[baseSequence].serverTime : 0, NetworkConfig.serverTimeContext);

            var temp = (int)input.ReadRawBits(8);

            serverSimTime = temp * 0.1f;

            // Only update time if received in-order..
            // TODO consider dropping out of order snapshots
            // TODO detecting out-of-order on pack sequences
            if (snapshotInfo.serverTime > serverTime)
            {
                serverTime           = snapshotInfo.serverTime;
                snapshotReceivedTime = NetworkUtils.stopwatch.ElapsedMilliseconds;
            }
            else
            {
                GameDebug.Log(string.Format("NetworkClient. Dropping out of order snaphot. Server time:{0} snapshot time:{1}", serverTime, snapshotInfo.serverTime));
            }

            counters.AddSectionStats("snapShotHeader", input.GetBitPosition2(), new Color(0.5f, 0.5f, 0.5f));

            // Used by thinclient that wants to very cheaply just do minimal handling of
            // snapshots
            if (m_DropSnapshots)
            {
                return;
            }

            // Read schemas
            var schemaCount = input.ReadPackedUInt(NetworkConfig.schemaCountContext);

            for (int schemaIndex = 0; schemaIndex < schemaCount; ++schemaIndex)
            {
                var typeId = (ushort)input.ReadPackedUInt(NetworkConfig.schemaTypeIdContext);

                var entityType = new EntityTypeInfo()
                {
                    typeId = typeId
                };
                entityType.schema = NetworkSchema.ReadSchema(ref input);
                counters.AddSectionStats("snapShotSchemas", input.GetBitPosition2(), new Color(0.0f, (schemaIndex & 1) == 1 ? 0.5f : 1.0f, 1.0f));
                entityType.baseline = new uint[NetworkConfig.maxEntitySnapshotDataSize];
                NetworkSchema.CopyFieldsToBuffer(entityType.schema, ref input, entityType.baseline);

                if (!entityTypes.ContainsKey(typeId))
                {
                    entityTypes.Add(typeId, entityType);
                }

                counters.AddSectionStats("snapShotSchemas", input.GetBitPosition2(), new Color(1.0f, (schemaIndex & 1) == 1 ? 0.5f : 1.0f, 1.0f));
            }

            // Remove any despawning entities that belong to older base sequences
            for (int i = 0; i < entities.Count; i++)
            {
                var e = entities[i];
                if (e.type == null)
                {
                    continue;
                }
                if (e.despawnSequence > 0 && e.despawnSequence <= baseSequence)
                {
                    e.Reset();
                }
            }

            // Read new spawns
            m_TempSpawnList.Clear();
            var previousId = 1;
            var spawnCount = input.ReadPackedUInt(NetworkConfig.spawnCountContext);

            for (var spawnIndex = 0; spawnIndex < spawnCount; ++spawnIndex)
            {
                var id = (int)input.ReadPackedIntDelta(previousId, NetworkConfig.idContext);
                previousId = id;

                // Register the entity
                var typeId = (ushort)input.ReadPackedUInt(NetworkConfig.spawnTypeIdContext);    //TODO: use another encoding
                GameDebug.Assert(entityTypes.ContainsKey(typeId), "Spawn request with unknown type id {0}", typeId);

                byte fieldMask = (byte)input.ReadRawBits(8);

                // TODO (petera) need an max entity id for safety
                while (id >= entities.Count)
                {
                    entities.Add(new EntityInfo());
                }

                // Incoming spawn of different type than what we have for this id, so immediately nuke
                // the one we have to make room for the incoming
                if (entities[id].type != null && entities[id].type.typeId != typeId)
                {
                    // This should only ever happen in case of no baseline as normally the server will
                    // not reuse an id before all clients have acknowledged its despawn.
                    GameDebug.Assert(haveBaseline == false, "Spawning entity but we already have with different type?");
                    GameDebug.Log("REPLACING old entity: " + id + " because snapshot gave us new type for this id");
                    despawns.Add(id);
                    entities[id].Reset();
                }

                // We can receive spawn information in several snapshots before our ack
                // has reached the server. Only pass on spawn to game layer once
                if (entities[id].type == null)
                {
                    var e = entities[id];
                    e.type      = entityTypes[typeId];
                    e.fieldMask = fieldMask;
                    spawns.Add(id);
                }

                m_TempSpawnList.Add(id);
            }

            counters.AddSectionStats("snapShotSpawns", input.GetBitPosition2(), new Color(0, 0.58f, 0));

            // Read despawns
            var despawnCount = input.ReadPackedUInt(NetworkConfig.despawnCountContext);

            // If we have no baseline, we need to clear all entities that are not being spawned
            if (!haveBaseline)
            {
                GameDebug.Assert(despawnCount == 0, "There should not be any despawns in a non-baseline snapshot");
                for (int i = 0, c = entities.Count; i < c; ++i)
                {
                    var e = entities[i];
                    if (e.type == null)
                    {
                        continue;
                    }
                    if (m_TempSpawnList.Contains(i))
                    {
                        continue;
                    }
                    GameDebug.Log("NO BL SO PRUNING Stale entity: " + i);
                    despawns.Add(i);
                    e.Reset();
                }
            }

            for (var despawnIndex = 0; despawnIndex < despawnCount; ++despawnIndex)
            {
                var id = (int)input.ReadPackedIntDelta(previousId, NetworkConfig.idContext);
                previousId = id;

                // we may see despawns many times, only handle if we still have the entity
                GameDebug.Assert(id < entities.Count, "Getting despawn for id {0} but we only know about entities up to {1}", id, entities.Count);
                if (entities[id].type == null)
                {
                    continue;
                }

                var entity = entities[id];

                // Already in the process of being despawned. This happens with same-snapshot spawn/despawn cases
                if (entity.despawnSequence > 0)
                {
                    continue;
                }

                // If we are spawning and despawning in same snapshot, delay actual deletion of
                // entity as we need it around to be able to read the update part of the snapshot
                if (m_TempSpawnList.Contains(id))
                {
                    entity.despawnSequence = sequence; // keep until baseSequence >= despawnSequence
                }
                else
                {
                    entity.Reset(); // otherwise remove right away; no further updates coming, not even in this snap
                }
                // Add to despawns list so we can request despawn from game later
                GameDebug.Assert(!despawns.Contains(id), "Double despawn in same snaphot? {0}", id);
                despawns.Add(id);
            }

            counters.AddSectionStats("snapShotDespawns", input.GetBitPosition2(), new Color(0.49f, 0, 0));

            // Predict all active entities
            for (var id = 0; id < entities.Count; id++)
            {
                var info = entities[id];
                if (info.type == null)
                {
                    continue;
                }

                // NOTE : As long as the server haven't gotten the spawn acked, it will keep sending
                // delta relative to 0, so we need to check if the entity was in the spawn list to determine
                // if the delta is relative to the last update or not

                int baseline0Time = 0;

                uint[] baseline0 = info.type.baseline;
                GameDebug.Assert(baseline0 != null, "Unable to find schema baseline for type {0}", info.type.typeId);

                if (haveBaseline && !m_TempSpawnList.Contains(id))
                {
                    baseline0 = info.baselines.FindMax(baseSequence);
                    GameDebug.Assert(baseline0 != null, "Unable to find baseline for seq {0} for id {1}", baseSequence, id);
                    baseline0Time = snapshots[baseSequence].serverTime;
                }

                if (enableNetworkPrediction)
                {
                    uint num_baselines = 1; // 1 because either we have schema baseline or we have a real baseline
                    int  baseline1Time = 0;
                    int  baseline2Time = 0;

                    uint[] baseline1 = null;
                    uint[] baseline2 = null;
                    if (baseSequence1 != baseSequence)
                    {
                        baseline1 = info.baselines.FindMax(baseSequence1);
                        if (baseline1 != null)
                        {
                            num_baselines = 2;
                            baseline1Time = snapshots[baseSequence1].serverTime;
                        }
                        if (baseSequence2 != baseSequence1)
                        {
                            baseline2 = info.baselines.FindMax(baseSequence2);
                            if (baseline2 != null)
                            {
                                num_baselines = 3;
                                baseline2Time = snapshots[baseSequence2].serverTime;
                            }
                        }
                    }

                    // TODO (petera) are these clears needed?
                    for (int i = 0, c = info.fieldsChangedPrediction.Length; i < c; ++i)
                    {
                        info.fieldsChangedPrediction[i] = 0;
                    }
                    for (int i = 0; i < NetworkConfig.maxEntitySnapshotDataSize; i++)
                    {
                        info.prediction[i] = 0;

                        fixed(uint *prediction = info.prediction, baseline0p = baseline0, baseline1p = baseline1, baseline2p = baseline2)
                        {
                            NetworkPrediction.PredictSnapshot(prediction, info.fieldsChangedPrediction, info.type.schema, num_baselines, (uint)baseline0Time, baseline0p, (uint)baseline1Time, baseline1p, (uint)baseline2Time, baseline2p, (uint)snapshotInfo.serverTime, info.fieldMask);
                        }
                }
                else
                {
                    var f = info.fieldsChangedPrediction;
                    for (var i = 0; i < f.Length; ++i)
                    {
                        f[i] = 0;
                    }
                    for (int i = 0, c = info.type.schema.GetByteSize() / 4; i < c; ++i)
                    {
                        info.prediction[i] = baseline0[i];
                    }
                }
            }

            // Read updates
            var updateCount = input.ReadPackedUInt(NetworkConfig.updateCountContext);

            for (var updateIndex = 0; updateIndex < updateCount; ++updateIndex)
            {
                var id = (int)input.ReadPackedIntDelta(previousId, NetworkConfig.idContext);
                previousId = id;

                var info = entities[id];

                uint hash = 0;
                // Copy prediction to temp buffer as we now overwrite info.prediction with fully unpacked
                // state by applying incoming delta to prediction.
                for (int i = 0, c = info.type.schema.GetByteSize() / 4; i < c; ++i)
                {
                    tempSnapshotBuffer[i] = info.prediction[i];
                }

                DeltaReader.Read(ref input, info.type.schema, info.prediction, tempSnapshotBuffer, info.fieldsChangedPrediction, info.fieldMask, ref hash);
                if (enableHashing)
                {
                    uint hashCheck = input.ReadRawBits(32);

                    if (hash != hashCheck)
                    {
                        GameDebug.Log("Hash check fail for entity " + id);
                        if (enableNetworkPrediction)
                        {
                            GameDebug.Assert(false, "Snapshot (" + snapshotInfo.serverTime + ") " + (haveBaseline ? "Snap [BL]" : "Snap [  ]") + "  " + baseSequence + " - " + baseSequence1 + " - " + baseSequence2 + ". Sche: " + schemaCount + " Spwns: " + spawnCount + " Desp: " + despawnCount + " Upd: " + updateCount);
                        }
                        else
                        {
                            GameDebug.Assert(false, "Snapshot (" + snapshotInfo.serverTime + ") " + (haveBaseline ? "Snap [BL]" : "Snap [  ]") + "  " + baseSequence + ". Sche: " + schemaCount + " Spwns: " + spawnCount + " Desp: " + despawnCount + " Upd: " + updateCount);
                        }
                    }
                }
            }

            if (enableNetworkPrediction)
            {
                counters.AddSectionStats("snapShotUpdatesPredict", input.GetBitPosition2(), haveBaseline ? new Color(0.09f, 0.38f, 0.93f) : Color.cyan);
            }
            else
            {
                counters.AddSectionStats("snapShotUpdatesNoPredict", input.GetBitPosition2(), haveBaseline ? new Color(0.09f, 0.38f, 0.93f) : Color.cyan);
            }

            uint snapshotHash = 0; // sum of hash for all (updated or not) entity snapshots
            uint numEnts      = 0;

            for (int id = 0; id < entities.Count; id++)
            {
                var info = entities[id];
                if (info.type == null)
                {
                    continue;
                }

                // Skip despawned that have not also been spawned in this snapshot
                if (info.despawnSequence > 0 && !spawns.Contains(id))
                {
                    continue;
                }

                // If just spawned or if new snapshot is different from the last we deserialized,
                // we need to deserialize. Otherwise just ignore; no reason to deserialize the same
                // values again
                int schemaSize = info.type.schema.GetByteSize();
                if (info.baselines.GetSize() == 0 || NetworkUtils.MemCmp(info.prediction, 0, info.lastUpdate, 0, schemaSize) != 0)
                {
                    var data = info.baselines.Insert(sequence);
                    for (int i = 0; i < schemaSize / 4; ++i)
                        data[i] = info.prediction[i]; }
                    if (sequence > info.lastUpdateSequence)
                    {
                        if (!updates.Contains(id))
                        {
                            updates.Add(id);
                        }

                        for (int i = 0; i < schemaSize / 4; ++i)
                        {
                            info.lastUpdate[i] = info.prediction[i];
                        }
                        info.lastUpdateSequence = sequence;
                    }
                }

                if (enableHashing && info.despawnSequence == 0)
                {
                    snapshotHash += NetworkUtils.SimpleHash(info.prediction, schemaSize);
                    numEnts++;
                }
            }
Beispiel #8
0
        public void ReadPackage <TInputStream>(byte[] packageData, int packageSize, NetworkCompressionModel compressionModel, INetworkClientCallbacks networkClientConsumer, ISnapshotConsumer snapshotConsumer) where TInputStream : struct, NetworkCompression.IInputStream
        {
            counters.bytesIn += packageSize;

            NetworkMessage content;

            byte[] assembledData;
            int    assembledSize;
            int    headerSize;
            var    packageSequence = ProcessPackageHeader(packageData, packageSize, out content, out assembledData, out assembledSize, out headerSize);

            // Reset stats
            counters.ClearSectionStats();
            counters.AddSectionStats("header", headerSize * 8, Color.white);

            // The package was dropped (duplicate or too old) or if it was a fragment not yet assembled, bail out here
            if (packageSequence == 0)
            {
                return;
            }

            var input = default(TInputStream); //  new TInputStream(); due to bug new generates garbage here

            input.Initialize(compressionModel, assembledData, headerSize);

            if ((content & NetworkMessage.ClientInfo) != 0)
            {
                ReadClientInfo(ref input);
            }
            counters.AddSectionStats("clientInfo", input.GetBitPosition2(), Color.green);

            if ((content & NetworkMessage.MapInfo) != 0)
            {
                ReadMapInfo(ref input);
            }
            counters.AddSectionStats("mapInfo", input.GetBitPosition2(), new Color(0.65f, 0.16f, 0.16f));

            // The package was received out of order but older than the last map reset,
            // so we ignore the remaining data
            if (mapInfo.ackSequence == 0 || packageSequence < mapInfo.ackSequence)
            {
                return;
            }

            if ((content & NetworkMessage.Snapshot) != 0)
            {
                ReadSnapshot(packageSequence, ref input, snapshotConsumer);

                // Make sure the callback actually picked up the snapshot data. It is important that
                // every snapshot gets processed by the game so that the spawns, despawns and updates lists
                // does not end up containing stuff from different snapshots
                GameDebug.Assert(spawns.Count == 0 && despawns.Count == 0 && updates.Count == 0, "Game did not consume snapshots");
            }

            // We have to skip this if we dropped snapshot as we will then be in the middle of the input stream
            if ((content & NetworkMessage.Events) != 0 && !m_DropSnapshots)
            {
                ReadEvents(ref input, networkClientConsumer);
            }
            counters.AddSectionStats("events", input.GetBitPosition2(), new Color(1.0f, 0.84f, 0.0f));

            counters.AddSectionStats("unknown", assembledSize * 8, Color.black);

            counters.packageContentStatsPackageSequence = packageSequence;
        }
Beispiel #9
0
    void OnData(int connectionId, byte[] data, int size, INetworkClientCallbacks networkClientConsumer, ISnapshotConsumer snapshotConsumer)
    {
        // Block A number of incoming packets. -1 for all
        if (clientBlockIn.IntValue > 0)
        {
            clientBlockIn.Value = (clientBlockIn.IntValue - 1).ToString();
        }
        if (clientBlockIn.IntValue != 0)
        {
            return;
        }

        // SHould these be asserts?
        if (m_Connection != null && m_Connection.connectionId == connectionId)
        {
#pragma warning disable 0162 // unreached code
            switch (NetworkConfig.ioStreamType)
            {
            case NetworkCompression.IOStreamType.Raw:
            {
                m_Connection.ReadPackage <RawInputStream>(data, size, m_Connection.compressionModel, networkClientConsumer, snapshotConsumer);
                break;
            }

            case NetworkCompression.IOStreamType.Huffman:
            {
                m_Connection.ReadPackage <HuffmanInputStream>(data, size, m_Connection.compressionModel, networkClientConsumer, snapshotConsumer);
                break;
            }

            default:
                GameDebug.Assert(false);
                break;
            }
#pragma warning restore
        }
    }