示例#1
0
        /// <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>
        internal void HandleRemoteChangedAttributes(Connection connection, string id, EntitySyncInfo changedAttributes)
        {
            Guid   guid   = new Guid(id);
            Entity entity = World.Instance.FindEntity(guid);

            foreach (IRemoteServer server in ServerSync.RemoteServers)
            {
                if (server.Connection == connection)
                {
                    continue;
                }

                EntitySyncInfo filteredAttributes       = new EntitySyncInfo();
                bool           containsAttributesToSync = false;
                foreach (var component in changedAttributes.Components)
                {
                    foreach (var attribute in component.Value.Attributes)
                    {
                        if (server.DoI.IsInterestedInAttributeChange(entity, component.Key, attribute.Key))
                        {
                            filteredAttributes[component.Key][attribute.Key] = attribute.Value;
                            containsAttributesToSync = true;
                        }
                    }
                }

                if (containsAttributesToSync)
                {
                    server.Connection["serverSync.changeAttributes"](id, filteredAttributes);
                }
            }

            ProcessChangedAttributes(guid, changedAttributes);
        }
示例#2
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 = syncInfo[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 = remoteAttributeSyncInfo.LastValue;

                    // Ignore event for this change.
                    lock (ignoredAttributeChanges)
                    {
                        ignoredAttributeChanges.Add(new AttributeUpdate(localEntity.Guid, componentName, attributeName,
                                                                        attributeValue));
                    }

                    localEntity[componentName][attributeName].Suggest(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);
        }
        /// <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(ServerSync.LocalServer.SyncID, component[attrDefinition.Name].Value);
                }
            }
            return(entitySyncInfo);
        }
示例#4
0
        public void ShouldChangeAttributesOnUpdate()
        {
            var entity = new Entity();

            World.Instance.Add(entity);

            var changedAttributes = new EntitySyncInfo();

            changedAttributes["test"]["a"] = new AttributeSyncInfo(Guid.NewGuid().ToString(), 99);

            var worldSync = new WorldSync();

            worldSync.HandleRemoteChangedAttributes(remoteConnectionMock.Object, entity.Guid.ToString(), changedAttributes);

            Assert.AreEqual(entity["test"]["a"].Value, 99);
        }
示例#5
0
        public void ShouldNotSendUpdatesWhenTheyResultFromRemoteUpdate()
        {
            var changedAttributes = new EntitySyncInfo();

            changedAttributes["test"]["a"] = new AttributeSyncInfo(Guid.NewGuid().ToString(), 99);

            var entity    = new Entity();
            var worldSync = new WorldSync();

            worldSync.HandleRemoteAddedEntity(remoteConnectionMock.Object, entity.Guid.ToString(), entity.Owner.ToString(), new EntitySyncInfo());
            worldSync.HandleRemoteChangedAttributes(remoteConnectionMock.Object, entity.Guid.ToString(), new EntitySyncInfo());
            worldSync.HandleRemoteRemovedEntity(remoteConnectionMock.Object, entity.Guid.ToString());

            handlers.Verify(h => h.AddEntity(entity.Guid.ToString(), It.IsAny <EntitySyncInfo>()), Times.Never());
            handlers.Verify(h => h.ChangeAttributes(entity.Guid.ToString(), It.IsAny <EntitySyncInfo>()), Times.Never());
            handlers.Verify(h => h.RemoveEntity(entity.Guid.ToString()), Times.Never());
        }
