/// <summary> /// Relays the changes to attributes to other connected sync nodes and process them locally. /// </summary> /// <param name="guid">Guid of the entity containing affected attributes.</param> /// <param name="changedAttributes">A set of modified attributes with their remote sync info.</param> private void HandleRemoteChangedAttributes(Connection connection, Guid guid, EntitySyncInfo changedAttributes) { if (IsSyncRelay) { RelaySyncMessage(connection, "changeAttributes", guid, changedAttributes); } ProcessChangedAttributes(guid, changedAttributes); }
/// <summary> /// Creates a new sync for an entity. /// </summary> /// <param name="entity">Entity for which sync info is to be created.</param> /// <returns>Created sync info.</returns> private EntitySyncInfo CreateSyncInfoForNewEntity(Entity entity) { var entitySyncInfo = new EntitySyncInfo(); foreach (Component component in entity.Components) { foreach (ReadOnlyAttributeDefinition attrDefinition in component.Definition.AttributeDefinitions) { entitySyncInfo[component.Name][attrDefinition.Name] = new AttributeSyncInfo(LocalSyncID, component[attrDefinition.Name]); } } return(entitySyncInfo); }
/// <summary> /// Handler for the event ChangedAttribute event in the Entity. Update local sync info for the attribute and /// invokes changedAttributes method on the connected sync nodes to notify them about the change. /// </summary> /// <param name="sender">Event sender.</param> /// <param name="e">Event arguments.</param> private void HandleLocalChangedAttribute(object sender, ChangedAttributeEventArgs e) { var componentName = e.Component.Name; var attributeName = e.AttributeName; // Ignore this change if it was caused by the scalability plugin itself. lock (remoteAttributeChanges) { foreach (RemoteAttributeChange change in remoteAttributeChanges) { if (change.Entity == e.Entity && change.ComponentName == componentName && change.AttributeName == attributeName && change.Value == e.NewValue) { remoteAttributeChanges.Remove(change); return; } } } var newAttributeSyncInfo = new AttributeSyncInfo(LocalSyncID, e.NewValue); lock (localSyncInfo) { if (!localSyncInfo.ContainsKey(e.Entity.Guid)) { logger.Warn("Local attribute changed in an entity which has no sync info."); return; } EntitySyncInfo entitySyncInfo = localSyncInfo[e.Entity.Guid]; entitySyncInfo[componentName][attributeName] = newAttributeSyncInfo; } // TODO: Optimization: we can send batch updates to improve performance. Received code is written to // process batches already, but sending is trickier as we don't have network load feedback from KIARA yet. var changedAttributes = new EntitySyncInfo(); changedAttributes[componentName][attributeName] = newAttributeSyncInfo; lock (remoteSyncNodes) { foreach (Connection connection in remoteSyncNodes) { connection["changeAttributes"](e.Entity.Guid, changedAttributes); } } }
/// <summary> /// Processes changes to a set of attributes on the remote sync node and updates local values accordingly. /// </summary> /// <param name="guid">Guid of the entity containing affected attributes.</param> /// <param name="changedAttributes">A set of modified attributes with their remote sync info.</param> private void ProcessChangedAttributes(Guid guid, EntitySyncInfo changedAttributes) { lock (localSyncInfo) { if (!localSyncInfo.ContainsKey(guid)) { logger.Warn("Ignoring changes to attributes in an entity that does not exist. Guid: " + guid); return; } Entity localEntity = World.Instance.FindEntity(guid); foreach (KeyValuePair <string, ComponentSyncInfo> componentPair in changedAttributes.Components) { foreach (KeyValuePair <string, AttributeSyncInfo> attributePair in componentPair.Value.Attributes) { HandleRemoteChangedAttribute(localEntity, componentPair.Key, attributePair.Key, attributePair.Value); } } } }
/// <summary> /// Handles an entity addition update from the remote sync node and adds a new entity locally. /// </summary> /// <param name="guid">Guid of the new entity.</param> /// <param name="initialSyncInfo">Initial sync info for the entity.</param> private void HandleRemoteAddedEntity(Connection connection, Guid guid, EntitySyncInfo initialSyncInfo) { lock (localSyncInfo) { if (IsSyncRelay) { RelaySyncMessage(connection, "addEntity", guid, initialSyncInfo); } if (!localSyncInfo.ContainsKey(guid)) { localSyncInfo.Add(guid, new EntitySyncInfo()); lock (remoteEntityAdditions) remoteEntityAdditions.Add(guid); World.Instance.Add(new Entity(guid)); } else { logger.Warn("Processing addition of already existing entity. Guid: " + guid); } ProcessChangedAttributes(guid, initialSyncInfo); } }
/// <summary> /// Handles an update to a single attribute. /// </summary> /// <param name="localEntity">Entity containing attribute.</param> /// <param name="componentName">Name of the component containing attribute.</param> /// <param name="attributeName">Name of the attribute.</param> /// <param name="remoteAttributeSyncInfo">Remote sync info on this attribute.</param> /// <returns>True if the update should be propagated to other sync nodes.</returns> private bool HandleRemoteChangedAttribute(Entity localEntity, string componentName, string attributeName, AttributeSyncInfo remoteAttributeSyncInfo) { EntitySyncInfo localEntitySyncInfo = localSyncInfo[localEntity.Guid]; bool shouldUpdateLocalAttribute = false; if (!localEntitySyncInfo.Components.ContainsKey(componentName) || !localEntitySyncInfo[componentName].Attributes.ContainsKey(attributeName)) { shouldUpdateLocalAttribute = true; localEntitySyncInfo[componentName][attributeName] = remoteAttributeSyncInfo; } else { AttributeSyncInfo localAttributeSyncInfo = localEntitySyncInfo[componentName][attributeName]; shouldUpdateLocalAttribute = (localAttributeSyncInfo.LastValue == null && remoteAttributeSyncInfo.LastValue != null) || (localAttributeSyncInfo.LastValue != null && !localAttributeSyncInfo.LastValue.Equals(remoteAttributeSyncInfo.LastValue)); if (!localAttributeSyncInfo.Sync(remoteAttributeSyncInfo)) { return(false); // ignore this update because sync discarded it } } if (shouldUpdateLocalAttribute) { try { // This is necessary, because Json.NET serializes primitive types using basic JSON values, which do // not retain original type (e.g. integer values always become int even if they were stored as // float values before) and there is no way to change this. var attributeType = localEntity[componentName].Definition[attributeName].Type; var attributeValue = Convert.ChangeType(remoteAttributeSyncInfo.LastValue, attributeType); // Ignore event for this change. lock (remoteAttributeChanges) { var remoteChange = new RemoteAttributeChange { Entity = localEntity, ComponentName = componentName, AttributeName = attributeName, Value = attributeValue }; remoteAttributeChanges.Add(remoteChange); } localEntity[componentName][attributeName] = attributeValue; return(true); } catch (ComponentAccessException) { // This is fine, because we may have some plugins not loaded on this node. } } // If we are here, it means that this update was not discarded by sync and thus should be propagated to // other nodes. This is still the case even if the value has been the same and local attribute has not been // update, because we still need to update sync info on other nodes. return(true); }