public void ReloadModel() { TypesByName.Clear(); TypesByType.Clear(); var context = Getter(); var types = context.Model.GetEntityTypes(); foreach (var t in types) { var ti = new EntityTypeInfo() { Name = t.ClrType.Name, PrimaryKey = t.FindPrimaryKey().Properties.FirstOrDefault()?.PropertyInfo, Type = t, //Getter = getter }; TypesByName.Add(t.ClrType.Name, ti); TypesByType.Add(t.ClrType, ti); if (!DB.ContainsKey(t.ClrType)) { DB.Add(t.ClrType, new Dictionary <object, WeakReference>()); } } }
public ViewResultBase Details() { if (!string.IsNullOrEmpty(Request["isTooltip"])) { Guid id; if (Guid.TryParse(Request["id"], out id)) { var data = new EntityTypeInfo(base.EntityType.GetData(id)); return(new PartialViewResult { ViewName = "Partials/Details", ViewData = new ViewDataDictionary(data) }); } else { throw new ValidationException("非法的Guid标识" + Request["id"]); } } else if (!string.IsNullOrEmpty(Request["isInner"])) { return(new PartialViewResult { ViewName = "Partials/Details" }); } else { return(this.View()); } }
public void Test() { TestClass1 o = new TestClass1(); EntityTypeInfo et = o.GetEntityInfo(); Assert.IsNotNull(et); Assert.AreEqual(101, et.ID); Assert.AreEqual(EntityState.Detached, et.EntityState); }
internal void A(string entityTypeName, EntityType newEntityType) { Trace.Assert(this.FindEntityTypeInfo(entityTypeName) == null); EntityTypeInfo typeInfo = new EntityTypeInfo(); typeInfo.entityTypeName = entityTypeName; typeInfo.newEntityType = newEntityType; this.aAf.Add(typeInfo); }
private EntityTypeInfo GetOrCreateEntityTypeInfo(ResourceType entityType) { if (_types.TryGetValue(entityType, out var info)) { return(info); } return(_types[entityType] = new EntityTypeInfo()); }
public static EntityTypeInfo GetEntityType(int id) { EntityTypeInfo ret = null; using (EntityMetadataContext ctx = EntityMetadataContext.CreateInstance()) { ret = ctx.GetEntityType(id); } return(ret); }
public void Reset() { type = null; lastUpdateSequence = 0; despawnSequence = 0; fieldMask = 0; // TODO needed? for (var i = 0; i < lastUpdate.Length; i++) { lastUpdate[i] = 0; } baselines.Clear(); }
public EntityTypeInfo GetEntityTypeInfo(Type type, bool addToEntityTypes = false) { lock (this) { EntityTypeInfo info; if (!entityTypeInfos.TryGetValue(type ?? throw new ArgumentNullException(nameof(type)), out info)) { info = new EntityTypeInfo(type, this); entityTypeInfos[type] = info; if (addToEntityTypes) { AddEntityType(type); } } return(info); } }
/// <summary> /// Gets the entity type information from the merge field identifier. /// </summary> /// <param name="mergeFieldId">The merge field identifier.</param> /// <returns></returns> public static EntityTypeInfo GetEntityTypeInfoFromMergeFieldId(string mergeFieldId) { var entityTypeInfo = new EntityTypeInfo(); var entityTypeParts = mergeFieldId.Split(new char[] { '~' }, StringSplitOptions.RemoveEmptyEntries); var entityTypeName = entityTypeParts[0]; var entityType = EntityTypeCache.Get(entityTypeName, false); if (entityType?.IsEntity == true) { entityTypeInfo.EntityType = entityType; } else { return(null); } if (entityTypeParts.Length > 1) { var entityTypeQualifiersParts = entityTypeParts.Skip(1).ToArray(); var qualifiers = new List <EntityTypeInfo.EntityTypeQualifier>(); foreach (var entityTypeQualifiersPart in entityTypeQualifiersParts) { var qualifierParts = entityTypeQualifiersPart.Split(new char[] { '+', ' ' }).ToArray(); if (qualifierParts.Length == 2) { qualifiers.Add(new EntityTypeInfo.EntityTypeQualifier(qualifierParts[0], qualifierParts[1])); } } entityTypeInfo.EntityTypeQualifiers = qualifiers.ToArray(); } return(entityTypeInfo); }
private void AddEntity <TEntity>(TEntity entity, EntityState state) { if (entity == null) { return; } if (!_typeEntities.TryGetValue(typeof(TEntity), out var entityTypeInfo)) { entityTypeInfo = new EntityTypeInfo(_entityMetadataProvider.GetMetadata(typeof(TEntity))); _typeEntities.Add(typeof(TEntity), entityTypeInfo); } var persister = entityTypeInfo.EntityMetadata.EntityPersister; if (state == EntityState.Modified && persister.IsVersioned) { // Simulate breeze js behavior by increasing the version number var version = persister.GetPropertyValue(entity, persister.VersionProperty); persister.SetPropertyValue(entity, persister.VersionProperty, persister.VersionType.Next(version, null)); } entityTypeInfo.AddEntity(entity, state); }
public ViewResultBase Details() { if (!string.IsNullOrEmpty(Request["isTooltip"])) { Guid id; if (Guid.TryParse(Request["id"], out id)) { var data = new EntityTypeInfo(base.EntityType.GetData(id)); return new PartialViewResult { ViewName = "Partials/Details", ViewData = new ViewDataDictionary(data) }; } else { throw new ValidationException("非法的Guid标识" + Request["id"]); } } else if (!string.IsNullOrEmpty(Request["isInner"])) { return new PartialViewResult { ViewName = "Partials/Details" }; } else { return this.View(); } }
void ReadSnapshot <TInputStream>(int sequence, ref TInputStream input) 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 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((baseSequence > 0 ? "Snap [BL]" : "Snap [ ]") + "(" + sequence + ") " + baseSequence + " - " + baseSequence1 + " - " + baseSequence2); } else { GameDebug.Log((baseSequence > 0 ? "Snap [BL]" : "Snap [ ]") + "(" + sequence + ") " + baseSequence); } } if (baseSequence == 0) { counters.fullSnapshotsIn++; } GameDebug.Assert(baseSequence == 0 || (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(baseSequence != 0 ? 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)); } // 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); entityType.baseline = new byte[NetworkConfig.maxEntitySnapshotDataSize]; NetworkSchema.CopyFieldsToBuffer(entityType.schema, ref input, entityType.baseline); if (!entityTypes.ContainsKey(typeId)) { entityTypes.Add(typeId, entityType); } } // 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); // Check if we already registered the entity since we can receive spawn information // in several snapshots before the client has ack a package containing the spawn // TODO (petera) need an max entity id for safety while (id >= entities.Count) { entities.Add(new EntityInfo()); } if (entities[id].type == null) { var e = entities[id]; e.type = entityTypes[typeId]; e.fieldMask = fieldMask; spawns.Add(id); } m_TempSpawnList.Add(id); } // Read despawns var despawnCount = input.ReadPackedUInt(NetworkConfig.despawnCountContext); 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); } // 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; byte[] baseline0 = info.type.baseline; GameDebug.Assert(baseline0 != null, "Unable to find schema baseline for type {0}", info.type.typeId); if (baseSequence != 0 && !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; byte[] baseline1 = null; byte[] 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; } NetworkPrediction.PredictSnapshot(info.prediction, info.fieldsChangedPrediction, info.type.schema, num_baselines, (uint)baseline0Time, baseline0, (uint)baseline1Time, baseline1, (uint)baseline2Time, baseline2, (uint)snapshotInfo.serverTime, info.fieldMask); } else { var f = info.fieldsChangedPrediction; for (var i = 0; i < f.Length; ++i) { f[i] = 0; } NetworkUtils.MemCopy(baseline0, 0, info.prediction, 0, info.type.schema.GetByteSize()); } } // 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. NetworkUtils.MemCopy(info.prediction, 0, tempSnapshotBuffer, 0, info.type.schema.GetByteSize()); 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 + ") " + (baseSequence > 0 ? "Snap [BL]" : "Snap [ ]") + " " + baseSequence + " - " + baseSequence1 + " - " + baseSequence2 + ". Sche: " + schemaCount + " Spwns: " + spawnCount + " Desp: " + despawnCount + " Upd: " + updateCount); } else { GameDebug.Assert(false, "Snapshot (" + snapshotInfo.serverTime + ") " + (baseSequence > 0 ? "Snap [BL]" : "Snap [ ]") + " " + baseSequence + ". Sche: " + schemaCount + " Spwns: " + spawnCount + " Desp: " + despawnCount + " Upd: " + updateCount); } } } } 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); NetworkUtils.MemCopy(info.prediction, 0, data, 0, schemaSize); if (sequence > info.lastUpdateSequence) { if (!updates.Contains(id)) { updates.Add(id); } NetworkUtils.MemCopy(info.prediction, 0, info.lastUpdate, 0, schemaSize); info.lastUpdateSequence = sequence; } } if (enableHashing && info.despawnSequence == 0) { snapshotHash += NetworkUtils.SimpleHash(info.prediction, schemaSize); numEnts++; } } if (clientDebug.IntValue > 1) { if (clientDebug.IntValue > 2 || spawnCount > 0 || despawnCount > 0 || schemaCount > 0 || baseSequence == 0) { string entityIds = ""; for (var i = 0; i < entities.Count; i++) { entityIds += entities[i].type == null ? ",-" : ("," + i); } string despawnIds = string.Join(",", despawns); string spawnIds = string.Join(",", spawns); string updateIds = string.Join(",", updates); if (enableNetworkPrediction) { GameDebug.Log((baseSequence > 0 ? "Snap [BL]" : "Snap [ ]") + " " + baseSequence + " - " + baseSequence1 + " - " + baseSequence2 + ". Sche: " + schemaCount + " Spwns: " + spawnCount + "(" + spawnIds + ") Desp: " + despawnCount + "(" + despawnIds + ") Upd: " + updateCount + "(" + updateIds + ") Ents:" + entities.Count + " EntityIds:" + entityIds); } else { GameDebug.Log((baseSequence > 0 ? "Snap [BL]" : "Snap [ ]") + " " + baseSequence + ". Sche: " + schemaCount + " Spwns: " + spawnCount + "(" + spawnIds + ") Desp: " + despawnCount + "(" + despawnIds + ") Upd: " + updateCount + "(" + updateIds + ") Ents:" + entities.Count + " EntityIds:" + entityIds); } } } if (enableHashing) { uint numEntsCheck = input.ReadRawBits(32); if (numEntsCheck != numEnts) { GameDebug.Log("SYNC PROBLEM: server num ents: " + numEntsCheck + " us:" + numEnts); GameDebug.Assert(false); } } }
public PropertyElement(string name, string type) { Name = name; Type = EntityTypeInfo.GetTypeShortName(type); CellStyle = UITableViewCellStyle.Value1; }
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; }
internal static bool ResolveEntityProperties(IList <JsonProperty> jsonProperties, Type entityType, EntityTypeInfo entityTypeInfo, EntityService entityService, DefaultContractResolver resolver, Func <PropertyInfo, JsonProperty> createPropertyFunc) { if (entityService.ContainsEntityType(entityType)) { if (jsonProperties.Any(jp => jp.PropertyName == Defaults.MetadataPropertyName && jp.UnderlyingName == Defaults.DummyMetadataPropertyInfo.Name)) { return(false); //if we ever find the metadata property there, assume it has been resolved. } var _properties = new JsonPropertyCollection(entityType); var isComplex = entityType.IsComplex(); var propertiesToIgnore = entityTypeInfo.PropertiesToIgnore.ToArray(); foreach (var jsonProp in jsonProperties) { if (isComplex) { jsonProp.NullValueHandling = NullValueHandling.Include; //we need null values for complex types jsonProp.DefaultValueHandling = DefaultValueHandling.Include; //we need all properties serialized } if (!jsonProp.Ignored && propertiesToIgnore.Any(np => np.Name == jsonProp.UnderlyingName)) { jsonProp.Ignored = true; } _properties.Add(jsonProp); } lock (entityTypeInfo) { //check for complextypes var complexTypedProperties = entityTypeInfo.ComplexTypedProperties; var complexJsonProperties = new List <JsonProperty>(); if (complexTypedProperties?.Count > 0) { //filter to complexproperties var filteredJsonProperties = _properties? .Select(p => new { JsonProperty = p, PropertyInfo = complexTypedProperties.Where(pi => pi.Name == p.UnderlyingName) .FirstOrDefault() }) .Where(np => np.PropertyInfo != null) .ToDictionary(np => np.JsonProperty, np => np.PropertyInfo); Func <Type, IList <JsonProperty> > getResolvedPropertiesFunc = t => { var contract = resolver.ResolveContract(t) as JsonObjectContract; if (contract.Properties?.Count > 0) { ResolveEntityProperties (contract.Properties, t, entityService.GetEntityTypeInfo(t), entityService, resolver, createPropertyFunc); } return(contract.Properties); }; //generate new properties with new names for the complex types foreach (var complexTypedJsonProp in filteredJsonProperties) { //get the complexTypedProperty's own jsonproperties //include derived classes var derivedTypes = entityService .GetDerivedEntityTypes(complexTypedJsonProp.Key.PropertyType)? .Where(t => t.IsComplex()).ToList(); if (derivedTypes == null || derivedTypes.Count == 0) { entityService.AddEntityType(complexTypedJsonProp.Key.PropertyType); derivedTypes = new List <Type> { complexTypedJsonProp.Key.PropertyType }; } var childProperties = derivedTypes .SelectMany(dt => getResolvedPropertiesFunc(dt)?.Where (p => /*!p.Ignored &&*/ p.PropertyName != Defaults.MetadataPropertyName) ?? new JsonProperty[0], (dt, property) => new { DerivedType = dt, Property = property }) .Where(jp => jp.Property != null) .GroupBy(jp => jp.Property.PropertyName) .Select(jpg => jpg.FirstOrDefault()) .ToList(); foreach (var childProp in childProperties) { //add the child to this type's properties try { var newChildProp = GetComplexTypedPropertyChild (childProp.DerivedType, complexTypedJsonProp.Key, complexTypedJsonProp.Value, childProp.Property); _properties.AddProperty(newChildProp); complexJsonProperties.Add(newChildProp); } catch (JsonSerializationException e) { //for some reason member already exists and is duplicate } } //ignore all complex typed properties complexTypedJsonProp.Key.Ignored = true; } } var nextIdx = -1; //clear and re-add these properties jsonProperties.Clear(); var orderedProperties = _properties.OrderBy(p => p.Order ?? nextIdx++); foreach (var prop in orderedProperties) { jsonProperties.Add(prop); } //create metadata property and add it last var metadataJsonProperty = createPropertyFunc(Defaults.DummyMetadataPropertyInfo); metadataJsonProperty.PropertyName = Defaults.MetadataPropertyName; metadataJsonProperty.ValueProvider = new MetadataValueProvider(entityType, complexJsonProperties); metadataJsonProperty.ShouldSerialize = instance => { return(!(metadataJsonProperty.ValueProvider as MetadataValueProvider) .BuildMetadata(instance).IsEmpty()); }; metadataJsonProperty.Order = int.MaxValue; //try to make it the last serialized jsonProperties.Add(metadataJsonProperty); //assign and resolve these properties entityTypeInfo.JsonProperties = new List <JsonProperty>(jsonProperties); return(true); } } return(false); }
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++; } }
private void InitializeEntityInfo(EntityTypeInfo entityInfo, JsonSerializer serializer) { entityInfo.ResolveJsonPropertiesUsing(serializer.ContractResolver as DefaultContractResolver); }