// Constructor public SyncedProperty(SyncableProperties.Type property, Object initValue, long initTS, string syncID) { Property = property; LastUpdateValue = initValue; LastUpdateTimeStamp = initTS; LastUpdateSyncID = syncID; }
private Object GetPropertyValue(SceneObjectPart part, SyncableProperties.Type property) { switch (property) { /////////////////////// //SOP properties /////////////////////// case SyncableProperties.Type.AggregateScriptEvents: return (int)part.AggregateScriptEvents; case SyncableProperties.Type.AllowedDrop: return part.AllowedDrop; case SyncableProperties.Type.AngularVelocity: return part.AngularVelocity; case SyncableProperties.Type.AttachedAvatar: return part.ParentGroup.AttachedAvatar; case SyncableProperties.Type.AttachedPos: return part.AttachedPos; case SyncableProperties.Type.AttachmentPoint: return part.ParentGroup.AttachmentPoint; case SyncableProperties.Type.BaseMask: return part.BaseMask; case SyncableProperties.Type.Category: return part.Category; case SyncableProperties.Type.ClickAction: return part.ClickAction; case SyncableProperties.Type.CollisionSound: return part.CollisionSound; case SyncableProperties.Type.CollisionSoundVolume: return part.CollisionSoundVolume; case SyncableProperties.Type.Color: return PropertySerializer.SerializeColor(part.Color); case SyncableProperties.Type.CreationDate: return part.CreationDate; case SyncableProperties.Type.CreatorData: return part.CreatorData; case SyncableProperties.Type.CreatorID: return part.CreatorID; case SyncableProperties.Type.Description: return part.Description; case SyncableProperties.Type.EveryoneMask: return part.EveryoneMask; case SyncableProperties.Type.Flags: return (int)part.Flags; case SyncableProperties.Type.FolderID: return part.FolderID; //Skip SyncableProperties.FullUpdate, which should be handled seperatedly case SyncableProperties.Type.GroupID: return part.GroupID; case SyncableProperties.Type.GroupMask: return part.GroupMask; case SyncableProperties.Type.GroupPosition: return part.GroupPosition; case SyncableProperties.Type.InventorySerial: return part.InventorySerial; case SyncableProperties.Type.IsAttachment: return part.ParentGroup.IsAttachment; case SyncableProperties.Type.LastOwnerID: return part.LastOwnerID; case SyncableProperties.Type.LinkNum: return part.LinkNum; case SyncableProperties.Type.LocalId: return part.LocalId; case SyncableProperties.Type.LocalFlags: return (int)part.LocalFlags; case SyncableProperties.Type.Material: return part.Material; case SyncableProperties.Type.MediaUrl: return part.MediaUrl; case SyncableProperties.Type.Name: return part.Name; case SyncableProperties.Type.NextOwnerMask: return part.NextOwnerMask; case SyncableProperties.Type.ObjectSaleType: return part.ObjectSaleType; case SyncableProperties.Type.OffsetPosition: return part.OffsetPosition; case SyncableProperties.Type.OwnerID: return part.OwnerID; case SyncableProperties.Type.OwnerMask: return part.OwnerMask; case SyncableProperties.Type.OwnershipCost: return part.OwnershipCost; case SyncableProperties.Type.ParticleSystem: //byte[], return a cloned copy return part.ParticleSystem;//.Clone() case SyncableProperties.Type.PassTouches: return part.PassTouches; case SyncableProperties.Type.RotationOffset: return part.RotationOffset; case SyncableProperties.Type.SalePrice: return part.SalePrice; case SyncableProperties.Type.Scale: return part.Scale; case SyncableProperties.Type.ScriptAccessPin: return part.ScriptAccessPin; case SyncableProperties.Type.Shape: return PropertySerializer.SerializeShape(part.Shape); case SyncableProperties.Type.SitName: return part.SitName; case SyncableProperties.Type.SitTargetOrientation: return part.SitTargetOrientation; case SyncableProperties.Type.SitTargetOrientationLL: return part.SitTargetOrientationLL; case SyncableProperties.Type.SitTargetPosition: return part.SitTargetPosition; case SyncableProperties.Type.SitTargetPositionLL: return part.SitTargetPositionLL; case SyncableProperties.Type.SOP_Acceleration: return part.Acceleration; case SyncableProperties.Type.Sound: return part.Sound; case SyncableProperties.Type.TaskInventory: return PropertySerializer.SerializeTaskInventory(part.TaskInventory, Scene); case SyncableProperties.Type.Text: return part.Text; case SyncableProperties.Type.TextureAnimation: return part.TextureAnimation;//.Clone(); case SyncableProperties.Type.TouchName: return part.TouchName; /* case SyncableProperties.Type.UpdateFlag: return part.UpdateFlag; */ case SyncableProperties.Type.Velocity: return part.Velocity; case SyncableProperties.Type.VolumeDetectActive: return part.VolumeDetectActive; /////////////////////// //PhysActor properties /////////////////////// case SyncableProperties.Type.Buoyancy: if (part.PhysActor == null) return null; return part.PhysActor.Buoyancy; case SyncableProperties.Type.Flying: if (part.PhysActor == null) return null; return part.PhysActor.Flying; case SyncableProperties.Type.Force: if (part.PhysActor == null) return null; return part.PhysActor.Force; case SyncableProperties.Type.IsColliding: if (part.PhysActor == null) return null; return part.PhysActor.IsColliding; case SyncableProperties.Type.CollidingGround: if (part.PhysActor == null) return null; return part.PhysActor.CollidingGround; case SyncableProperties.Type.Kinematic: if (part.PhysActor == null) return null; return part.PhysActor.Kinematic; case SyncableProperties.Type.Orientation: if (part.PhysActor == null) return null; return part.PhysActor.Orientation; case SyncableProperties.Type.PA_Acceleration: if (part.PhysActor == null) return null; return part.PhysActor.Acceleration; case SyncableProperties.Type.PA_Velocity: if (part.PhysActor == null) return null; return part.PhysActor.Velocity; case SyncableProperties.Type.PA_TargetVelocity: if (part.PhysActor == null) return null; return part.PhysActor.TargetVelocity; case SyncableProperties.Type.Position: if (part.PhysActor == null) return null; return part.PhysActor.Position; case SyncableProperties.Type.RotationalVelocity: if (part.PhysActor == null) return null; return part.PhysActor.RotationalVelocity; case SyncableProperties.Type.Size: if (part.PhysActor == null) return null; return part.PhysActor.Size; case SyncableProperties.Type.Torque: if (part.PhysActor == null) return null; return part.PhysActor.Torque; /////////////////////// //SOG properties /////////////////////// case SyncableProperties.Type.AbsolutePosition: return part.ParentGroup.AbsolutePosition; case SyncableProperties.Type.IsSelected: return part.ParentGroup.IsSelected; } //DebugLog.ErrorFormat("{0}: GetPropertyValue could not get property {1} from {2}", LogHeader, property.ToString(), part.UUID); return null; }
/// <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 SOP data, /// synchronize the two: /// (1) if the value here has a timestamp newer than lastUpdateByLocalTS /// (e.g. due to clock drifts among different sync nodes, a remote /// write might have a newer timestamp than the local write), /// overwrite the SOP's property with the value here (effectively /// disvalidate the local write operation that just happened). /// (2) otherwise, copy SOP's data and update timestamp and syncID /// as indicated by "lastUpdateByLocalTS" and "syncID". /// </summary> /// <param name="part"></param> /// <param name="property"></param> /// <param name="lastUpdateByLocalTS"></param> /// <param name="syncID"></param> /// <returns>Return true if the property's value maintained in this SyncInfoPrim is replaced by SOP's data.</returns> private bool CompareValue_UpdateByLocal(SceneObjectPart part, SyncableProperties.Type property, long lastUpdateByLocalTS, string syncID, out bool resetSceneValue) { resetSceneValue = false; //DebugLog.WarnFormat("[SYNC INFO PRIM] CompareValue_UpdateByLocal: Updating property {0} on part {1}", property.ToString(), part.UUID); SyncedProperty syncedProperty; CurrentlySyncedProperties.TryGetValue(property, out syncedProperty); // If the property does not exist yet then add it. if (syncedProperty == null) { Object initValue = GetPropertyValue(part, property); SyncedProperty syncInfo = new SyncedProperty(property, initValue, lastUpdateByLocalTS, syncID); CurrentlySyncedProperties.Add(property, syncInfo); return true; } // First, check if the value maintained here is different from that in SOP'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 SOP's value with what's maintained // in SyncInfo; otherwise, copy SOP's data to SyncInfo. switch (property) { case SyncableProperties.Type.GroupPosition: return CompareAndUpdateSOPGroupPositionByLocal(part, lastUpdateByLocalTS, syncID); default: Object value = GetPropertyValue(part, property); // If both null, no update needed if (syncedProperty.LastUpdateValue == null && value == null) return false; // If one is null and the other is not, or if they are not equal, the property was changed. if ((value == null && syncedProperty.LastUpdateValue != null) || (value != null && syncedProperty.LastUpdateValue == null) || (!value.Equals(syncedProperty.LastUpdateValue))) { if (value != null) { switch (property) { 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 (property == SyncableProperties.Type.Shape) { //DebugLog.WarnFormat("[SYNC INFO PRIM]: SHAPES DIFFER {0} {1}", (string)value, (string)syncedProperty.LastUpdateValue); } // DebugLog.WarnFormat("[SYNC INFO PRIM] CompareValue_UpdateByLocal (property={0}): value != syncedProperty.LastUpdateValue", property.ToString()); if (lastUpdateByLocalTS >= syncedProperty.LastUpdateTimeStamp) { // DebugLog.WarnFormat("[SYNC INFO PRIM] CompareValue_UpdateByLocal (property={0}): TS >= lastTS (updating SyncInfo)", property.ToString()); syncedProperty.UpdateSyncInfoByLocal(lastUpdateByLocalTS, syncID, value); // Updating either absolute position or position also requires checking for updates to group position if (property == SyncableProperties.Type.AbsolutePosition || property == SyncableProperties.Type.Position) { CompareValue_UpdateByLocal(part, SyncableProperties.Type.GroupPosition, lastUpdateByLocalTS, syncID, out resetSceneValue); resetSceneValue = false; } return true; } // DebugLog.WarnFormat("[SYNC INFO PRIM] CompareValue_UpdateByLocal (property={0}): TS < lastTS (updating SOP)", property.ToString()); //We'll reset the property value outside of CompareValue_UpdateByLocal //SetPropertyValue(property); resetSceneValue = true; } break; } return false; }
public override Object GetPropertyValue(SyncableProperties.Type property) { return GetPropertyValue((SceneObjectPart)SceneThing, property); }
public abstract Object GetPropertyValue(SyncableProperties.Type property);
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; }
/// <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; }