public unsafe static NetworkEvent Serialize(ushort typeId, bool reliable, Dictionary <ushort, NetworkEventType> eventTypes, NetworkEventGenerator generator) { bool generateSchema = false; NetworkEventType type; if (!eventTypes.TryGetValue(typeId, out type)) { generateSchema = true; type = new NetworkEventType() { typeId = typeId, schema = new NetworkSchema(NetworkConfig.firstEventTypeSchemaId + typeId) }; eventTypes.Add(typeId, type); } var result = Create(type, reliable); result.sequence = ++s_Sequence; if (NetworkConfig.netDebug.IntValue > 0) GameDebug.Log("Serializing event " + ((GameNetworkEvents.EventType)result.type.typeId) + " in seq no: " + result.sequence); fixed(uint *data = result.data) { NetworkWriter writer = new NetworkWriter(data, result.data.Length, type.schema, generateSchema); generator(ref writer); writer.Flush(); } return(result); }
private BinaryWriter CreateOutputWriter() { try { string filePath = GenerateNewOutputFile(); if (string.IsNullOrEmpty(filePath)) { Console.WriteLine($"Unable to generate a numbered file name using the prefix: {OutputPrefix}. Try cleaning up the output directory."); return(null); } Console.WriteLine($"Recording to: {filePath}"); Stream stream = null; FileStream fileStream = new FileStream(filePath, FileMode.Create); stream = fileStream; // Write the recording header uncompressed to the file. // We'll rewind here later and update the frame count. // Write to a memory stream to prevent corruption of the file stream when we wrap it // in a GZipStream. MemoryStream headerStream = new MemoryStream(512); BinaryWriter writer = new NetworkWriter(headerStream); WriteRecordingHeader(writer); writer.Flush(); byte[] headerBytes = headerStream.ToArray(); // Copy header to the file. fileStream.Write(headerBytes, 0, headerBytes.Length); // Dispose of the temporary objects. headerBytes = null; writer = null; headerStream = null; switch (DecodeMode) { case Mode.CollateAndCompress: stream = new CollationStream(fileStream, true); break; case Mode.CollateOnly: stream = new CollationStream(fileStream, false); break; case Mode.FileCompression: stream = new GZipStream(fileStream, CompressionLevel.Fastest); break; case Mode.Uncompressed: stream = fileStream; break; } return(new NetworkWriter(stream)); } catch (Exception e) { Console.Error.WriteLine(e); } return(null); }
unsafe public void InitializeMap(DataGenerator generator) { // Generate schema the first time we set map info bool generateSchema = false; if (m_MapInfo.schema == null) { m_MapInfo.schema = new NetworkSchema(NetworkConfig.mapSchemaId); generateSchema = true; } // Update map info var writer = new NetworkWriter(m_MapInfo.data, 1024, m_MapInfo.schema, generateSchema); generator(ref writer); writer.Flush(); m_MapInfo.serverInitSequence = m_ServerSequence; ++m_MapInfo.mapId; // Reset map and connection state serverTime = 0; //m_Entities.Clear(); //m_FreeEntities.Clear(); foreach (var pair in _serverConnections) { pair.Value.Reset(); } }
public void QueueCommand(int time, DataGenerator generator) { var generateSchema = (commandSchema == null); if (generateSchema) { commandSchema = new NetworkSchema(NetworkConfig.networkClientQueueCommandSchemaId); } var info = commandsOut.Acquire(++commandSequence); info.time = time; var writer = new NetworkWriter(info.data, commandSchema, generateSchema); generator(ref writer); writer.Flush(); }
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 static void WriteValues(List <object> values, uint[] buffer, NetworkSchema schema) { fixed(uint *buf = buffer) { NetworkWriter writer = new NetworkWriter(buf, buffer.Length, schema); for (int j = 0; j < values.Count; ++j) { var value = values[j]; var field = schema.fields[j]; string fieldName = "field_" + j; if (value is bool) { writer.WriteBoolean(fieldName, (bool)value); } else if (value is byte) { writer.WriteByte(fieldName, (byte)value); } else if (value is ushort) { writer.WriteUInt16(fieldName, (ushort)value); } else if (value is short) { writer.WriteInt16(fieldName, (short)value); } else if (value is uint) { writer.WriteUInt32(fieldName, (uint)value); } else if (value is int) { writer.WriteInt32(fieldName, (int)value); } else if (value is float) { if (field.delta) { writer.WriteFloatQ(fieldName, (float)value, field.precision); } else { writer.WriteFloat(fieldName, (float)value); } } else if (value is Vector2) { if (field.delta) { writer.WriteVector2Q(fieldName, (Vector2)value, field.precision); } else { writer.WriteVector2(fieldName, (Vector2)value); } } else if (value is Vector3) { if (field.delta) { writer.WriteVector3Q(fieldName, (Vector3)value, field.precision); } else { writer.WriteVector3(fieldName, (Vector3)value); } } else if (value is Quaternion) { if (field.delta) { writer.WriteQuaternionQ(fieldName, (Quaternion)value, field.precision); } else { writer.WriteQuaternion(fieldName, (Quaternion)value); } } else if (value is string) { writer.WriteString(fieldName, (string)value, 1024); } else if (value is byte[]) { writer.WriteBytes(fieldName, (byte[])value, 0, 10, 1024); } else if (value == null && field.fieldType == NetworkSchema.FieldType.String) { writer.WriteString(fieldName, null, 1024); } else { Assert.Fail(); } } writer.Flush(); } }
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; }