Beispiel #1
0
        /// <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);
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Synchronizes the values of attributes given a remote sync info.
        /// </summary>
        /// <param name="remoteAttrSyncInfo">Remote sync info.</param>
        /// <returns>True if the local attribute sync info has been updated.</returns>
        public bool Sync(AttributeSyncInfo remoteAttrSyncInfo)
        {
            if (remoteAttrSyncInfo.LastTimestamp < LastTimestamp)
                return false;

            // Equality sign in "<=" below is very important, because it ensures that we don't have circulating
            // updates. Should an update return back to the sender it will be discarded as it will have same timestamp
            // and same SyncID.
            if (remoteAttrSyncInfo.LastTimestamp == LastTimestamp &&
                remoteAttrSyncInfo.LastSyncID.CompareTo(LastSyncID) <= 0)
                return false;

            LastValue = remoteAttrSyncInfo.LastValue;
            LastTimestamp = remoteAttrSyncInfo.LastTimestamp;
            LastSyncID = remoteAttrSyncInfo.LastSyncID;

            return true;
        }
Beispiel #3
0
        /// <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);
        }