示例#6
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>
        internal 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 (ignoredAttributeChanges)
            {
                var attributeUpdate = new AttributeUpdate(e.Entity.Guid, componentName, attributeName, e.NewValue);
                if (ignoredAttributeChanges.Remove(attributeUpdate))
                {
                    return;
                }
            }

            var newAttributeSyncInfo =
                new AttributeSyncInfo(ServerSync.LocalServer.SyncID.ToString(), e.NewValue);

            lock (syncInfo)
            {
                if (!syncInfo.ContainsKey(e.Entity.Guid))
                {
                    logger.Warn("Local attribute changed in an entity which has no sync info.");
                    return;
                }

                EntitySyncInfo entitySyncInfo = syncInfo[e.Entity.Guid];
                entitySyncInfo[componentName][attributeName] = newAttributeSyncInfo;
            }

            var changedAttributes = new EntitySyncInfo();

            changedAttributes[componentName][attributeName] = newAttributeSyncInfo;
            foreach (IRemoteServer server in ServerSync.RemoteServers)
            {
                if (server.DoI.IsInterestedInAttributeChange(e.Entity, e.Component.Name, e.AttributeName))
                {
                    server.Connection["serverSync.changeAttributes"](e.Entity.Guid.ToString(), changedAttributes);
                }
            }
        }
示例#7
0
        /// <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 (syncInfo)
            {
                if (!syncInfo.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);
                    }
                }
            }
        }
