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; } }
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; }