/// <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); }
public void ShouldCorrectlyResolveSyncConflicts() { var guid1 = Guid.Parse("38d6f8c5-ded5-405a-9f7d-80ebd36d7a26"); var guid2 = Guid.Parse("e0021595-ead4-4a70-825b-749175c0d9b9"); var info1 = new AttributeSyncInfo(guid2, 1); var info2 = new AttributeSyncInfo(guid2, 2); var info3 = new AttributeSyncInfo(guid1, 3); info3.LastTimestamp = info1.LastTimestamp; Assert.IsFalse(info2.Sync(info1)); Assert.AreEqual(2, info2.LastValue); Assert.IsTrue(info3.Sync(info1)); Assert.AreEqual(1, info3.LastValue); }
public void ShouldCorrectlyResolveSyncConflicts() { var guid1 = Guid.Parse("38d6f8c5-ded5-405a-9f7d-80ebd36d7a26"); var guid2 = Guid.Parse("e0021595-ead4-4a70-825b-749175c0d9b9"); var info1 = new AttributeSyncInfo(guid2, 1); var info2 = new AttributeSyncInfo(guid2, 2); var info3 = new AttributeSyncInfo(guid1, 3); info3.LastTimestamp = info1.LastTimestamp; Assert.IsFalse(info2.Sync(info1)); Assert.AreEqual(2, info2.LastValue); Assert.IsTrue(info3.Sync(info1)); Assert.AreEqual(1, info3.LastValue); }