// Received a list of animations for this avatar. Check to see if animation list has // changed and update the scene presence. // Doing any updates to the Animator causes events to be sent out so don't change willy nilly. // Changes to the animation set must be done through sp.Animator so the proper side // effects happen and updates are sent out. private void UpdateAvatarAnimations(ScenePresence sp, OSDArray pPackedAnimations) { AnimationSet newSet = new AnimationSet(pPackedAnimations); AnimationSet currentSet = sp.Animator.Animations; if (!newSet.Equals(currentSet)) { // DebugLog.DebugFormat("{0} UpdateAvatarAnimations. spID={1},CurrAnims={2},NewAnims={3}", // LogHeader, sp.LocalId, currentSet, newSet); // DEBUG DEBUG // If something changed, stuff the new values in the existing animation collection. sp.Animator.Animations.FromOSDArray(pPackedAnimations); } // Doesn't matter if it changed or not. If someone sends us an animation update, tell any connected client. sp.Animator.SendAnimPack(); }
/// <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); }