public void UpdateGameInfo()
    {
        var writer = new NetworkWriter(m_Msg, null);

        writer.WriteInt32("serverPort", m_ServerPort);
        writer.WriteUInt32("token", gameInfo.token);
        writer.WriteString("servername", gameInfo.servername);
        writer.WriteString("levelname", gameInfo.levelname);
        writer.WriteString("gamemode", gameInfo.gamemode);
        writer.WriteInt32("connectedPlayers", gameInfo.connectedPlayers);
        writer.WriteInt32("maxPlayers", gameInfo.maxPlayers);
        writer.Flush();
        m_MsgSize = writer.GetLength();

        if (m_Broadcasting)
        {
            // TODO : We have to restart the broadcast to update the message but we cannot
            // do this in a single frame since we have to wait for some internal state in UNET
            // to reset, so we try 3 times
            NetworkTransport.StopBroadcastDiscovery();
            m_PendingStartAttempts = 3;
        }
    }
Beispiel #2
0
    unsafe public void GenerateSnapshot(ISnapshotGenerator snapshotGenerator, float simTime)
    {
        var time = snapshotGenerator.WorldTick;

        GameDebug.Assert(time > serverTime);      // Time should always flow forward
        GameDebug.Assert(m_MapInfo.mapId > 0);    // Initialize map before generating snapshot

        ++m_ServerSequence;

        // We currently keep entities around until every client has ack'ed the snapshot with the despawn
        // Then we delete them from our list and recycle the id
        // TODO: we do not need this anymore?

        // Find oldest (smallest seq no) acked snapshot.
        var minClientAck = int.MaxValue;

        foreach (var pair in _serverConnections)
        {
            var c = pair.Value;
            // If a client is so far behind that we have to send non-baseline updates to it
            // there is no reason to keep despawned entities around for this clients sake
            if (m_ServerSequence - c.maxSnapshotAck >= NetworkConfig.snapshotDeltaCacheSize - 2) // -2 because we want 3 baselines!
            {
                continue;
            }
            var acked = c.maxSnapshotAck;
            if (acked < minClientAck)
            {
                minClientAck = acked;
            }
        }

        // Recycle despawned entities that have been acked by all
        for (int i = 0; i < m_Entities.Count; i++)
        {
            var e = m_Entities[i];
            if (e.despawnSequence > 0 && e.despawnSequence < minClientAck)
            {
                //if (serverDebugEntityIds.IntValue > 1)
                //    GameDebug.Log("Recycling entity id: " + i + " because despawned in " + e.despawnSequence + " and minAck is now " + minClientAck);
                e.Reset();
                m_FreeEntities.Add(i);
            }
        }

        serverTime      = time;
        m_ServerSimTime = simTime;

        m_LastEntityCount = 0;

        // Grab world snapshot from circular buffer
        var worldsnapshot = m_Snapshots[m_ServerSequence % m_Snapshots.Length];

        worldsnapshot.serverTime = time;
        worldsnapshot.length     = 0;

        // Run through all the registered network entities and serialize the snapshot
        for (var id = 0; id < m_Entities.Count; id++)
        {
            var entity = m_Entities[id];

            // Skip freed
            if (entity.spawnSequence == 0)
            {
                continue;
            }

            // Skip entities that are depawned
            if (entity.despawnSequence > 0)
            {
                continue;
            }

            // If we are here and are despawned, we must be a despawn/spawn in same frame situation
            GameDebug.Assert(entity.despawnSequence == 0 || entity.despawnSequence == entity.spawnSequence, "Snapshotting entity that was deleted in the past?");
            GameDebug.Assert(entity.despawnSequence == 0 || entity.despawnSequence == m_ServerSequence, "WUT");

            // For now we generate the entity type info the first time we generate a snapshot
            // for the particular entity as a more lightweight approach rather than introducing
            // a full schema system where the game code must generate and register the type
            EntityTypeInfo typeInfo;
            bool           generateSchema = false;
            if (!m_EntityTypes.TryGetValue(entity.typeId, out typeInfo))
            {
                typeInfo = new EntityTypeInfo()
                {
                    name = snapshotGenerator.GenerateEntityName(id), typeId = entity.typeId, createdSequence = m_ServerSequence, schema = new NetworkSchema(entity.typeId + NetworkConfig.firstEntitySchemaId)
                };
                m_EntityTypes.Add(entity.typeId, typeInfo);
                generateSchema = true;
            }

            // Generate entity snapshot
            var snapshotInfo = entity.snapshots.Acquire(m_ServerSequence);
            snapshotInfo.start = worldsnapshot.data + worldsnapshot.length;

            var writer = new NetworkWriter(snapshotInfo.start, NetworkConfig.maxWorldSnapshotDataSize / 4 - worldsnapshot.length, typeInfo.schema, generateSchema);
            snapshotGenerator.GenerateEntitySnapshot(id, ref writer);
            writer.Flush();
            snapshotInfo.length = writer.GetLength();

            worldsnapshot.length += snapshotInfo.length;

            if (entity.despawnSequence == 0)
            {
                m_LastEntityCount++;
            }

            GameDebug.Assert(snapshotInfo.length > 0, "Tried to generate a entity snapshot but no data was delivered by generator?");

            if (generateSchema)
            {
                GameDebug.Assert(typeInfo.baseline == null, "Generating schema twice?");
                // First time a type/schema is encountered, we clone the serialized data and
                // use it as the type-baseline
                typeInfo.baseline = (uint *)UnsafeUtility.Malloc(snapshotInfo.length * 4, UnsafeUtility.AlignOf <UInt32>(), Unity.Collections.Allocator.Persistent);// new uint[snapshot.length];// (uint[])snapshot.data.Clone();
                for (int i = 0; i < snapshotInfo.length; i++)
                {
                    typeInfo.baseline[i] = *(snapshotInfo.start + i);
                }
            }

            // Check if it is different from the previous generated snapshot
            var dirty = !entity.snapshots.Exists(m_ServerSequence - 1);
            if (!dirty)
            {
                var previousSnapshot = entity.snapshots[m_ServerSequence - 1];
                if (previousSnapshot.length != snapshotInfo.length || // TODO how could length differ???
                    UnsafeUtility.MemCmp(previousSnapshot.start, snapshotInfo.start, snapshotInfo.length) != 0)
                {
                    dirty = true;
                }
            }

            if (dirty)
            {
                entity.updateSequence = m_ServerSequence;
            }

            //statsGeneratedEntitySnapshots++;
            //statsSnapshotData += snapshotInfo.length;
        }
        //statsGeneratedSnapshotSize += worldsnapshot.length * 4;
    }