示例#8
0
        public void ShouldForwardUpdatesToServersOtherThanTheSource()
        {
            var otherConnectionMock = new Mock <Connection>();
            var handlers2           = new Mock <IHandlers>();

            otherConnectionMock.Setup(rc => rc.GenerateClientFunction("serverSync", "addEntity"))
            .Returns((ClientFunction)handlers2.Object.AddEntity);
            otherConnectionMock.Setup(rc => rc.GenerateClientFunction("serverSync", "removeEntity"))
            .Returns((ClientFunction)handlers2.Object.RemoveEntity);
            otherConnectionMock.Setup(rc => rc.GenerateClientFunction("serverSync", "changeAttributes"))
            .Returns((ClientFunction)handlers2.Object.ChangeAttributes);

            var guid = Guid.NewGuid();
            RemoteServerImpl remoteServer2 = new RemoteServerImpl(otherConnectionMock.Object, doiMock.Object,
                                                                  dorMock.Object, guid);

            serverSyncMock.Setup(ss => ss.RemoteServers).Returns(
                new List <IRemoteServer> {
                remoteServer, remoteServer2
            });

            var changedAttributes = new EntitySyncInfo();

            changedAttributes["test"]["a"] = new AttributeSyncInfo(Guid.NewGuid().ToString(), 99);

            var entity    = new Entity();
            var worldSync = new WorldSync();

            worldSync.HandleRemoteAddedEntity(remoteConnectionMock.Object, entity.Guid.ToString(), entity.Owner.ToString(), new EntitySyncInfo());
            worldSync.HandleRemoteChangedAttributes(remoteConnectionMock.Object, entity.Guid.ToString(), changedAttributes);
            worldSync.HandleRemoteRemovedEntity(remoteConnectionMock.Object, entity.Guid.ToString());

            handlers.Verify(h => h.AddEntity(entity.Guid.ToString(), It.IsAny <EntitySyncInfo>()), Times.Never());
            handlers.Verify(h => h.ChangeAttributes(entity.Guid.ToString(), It.IsAny <EntitySyncInfo>()), Times.Never());
            handlers.Verify(h => h.RemoveEntity(entity.Guid.ToString()), Times.Never());

            handlers2.Verify(h => h.AddEntity(entity.Guid.ToString(), entity.Owner.ToString(), It.IsAny <EntitySyncInfo>()), Times.Once());
            handlers2.Verify(h => h.ChangeAttributes(entity.Guid.ToString(), It.IsAny <EntitySyncInfo>()), Times.Once());
            handlers2.Verify(h => h.RemoveEntity(entity.Guid.ToString()), Times.Once());
        }
        public void ShouldChangeAttributesOnUpdate()
        {
            var entity = new Entity();
            World.Instance.Add(entity);

            var changedAttributes = new EntitySyncInfo();
            changedAttributes["test"]["a"] = new AttributeSyncInfo(Guid.NewGuid(), 99);

            var worldSync = new WorldSync();
            worldSync.HandleRemoteChangedAttributes(remoteConnectionMock.Object, entity.Guid, changedAttributes);

            Assert.AreEqual(entity["test"]["a"].Value, 99);
        }
 /// <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(ServerSync.LocalServer.SyncID, component[attrDefinition.Name].Value);
         }
     }
     return entitySyncInfo;
 }
        /// <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>
        internal void HandleRemoteChangedAttributes(Connection connection, Guid guid, EntitySyncInfo changedAttributes)
        {
            Entity entity = World.Instance.FindEntity(guid);
            foreach (IRemoteServer server in ServerSync.RemoteServers)
            {
                if (server.Connection == connection)
                    continue;

                EntitySyncInfo filteredAttributes = new EntitySyncInfo();
                bool containsAttributesToSync = false;
                foreach (var component in changedAttributes.Components)
                {
                    foreach (var attribute in component.Value.Attributes)
                    {
                        if (server.DoI.IsInterestedInAttributeChange(entity, component.Key, attribute.Key))
                        {
                            filteredAttributes[component.Key][attribute.Key] = attribute.Value;
                            containsAttributesToSync = true;
                        }
                    }
                }

                if (containsAttributesToSync)
                    server.Connection["serverSync.changeAttributes"](guid, filteredAttributes);
            }

            ProcessChangedAttributes(guid, changedAttributes);
        }
        internal void HandleRemoteAddedEntity(Connection connection, Guid guid, Guid owner, EntitySyncInfo initialSyncInfo)
        {
            lock (syncInfo)
            {
                if (!syncInfo.ContainsKey(guid))
                {
                    syncInfo.Add(guid, new EntitySyncInfo());
                    lock (ignoredEntityAdditions)
                        ignoredEntityAdditions.Add(guid);
                    World.Instance.Add(new Entity(guid, owner));
                }
                else
                {
                    logger.Warn("Processing addition of already existing entity. Guid: " + guid);
                    return;
                }

                ProcessChangedAttributes(guid, initialSyncInfo);

                Entity entity = World.Instance.FindEntity(guid);
                foreach (IRemoteServer server in ServerSync.RemoteServers)
                    if (server.Connection != connection && server.DoI.IsInterestedInEntity(entity))
                        server.Connection["serverSync.addEntity"](guid, initialSyncInfo);
            }
        }
        /// <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>
        internal 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 (ignoredAttributeChanges)
            {
                var attributeUpdate = new AttributeUpdate(e.Entity.Guid, componentName, attributeName, e.NewValue);
                if (ignoredAttributeChanges.Remove(attributeUpdate))
                    return;
            }

            var newAttributeSyncInfo = new AttributeSyncInfo(ServerSync.LocalServer.SyncID, e.NewValue);
            lock (syncInfo)
            {
                if (!syncInfo.ContainsKey(e.Entity.Guid))
                {
                    logger.Warn("Local attribute changed in an entity which has no sync info.");
                    return;
                }

                EntitySyncInfo entitySyncInfo = syncInfo[e.Entity.Guid];
                entitySyncInfo[componentName][attributeName] = newAttributeSyncInfo;
            }

            var changedAttributes = new EntitySyncInfo();
            changedAttributes[componentName][attributeName] = newAttributeSyncInfo;
            foreach (IRemoteServer server in ServerSync.RemoteServers)
                if (server.DoI.IsInterestedInAttributeChange(e.Entity, e.Component.Name, e.AttributeName))
                    server.Connection["serverSync.changeAttributes"](e.Entity.Guid, changedAttributes);
        }
        public void ShouldNotSendUpdatesWhenTheyResultFromRemoteUpdate()
        {
            var changedAttributes = new EntitySyncInfo();
            changedAttributes["test"]["a"] = new AttributeSyncInfo(Guid.NewGuid(), 99);

            var entity = new Entity();
            var worldSync = new WorldSync();
            worldSync.HandleRemoteAddedEntity(remoteConnectionMock.Object, entity.Guid, entity.Owner, new EntitySyncInfo());
            worldSync.HandleRemoteChangedAttributes(remoteConnectionMock.Object, entity.Guid, new EntitySyncInfo());
            worldSync.HandleRemoteRemovedEntity(remoteConnectionMock.Object, entity.Guid);

            handlers.Verify(h => h.AddEntity(entity.Guid, It.IsAny<EntitySyncInfo>()), Times.Never());
            handlers.Verify(h => h.ChangeAttributes(entity.Guid, It.IsAny<EntitySyncInfo>()), Times.Never());
            handlers.Verify(h => h.RemoveEntity(entity.Guid), Times.Never());
        }
        public void ShouldForwardUpdatesToServersOtherThanTheSource()
        {
            var otherConnectionMock = new Mock<Connection>();
            var handlers2 = new Mock<IHandlers>();
            otherConnectionMock.Setup(rc => rc.GenerateClientFunction("serverSync","addEntity"))
                .Returns((ClientFunction)handlers2.Object.AddEntity);
            otherConnectionMock.Setup(rc => rc.GenerateClientFunction("serverSync","removeEntity"))
                .Returns((ClientFunction)handlers2.Object.RemoveEntity);
            otherConnectionMock.Setup(rc => rc.GenerateClientFunction("serverSync", "changeAttributes"))
                .Returns((ClientFunction)handlers2.Object.ChangeAttributes);

            var guid = Guid.NewGuid();
            RemoteServerImpl remoteServer2 = new RemoteServerImpl(otherConnectionMock.Object, doiMock.Object,
                dorMock.Object, guid);
            serverSyncMock.Setup(ss => ss.RemoteServers).Returns(
                new List<IRemoteServer> { remoteServer, remoteServer2 });

            var changedAttributes = new EntitySyncInfo();
            changedAttributes["test"]["a"] = new AttributeSyncInfo(Guid.NewGuid(), 99);

            var entity = new Entity();
            var worldSync = new WorldSync();
            worldSync.HandleRemoteAddedEntity(remoteConnectionMock.Object, entity.Guid, entity.Owner, new EntitySyncInfo());
            worldSync.HandleRemoteChangedAttributes(remoteConnectionMock.Object, entity.Guid, changedAttributes);
            worldSync.HandleRemoteRemovedEntity(remoteConnectionMock.Object, entity.Guid);

            handlers.Verify(h => h.AddEntity(entity.Guid, It.IsAny<EntitySyncInfo>()), Times.Never());
            handlers.Verify(h => h.ChangeAttributes(entity.Guid, It.IsAny<EntitySyncInfo>()), Times.Never());
            handlers.Verify(h => h.RemoveEntity(entity.Guid), Times.Never());

            handlers2.Verify(h => h.AddEntity(entity.Guid, It.IsAny<EntitySyncInfo>()), Times.Once());
            handlers2.Verify(h => h.ChangeAttributes(entity.Guid, It.IsAny<EntitySyncInfo>()), Times.Once());
            handlers2.Verify(h => h.RemoveEntity(entity.Guid), Times.Once());
        }
示例#16
0
        internal void HandleRemoteAddedEntity(Connection connection, string id, string owner, EntitySyncInfo initialSyncInfo)
        {
            var guid = new Guid(id);

            lock (syncInfo)
            {
                if (!syncInfo.ContainsKey(guid))
                {
                    syncInfo.Add(guid, new EntitySyncInfo());
                    lock (ignoredEntityAdditions)
                        ignoredEntityAdditions.Add(guid);
                    World.Instance.Add(new Entity(guid, new Guid(owner)));
                }
                else
                {
                    logger.Warn("Processing addition of already existing entity. Guid: " + guid);
                    return;
                }

                ProcessChangedAttributes(guid, initialSyncInfo);

                Entity entity = World.Instance.FindEntity(guid);
                foreach (IRemoteServer server in ServerSync.RemoteServers)
                {
                    if (server.Connection != connection && server.DoI.IsInterestedInEntity(entity))
                    {
                        server.Connection["serverSync.addEntity"](id, owner, initialSyncInfo);
                    }
                }
            }
        }
        /// <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 (syncInfo)
            {
                if (!syncInfo.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);
                    }
                }
            }
        }