// Constructor public SyncedProperty(SyncableProperties.Type property, Object initValue, long initTS, string syncID) { Property = property; LastUpdateValue = initValue; LastUpdateTimeStamp = initTS; LastUpdateSyncID = syncID; }
/// <summary> /// This function should only be triggered when an update update is received (i.e. /// triggered by remote update instead of local update). /// </summary> /// <param name="sp"></param> /// <param name="property"></param> private void SetPropertyValue(ScenePresence sp, SyncedProperty syncedProperty) { if (sp == null) { return; } SyncableProperties.Type property = syncedProperty.Property; if (syncedProperty == null) { DebugLog.ErrorFormat("{0}: SetPropertyValue: property {1} not in sync cache for uuid {2}. ", LogHeader, property, UUID); return; } SetPropertyValue(sp, property, syncedProperty); }
public abstract Object GetPropertyValue(SyncableProperties.Type property);
//TODO: might return status such as Updated, Unchanged, etc to caller public HashSet <SyncableProperties.Type> UpdatePropertiesBySync(UUID uuid, HashSet <SyncedProperty> syncedProperties) { long recvTS = RegionSyncModule.NowTicks(); HashSet <SyncableProperties.Type> propertiesUpdated = new HashSet <SyncableProperties.Type>(); List <SyncedProperty> updatedSyncedProperties = new List <SyncedProperty>(); lock (m_syncLock) { foreach (SyncedProperty syncedProperty in syncedProperties) { bool updated = false; SyncableProperties.Type property = syncedProperty.Property; //Compare if the value of the property in this SyncInfo is different than the value in local scene SyncedProperty currentlySyncedProperty; CurrentlySyncedProperties.TryGetValue(property, out currentlySyncedProperty); // If synced property is not in cache, add it now. if (currentlySyncedProperty == null) { //could happen if PhysActor is just created (object stops being phantom) if (SyncableProperties.PhysActorProperties.Contains(property)) { CurrentlySyncedProperties.Add(property, syncedProperty); } else { DebugLog.WarnFormat("{0}: UpdatePropertiesBySync: No record of property {1} for uuid {2}", LogHeader, property, uuid); } } else { try { //Compare timestamp and update SyncInfo if necessary updated = currentlySyncedProperty.CompareAndUpdateSyncInfoBySync(syncedProperty, recvTS); //If updated, update the property value in scene object/presence if (updated) { //SetPropertyValue(property); updatedSyncedProperties.Add(currentlySyncedProperty); propertiesUpdated.Add(property); } } catch (Exception e) { DebugLog.ErrorFormat("{0}: UpdatePropertiesBySync: Error in updating property {1}: {2}", LogHeader, property, e.Message); } } } } //Now we only need to read from the SyncInfo, so moving the SetPropertyValue out of lock, to avoid potential deadlocks //which might happen due to side effects of "set" functions of a SOp or SP property foreach (SyncedProperty updatedProperty in updatedSyncedProperties) { SetPropertyValue(updatedProperty); } PostUpdateBySync(propertiesUpdated); return(propertiesUpdated); }
/// <summary> /// Set member values by decoding out of propertyData. Should only /// be called in initialization time (e.g. from constructor). /// </summary> /// <param name="propertyData"></param> private void FromOSDArray(OSDArray propertyData) { Property = (SyncableProperties.Type)(propertyData[0].AsInteger()); LastUpdateTimeStamp = propertyData[1].AsLong(); LastUpdateSyncID = propertyData[2].AsString(); OSD value = propertyData[3]; switch (Property) { /////////////////////////////////////// // Complex structure properties /////////////////////////////////////// case SyncableProperties.Type.AgentCircuitData: case SyncableProperties.Type.AvatarAppearance: LastUpdateValue = (OSDMap)value; break; case SyncableProperties.Type.Animations: LastUpdateValue = (OSDArray)value; break; //////////////////////////// // Integer/enum type properties //////////////////////////// case SyncableProperties.Type.CreationDate: // int case SyncableProperties.Type.LinkNum: // int case SyncableProperties.Type.OwnershipCost: // int case SyncableProperties.Type.SalePrice: // int case SyncableProperties.Type.ScriptAccessPin: // int case SyncableProperties.Type.AggregateScriptEvents: // enum case SyncableProperties.Type.Flags: // enum case SyncableProperties.Type.LocalFlags: // enum case SyncableProperties.Type.PresenceType: // enum LastUpdateValue = value.AsInteger(); break; case SyncableProperties.Type.ClickAction: case SyncableProperties.Type.Material: case SyncableProperties.Type.ObjectSaleType: LastUpdateValue = (byte)value.AsInteger(); break; //////////////////////////// // Boolean type properties //////////////////////////// case SyncableProperties.Type.AllowedDrop: case SyncableProperties.Type.IsAttachment: case SyncableProperties.Type.PassTouches: case SyncableProperties.Type.VolumeDetectActive: case SyncableProperties.Type.Flying: case SyncableProperties.Type.IsColliding: case SyncableProperties.Type.CollidingGround: case SyncableProperties.Type.Kinematic: case SyncableProperties.Type.IsSelected: case SyncableProperties.Type.AllowMovement: LastUpdateValue = value.AsBoolean(); break; //////////////////////////// // Vector3 type properties //////////////////////////// case SyncableProperties.Type.AngularVelocity: case SyncableProperties.Type.AttachedPos: case SyncableProperties.Type.GroupPosition: case SyncableProperties.Type.OffsetPosition: case SyncableProperties.Type.Scale: case SyncableProperties.Type.SitTargetPosition: case SyncableProperties.Type.SitTargetPositionLL: case SyncableProperties.Type.SOP_Acceleration: case SyncableProperties.Type.Velocity: case SyncableProperties.Type.Force: case SyncableProperties.Type.PA_Acceleration: case SyncableProperties.Type.PA_Velocity: case SyncableProperties.Type.PA_TargetVelocity: case SyncableProperties.Type.Position: case SyncableProperties.Type.RotationalVelocity: case SyncableProperties.Type.Size: case SyncableProperties.Type.Torque: case SyncableProperties.Type.AbsolutePosition: LastUpdateValue = value.AsVector3(); break; //////////////////////////// // UUID type properties //////////////////////////// case SyncableProperties.Type.AttachedAvatar: case SyncableProperties.Type.CollisionSound: case SyncableProperties.Type.CreatorID: case SyncableProperties.Type.FolderID: case SyncableProperties.Type.GroupID: case SyncableProperties.Type.LastOwnerID: case SyncableProperties.Type.OwnerID: case SyncableProperties.Type.Sound: LastUpdateValue = value.AsUUID(); break; //////////////////////////// // UInt type properties //////////////////////////// case SyncableProperties.Type.LocalId: case SyncableProperties.Type.AttachmentPoint: case SyncableProperties.Type.BaseMask: case SyncableProperties.Type.Category: case SyncableProperties.Type.EveryoneMask: case SyncableProperties.Type.GroupMask: case SyncableProperties.Type.InventorySerial: case SyncableProperties.Type.NextOwnerMask: case SyncableProperties.Type.OwnerMask: case SyncableProperties.Type.AgentControlFlags: case SyncableProperties.Type.ParentId: LastUpdateValue = value.AsUInteger(); break; //////////////////////////// // Float type properties //////////////////////////// case SyncableProperties.Type.CollisionSoundVolume: case SyncableProperties.Type.Buoyancy: LastUpdateValue = (float)value.AsReal(); break; //////////////////////////// // String type properties //////////////////////////// case SyncableProperties.Type.Color: case SyncableProperties.Type.CreatorData: case SyncableProperties.Type.Description: case SyncableProperties.Type.MediaUrl: case SyncableProperties.Type.Name: case SyncableProperties.Type.RealRegion: case SyncableProperties.Type.Shape: case SyncableProperties.Type.SitName: case SyncableProperties.Type.TaskInventory: case SyncableProperties.Type.Text: case SyncableProperties.Type.TouchName: LastUpdateValue = value.AsString(); break; //////////////////////////// // byte[] (binary data) type properties //////////////////////////// case SyncableProperties.Type.ParticleSystem: case SyncableProperties.Type.TextureAnimation: LastUpdateValue = value.AsBinary(); break; //////////////////////////// // Quaternion type properties //////////////////////////// case SyncableProperties.Type.RotationOffset: case SyncableProperties.Type.SitTargetOrientation: case SyncableProperties.Type.SitTargetOrientationLL: case SyncableProperties.Type.Orientation: case SyncableProperties.Type.Rotation: LastUpdateValue = value.AsQuaternion(); break; default: DebugLog.WarnFormat("[SYNCED PROPERTY] FromOSDArray: No handler for property {0} ", Property); break; } }
private void SetPropertyValue(ScenePresence sp, SyncableProperties.Type property, SyncedProperty pSyncInfo) { if (sp == null || pSyncInfo == null) { return; } Object pValue = pSyncInfo.LastUpdateValue; switch (property) { case SyncableProperties.Type.LocalId: sp.LocalId = (uint)pValue; break; case SyncableProperties.Type.AbsolutePosition: sp.AbsolutePosition = (Vector3)pValue; break; case SyncableProperties.Type.AgentCircuitData: DebugLog.WarnFormat("{0}: Received updated AgentCircuitData. Not implemented", LogHeader); break; case SyncableProperties.Type.ParentId: uint localID = (uint)pValue; if (localID == 0) { // DebugLog.DebugFormat("{0}: SetPropertyValue:ParentID. Standup. Input={1}", LogHeader, localID); // DEBUG DEBUG sp.StandUp(); } else { SceneObjectPart parentPart = Scene.GetSceneObjectPart(localID); if (parentPart != null) // TODO ?? { sp.HandleAgentRequestSit(sp.ControllingClient, sp.ControllingClient.AgentId, parentPart.UUID, Vector3.Zero); // DebugLog.DebugFormat("{0}: SetPropertyValue:ParentID. SitRequest. Input={1},sp={2},newParentID={3}", // LogHeader, localID, (string)(sp == null ? "NULL" : sp.Name), sp.ParentID); // DEBUG DEBUG } } //sp.ParentID = (uint)pValue; break; case SyncableProperties.Type.AgentControlFlags: sp.AgentControlFlags = (uint)pValue; break; case SyncableProperties.Type.AllowMovement: sp.AllowMovement = (bool)pValue; break; case SyncableProperties.Type.AvatarAppearance: sp.Appearance.Unpack((OSDMap)pValue); sp.SendAppearanceToAllOtherAgents(); DebugLog.DebugFormat("{0} Received updated AvatarAppearance for uuid {1}.", LogHeader, sp.UUID); break; case SyncableProperties.Type.Animations: UpdateAvatarAnimations(sp, (OSDArray)pValue); break; case SyncableProperties.Type.Rotation: sp.Rotation = (Quaternion)pValue; break; case SyncableProperties.Type.PA_Velocity: if (sp.PhysicsActor != null) { sp.PhysicsActor.Velocity = (Vector3)pValue; } break; case SyncableProperties.Type.RealRegion: ////// NOP ////// break; case SyncableProperties.Type.PA_TargetVelocity: if (sp.PhysicsActor != null) { sp.PhysicsActor.TargetVelocity = (Vector3)pValue; } break; case SyncableProperties.Type.Flying: sp.Flying = (bool)pValue; break; case SyncableProperties.Type.PresenceType: DebugLog.WarnFormat("{0} Received updated PresenceType for uuid {1}. Not implemented", LogHeader, sp.UUID); break; case SyncableProperties.Type.IsColliding: if (sp.PhysicsActor != null) { sp.IsColliding = (bool)pValue; } break; } // When presence values are changed, we tell the simulator with an event GenerateAgentUpdated(sp); }
// Gets the value out of the SP in local scene and returns it as an object private Object GetPropertyValue(ScenePresence sp, SyncableProperties.Type property) { if (sp == null) { return(null); } switch (property) { case SyncableProperties.Type.LocalId: return(sp.LocalId); case SyncableProperties.Type.AbsolutePosition: return(sp.AbsolutePosition); case SyncableProperties.Type.AgentCircuitData: return(Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode).PackAgentCircuitData()); case SyncableProperties.Type.ParentId: return(sp.ParentID); case SyncableProperties.Type.AgentControlFlags: return(sp.AgentControlFlags); case SyncableProperties.Type.AllowMovement: return(sp.AllowMovement); case SyncableProperties.Type.Animations: return(sp.Animator.Animations.ToOSDArray()); case SyncableProperties.Type.AvatarAppearance: return(sp.Appearance.Pack()); case SyncableProperties.Type.Rotation: return(sp.Rotation); case SyncableProperties.Type.PA_Velocity: if (sp.PhysicsActor == null) { return(Vector3.Zero); } return(sp.PhysicsActor.Velocity); case SyncableProperties.Type.RealRegion: // Always just the local scene name the avatar is in when requested locally. return(sp.Scene.Name); case SyncableProperties.Type.PA_TargetVelocity: if (sp.PhysicsActor == null) { return(Vector3.Zero); } return(sp.PhysicsActor.TargetVelocity); case SyncableProperties.Type.Flying: return(sp.Flying); case SyncableProperties.Type.PresenceType: return((int)sp.PresenceType); case SyncableProperties.Type.IsColliding: return(sp.IsColliding); } //DebugLog.ErrorFormat("{0}: GetPropertyValue could not get property {1} from {2}", LogHeader, property.ToString(), sp.UUID); return(null); }
public override Object GetPropertyValue(SyncableProperties.Type property) { return(GetPropertyValue((ScenePresence)SceneThing, property)); }
/// <summary> /// Compare the value (not "reference") of the given property. /// Assumption: the caller has already checked if PhysActor exists /// if there are physics properties updated. /// If the value maintained here is different from that in SP data, /// synchronize the two: /// (1) if the cached value has a timestamp newer than lastUpdateByLocalTS /// overwrite the SP's property with the cached value (effectively /// undoing the local write operation that just happened). /// (2) otherwise, copy SP's data and update timestamp and syncID /// as indicated by "lastUpdateByLocalTS" and "syncID". /// </summary> /// <param name="sp"></param> /// <param name="property"></param> /// <param name="lastUpdateByLocalTS"></param> /// <param name="syncID"></param> /// <returns>Return true if the property's value maintained in this /// RegionSyncModule is replaced by SP's data.</returns> private bool CompareValue_UpdateByLocal(ScenePresence sp, SyncableProperties.Type property, long lastUpdateByLocalTS, string syncID) { //DebugLog.WarnFormat("[SYNC INFO PRESENCE] CompareValue_UpdateByLocal: Updating property {0} on sp {1}", property.ToString(), sp.UUID); // Check to see if this property is in the sync cache for this object. // If not, add it and initialize value to value in ScenePresence. bool ret = false; SyncedProperty syncedProperty; CurrentlySyncedProperties.TryGetValue(property, out syncedProperty); // If synced property is not in cache, add it now if (syncedProperty == null) { Object initValue = GetPropertyValue(sp, property); if (initValue != null) { CurrentlySyncedProperties.Add(property, new SyncedProperty(property, initValue, lastUpdateByLocalTS, syncID)); ret = true; } return(ret); } // First, check if the value maintained here is different from that in SP's. // If different, next check if the timestamp in SyncInfo is newer than lastUpdateByLocalTS; // if so (although ideally should not happen, but due to things likc clock not so perfectly // sync'ed, it might happen), overwrite SP's value with what's maintained // in SyncInfo; otherwise, copy SP's data to SyncInfo. Object value = GetPropertyValue(sp, property); // If both null, no update needed if (syncedProperty.LastUpdateValue == null && value == null) { return(false); } switch (property) { default: // If one is null and the other is not, or if the references are different, the property was changed. // This will perform a value comparison for strings in C#. We could use String.Clone instead for string properties. if ((value == null && syncedProperty.LastUpdateValue != null) || (value != null && syncedProperty.LastUpdateValue == null) || (!value.Equals(syncedProperty.LastUpdateValue))) { if (value != null) { // Some values, even if they are not 'equal', might be close enough to be equal. // Note that the 'Equals()' above will most always return 'false' for lists and OSDMaps // since they are probably not the same object. // Returning a 'false' here means the values don't need any updating (they are equal enough). switch (property) { case SyncableProperties.Type.AvatarAppearance: String stringValue = OSDParser.SerializeJsonString((OSDMap)value); String lastStringValue = OSDParser.SerializeJsonString((OSDMap)syncedProperty.LastUpdateValue); if (stringValue == lastStringValue) { return(false); } break; case SyncableProperties.Type.Animations: if (syncedProperty.LastUpdateValue != null) { AnimationSet lastAnimations = new AnimationSet((OSDArray)syncedProperty.LastUpdateValue); // Get the home region for this presence (the client manager the presence is connected to). string cachedRealRegionName = (string)(CurrentlySyncedProperties[SyncableProperties.Type.RealRegion].LastUpdateValue); if (cachedRealRegionName != Scene.Name && sp.Animator.Animations.ToArray().Length == 0) { // If this is not the originating region for this presence and there is no additional // animations being added, this simulator does not change the animation. // THIS IS A HORRIBLE KLUDGE. FIGURE OUT THE REAL SOLUTION!! // The problem is that animations are changed by every simulator (setting default // sit and stand when parentID changes) and the updates conflict/override the real // settings (like a scripted sit animation). // DebugLog.DebugFormat("{0} CompareValue_UpdateByLocal. Not home sim or no anim change. spID={1}, homeSim={2}, thisSim={3}, anims={4}", // LogHeader, sp.LocalId, cachedRealRegionName, Scene.Name, sp.Animator.Animations.ToArray().Length); // DEBUG DEBUG return(false); } if (lastAnimations.Equals(sp.Animator.Animations)) { // DebugLog.DebugFormat("{0} CompareValue_UpdateByLocal. Equal anims. spID={1}, sp.Anim={2}, lastAnim={3}", // LogHeader, sp.LocalId, sp.Animator.Animations, lastAnimations); // DEBUG DEBUG return(false); } else { // If locally storing a new value of the animation, don't check for the time. // DebugLog.DebugFormat("{0} CompareValue_UpdateByLocal. Not equal anims. spID={1}, sp.Anim={2}, lastAnim={3}", // LogHeader, sp.LocalId, sp.Animator.Animations, lastAnimations); // DEBUG DEBUG syncedProperty.UpdateSyncInfoByLocal(lastUpdateByLocalTS, syncID, value); return(true); } } break; case SyncableProperties.Type.Velocity: case SyncableProperties.Type.PA_Velocity: case SyncableProperties.Type.PA_TargetVelocity: case SyncableProperties.Type.RotationalVelocity: case SyncableProperties.Type.AngularVelocity: { Vector3 partVal = (Vector3)value; Vector3 lastVal = (Vector3)syncedProperty.LastUpdateValue; // If velocity difference is small but not zero, don't update if (partVal.ApproxEquals(lastVal, VELOCITY_TOLERANCE) && !partVal.Equals(Vector3.Zero)) { return(false); } break; } case SyncableProperties.Type.RotationOffset: case SyncableProperties.Type.Orientation: { Quaternion partVal = (Quaternion)value; Quaternion lastVal = (Quaternion)syncedProperty.LastUpdateValue; if (partVal.ApproxEquals(lastVal, ROTATION_TOLERANCE)) { return(false); } break; } case SyncableProperties.Type.OffsetPosition: case SyncableProperties.Type.AbsolutePosition: case SyncableProperties.Type.Position: case SyncableProperties.Type.GroupPosition: { Vector3 partVal = (Vector3)value; Vector3 lastVal = (Vector3)syncedProperty.LastUpdateValue; if (partVal.ApproxEquals(lastVal, POSITION_TOLERANCE)) { return(false); } break; } } } // If we get here, the values are not equal and we need to update the cached value if the // new value is timestamp newer. if (lastUpdateByLocalTS >= syncedProperty.LastUpdateTimeStamp) { // DebugLog.DebugFormat("{0} CompareValue_UpdateByLocal (property={1}): TS >= lastTS (updating SyncInfo)", LogHeader, property); syncedProperty.UpdateSyncInfoByLocal(lastUpdateByLocalTS, syncID, value); return(true); } } break; } return(false); }