diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs index 1db8ac6379..6d458a6733 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs @@ -404,7 +404,7 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule } //include the property values of each object after delinking, for synchronizing the values - data["afterGroupsCount"] = OSD.FromInteger(beforeDelinkGroups.Count); + data["afterGroupsCount"] = OSD.FromInteger(afterDelinkGroups.Count); groupNum = 0; foreach (SceneObjectGroup afterGroup in afterDelinkGroups) { @@ -1031,8 +1031,10 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule /// The handler for processing incoming sync messages. /// /// + /// ActorID of the sender public void HandleIncomingMessage(SymmetricSyncMessage msg, string senderActorID) { + //Added senderActorID, so that we don't have to include actorID in sync messages -- TODO switch (msg.Type) { case SymmetricSyncMessage.MsgType.GetTerrain: @@ -1084,6 +1086,11 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule HandleLinkObject(msg, senderActorID); return; } + case SymmetricSyncMessage.MsgType.DelinkObject: + { + HandleDelinkObject(msg, senderActorID); + return; + } //EVENTS PROCESSING case SymmetricSyncMessage.MsgType.NewScript: case SymmetricSyncMessage.MsgType.UpdateScript: @@ -1254,7 +1261,7 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule return; } - string init_actorID = data["actorID"].AsString(); + //string init_actorID = data["actorID"].AsString(); string sogxml = data["linkedGroup"].AsString(); SceneObjectGroup linkedGroup = SceneObjectSerializer.FromXml2Format(sogxml); UUID rootID = data["rootID"].AsUUID(); @@ -1277,6 +1284,57 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule } } + private void HandleDelinkObject(SymmetricSyncMessage msg, string senderActorID) + { + OSDMap data = DeserializeMessage(msg); + if (data == null) + { + SymmetricSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; + } + + //List localPrims = new List(); + List delinkPrimIDs = new List(); + List beforeDelinkGroupIDs = new List(); + List incomingAfterDelinkGroups = new List(); + + int partCount = data["partCount"].AsInteger(); + for (int i = 0; i < partCount; i++) + { + string partTempID = "part" + i; + UUID primID = data[partTempID].AsUUID(); + //SceneObjectPart localPart = m_scene.GetSceneObjectPart(primID); + //localPrims.Add(localPart); + delinkPrimIDs.Add(primID); + } + + int beforeGroupCount = data["beforeGroupsCount"].AsInteger(); + for (int i = 0; i < beforeGroupCount; i++) + { + string groupTempID = "beforeGroup" + i; + UUID beforeGroupID = data[groupTempID].AsUUID(); + beforeDelinkGroupIDs.Add(beforeGroupID); + } + + int afterGroupsCount = data["afterGroupsCount"].AsInteger(); + for (int i = 0; i < afterGroupsCount; i++) + { + string groupTempID = "afterGroup" + i; + string sogxml = data[groupTempID].AsString(); + SceneObjectGroup afterGroup = SceneObjectSerializer.FromXml2Format(sogxml); + incomingAfterDelinkGroups.Add(afterGroup); + } + + m_scene.DelinkObjectsBySync(delinkPrimIDs, beforeDelinkGroupIDs, incomingAfterDelinkGroups); + + //if this is a relay node, forwards the event + if (m_isSyncRelay) + { + //SendSceneEventToRelevantSyncConnectors(init_actorID, msg); + SendSceneEventToRelevantSyncConnectors(senderActorID, msg); + } + } + /// /// The common actions for handling remote events (event initiated at other actors and propogated here) /// @@ -1284,6 +1342,12 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule private void HandleRemoteEvent(SymmetricSyncMessage msg, string senderActorID) { OSDMap data = DeserializeMessage(msg); + if (data == null) + { + SymmetricSyncMessage.HandleError(LogHeader, msg, "Could not deserialize JSON data."); + return; + } + string init_actorID = data["actorID"].AsString(); ulong evSeqNum = data["seqNum"].AsULong(); diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 40c37f3e1e..1914e1abee 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -808,6 +808,11 @@ namespace OpenSim.Region.Framework.Scenes * */ } + public void DelinkObjectsBySync(List delinkPrimIDs, List beforeDelinkGroupIDs, List incomingAfterDelinkGroups) + { + m_sceneGraph.DelinkObjectsBySync(delinkPrimIDs, beforeDelinkGroupIDs, incomingAfterDelinkGroups); + } + #endregion //SYMMETRIC SYNC public ICapabilitiesModule CapsModule diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 16358c731c..c35b0b440e 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -2177,6 +2177,371 @@ namespace OpenSim.Region.Framework.Scenes } + /// + /// Delink the prims as indicated in localPrimIDs, and regroup them as the object-groups indicated in incomingAfterDelinkGroups. + /// + /// + /// + /// + public void DelinkObjectsBySync(List delinkPrimIDs, List beforeDelinkGroupIDs, List incomingAfterDelinkGroups) + { + Dictionary localBeforeDelinkGroups = new Dictionary(); + Dictionary delinkPrims = new Dictionary(); + bool beforeStateConsistent = true; + bool afterStateConsistent = true; + + Monitor.Enter(m_updateLock); + try + { + //get the before-delink-groups, and all the prims to delink + foreach (UUID primID in delinkPrimIDs) + { + SceneObjectPart localPart = GetSceneObjectPart(primID); + if (!delinkPrims.ContainsKey(primID)) + { + delinkPrims.Add(primID, localPart); + } + SceneObjectGroup localGroup = localPart.ParentGroup; + if (!localBeforeDelinkGroups.ContainsKey(localGroup.UUID)) + { + localBeforeDelinkGroups.Add(localGroup.UUID, localGroup); + } + } + + //Next, do some sanity check to see if the local copy agrees with remote copy on the before-link state. + //TODO:: Actions to be taken after detecting conflicts. For now, we just assume the chance that conflict will happen is almost 0. + + //First, check if the groups match + if (beforeDelinkGroupIDs.Count != localBeforeDelinkGroups.Count) + { + //detected conflict on editing object groups + m_log.Warn("DelinkObjectsBySync: the # of groups in before-delink-groups is different from the incoming delink message. NEED BETTER CONCURRENCY CONTROL IMPLEMENTATION!!!"); + beforeStateConsistent = false; + //TODO: further actions + } + else + { + foreach (UUID beforeGroupID in beforeDelinkGroupIDs) + { + if (!localBeforeDelinkGroups.ContainsKey(beforeGroupID)) + { + m_log.Warn("DelinkObjectsBySync: the local state of before-delink-groups is different from the incoming delink message. NEED BETTER CONCURRENCY CONTROL IMPLEMENTATION!!!"); + beforeStateConsistent = false; + break; + } + } + //TODO: further actions + } + + if(beforeStateConsistent){ + //Second, check if the prims match + List allPrimsInLocalGroups = new List(); + foreach (KeyValuePair pair in localBeforeDelinkGroups) + { + foreach (SceneObjectPart part in pair.Value.Parts) + { + allPrimsInLocalGroups.Add(part); + } + } + if (allPrimsInLocalGroups.Count != delinkPrims.Count) + { + m_log.Warn("DelinkObjectsBySync: the # of prims of before-delink-groups is different from the incoming delink message. NEED BETTER CONCURRENCY CONTROL IMPLEMENTATION!!!"); + beforeStateConsistent = false; + //TODO: further action + }else{ + foreach (SceneObjectPart part in allPrimsInLocalGroups) + { + if (!delinkPrims.ContainsKey(part.UUID)) + { + m_log.Warn("DelinkObjectsBySync: some local prims in before-delink-groups not exist in the incoming delink message. NEED BETTER CONCURRENCY CONTROL IMPLEMENTATION!!!"); + beforeStateConsistent = false; + break; + //TODO: further action + } + } + } + } + //end of sanity checking + + if(!beforeStateConsistent){ + m_log.Warn("DelinkObjectsBySync: before-delink state not consistent in local copy and the incoming copy. Return without further operations."); + }else{ + //Next, apply the delink operation locally. + List localAfterDelinkGroups = DelinkObjectsBySync(new List(delinkPrims.Values)); + + + //Check if local after-state agrees with that in the remote copy, and update the groups' properties + if (localAfterDelinkGroups.Count != incomingAfterDelinkGroups.Count) + { + m_log.Warn("DelinkObjectsBySync: local state after delink does not agree with the incoming delink message (# of groups are different). NEED BETTER CONCURRENCY CONTROL IMPLEMENTATION!!!"); + afterStateConsistent = false; + } + else + { + Dictionary incomingAfterDelinkGroupsDictionary = new Dictionary(); + foreach (SceneObjectGroup incomingGroup in incomingAfterDelinkGroups){ + incomingAfterDelinkGroupsDictionary.Add(incomingGroup.UUID, incomingGroup); + } + foreach (SceneObjectGroup localAfterGroup in localAfterDelinkGroups) + { + if (!incomingAfterDelinkGroupsDictionary.ContainsKey(localAfterGroup.UUID)) + { + m_log.Warn("DelinkObjectsBySync: local state after delink does not agree with the incoming delink message. NEED BETTER CONCURRENCY CONTROL IMPLEMENTATION!!!"); + afterStateConsistent = false; + } + else + { + localAfterGroup.UpdateObjectProperties(incomingAfterDelinkGroupsDictionary[localAfterGroup.UUID]); + } + } + } + } + } + finally + { + Monitor.Exit(m_updateLock); + } + + if(beforeStateConsistent && afterStateConsistent){ + m_log.Debug("DelinkObjectsBySync successful"); + } + } + + //Similar to DelinkObjects(), w/o triggering any ScheduleFullUpdate(), + private List DelinkObjectsBySync(List prims) + { + //!!!Caller of this function should already lock on m_updateLock, so no locking here !!! + + List afterDelinkGroups = new List(); + + List childParts = new List(); + List rootParts = new List(); + List affectedGroups = new List(); + + foreach (SceneObjectPart part in prims) + { + if (part != null) + { + if (part.ParentGroup.PrimCount != 1) // Skip single + { + if (part.LinkNum < 2) // Root + rootParts.Add(part); + else + childParts.Add(part); + + SceneObjectGroup group = part.ParentGroup; + if (!affectedGroups.Contains(group)) + { + affectedGroups.Add(group); + } + } + } + } + + foreach (SceneObjectPart child in childParts) + { + // Unlink all child parts from their groups, w/o triggering unwanted events or syncinfo updates + child.ParentGroup.DelinkFromGroupBySync(child, true); + + // These are not in affected groups and will not be + // handled further. Do the honors here. + child.ParentGroup.HasGroupChanged = true; + + //record the after-delink-groups + afterDelinkGroups.Add(child.ParentGroup); + } + + foreach (SceneObjectPart root in rootParts) + { + // In most cases, this will run only one time, and the prim + // will be a solo prim + // However, editing linked parts and unlinking may be different + // + SceneObjectGroup group = root.ParentGroup; + + List newSet = new List(group.Parts); + int numChildren = newSet.Count; + + // If there are prims left in a link set, but the root is + // slated for unlink, we need to do this + // + if (numChildren != 1) + { + // Unlink the remaining set + // + bool sendEventsToRemainder = true; + if (numChildren > 1) + sendEventsToRemainder = false; + + foreach (SceneObjectPart p in newSet) + { + if (p != group.RootPart) + group.DelinkFromGroupBySync(p, sendEventsToRemainder); + } + + // If there is more than one prim remaining, we + // need to re-link + // + if (numChildren > 2) + { + // Remove old root + // + if (newSet.Contains(root)) + newSet.Remove(root); + + // Preserve link ordering + // + newSet.Sort(delegate(SceneObjectPart a, SceneObjectPart b) + { + return a.LinkNum.CompareTo(b.LinkNum); + }); + + // Determine new root + // + SceneObjectPart newRoot = newSet[0]; + newSet.RemoveAt(0); + + foreach (SceneObjectPart newChild in newSet) + newChild.UpdateFlag = 0; + + LinkObjectsBySync(newRoot, newSet); + if (!affectedGroups.Contains(newRoot.ParentGroup)) + affectedGroups.Add(newRoot.ParentGroup); + } + } + } + + foreach (SceneObjectGroup g in affectedGroups) + { + g.TriggerScriptChangedEvent(Changed.LINK); + g.HasGroupChanged = true; // Persist + + afterDelinkGroups.Add(g); + } + + return afterDelinkGroups; + } + + + /* + * //Initial effort to delink objects by copying from the linksets from remote copy. Too much book-keeping updating work to make sure all details are right. + * //So we switched to letting local actor to apply the same "delink" operation as the remote actor did, and check if the "before-state" and "after-state" + * //agrees. + public void DelinkObjectsBySync(List delinkPrimIDs, List beforeDelinkGroupIDs, List incomingAfterDelinkGroups) + { + Monitor.Enter(m_updateLock); + try + { + Dictionary localBeforeDelinkGroups = new Dictionary(); + Dictionary delinkPrims = new Dictionary(); + + //get the before-delink-groups, and all the prims to delink + foreach (UUID primID in delinkPrimIDs) + { + SceneObjectPart localPart = GetSceneObjectPart(primID); + if (!delinkPrims.ContainsKey(primID)) + { + delinkPrims.Add(primID, localPart); + } + SceneObjectGroup localGroup = localPart.ParentGroup; + if (!localBeforeDelinkGroups.ContainsKey(localGroup.UUID)) + { + localBeforeDelinkGroups.Add(localGroup.UUID, localGroup); + } + } + + //Next, do some sanity check to see if the local copy agrees with remote copy on the before-link state. + //TODO:: Actions to be taken after detecting conflicts. For now, we just assume the chance that conflict will happen is almost 0. + + //First, check if the groups match + if (beforeDelinkGroupIDs.Count != localBeforeDelinkGroups.Count) + { + //detected conflict on editing object groups + m_log.Warn("DelinkObjectsBySync: the # of groups in before-delink-groups is different from the incoming delink message. NEED BETTER CONCURRENCY CONTROL IMPLEMENTATION!!!"); + //TODO: further actions + } + else + { + foreach (UUID beforeGroupID in beforeDelinkGroupIDs) + { + if (!localBeforeDelinkGroups.ContainsKey(beforeGroupID)) + { + m_log.Warn("DelinkObjectsBySync: the local state of before-delink-groups is different from the incoming delink message. NEED BETTER CONCURRENCY CONTROL IMPLEMENTATION!!!"); + } + } + //TODO: further actions + } + //Second, check if the prims match + List allPrimsInLocalGroups = new List(); + foreach (KeyValuePair pair in localBeforeDelinkGroups) + { + foreach (SceneObjectPart part in pair.Value.Parts) + { + allPrimsInLocalGroups.Add(part); + } + } + if (allPrimsInLocalGroups.Count != delinkPrims.Count) + { + m_log.Warn("DelinkObjectsBySync: the # of prims of before-delink-groups is different from the incoming delink message. NEED BETTER CONCURRENCY CONTROL IMPLEMENTATION!!!"); + //TODO: further action + } + foreach (SceneObjectPart part in allPrimsInLocalGroups) + { + if (!delinkPrims.ContainsKey(part.UUID)) + { + m_log.Warn("DelinkObjectsBySync: some local prims in before-delink-groups not exist in the incoming delink message. NEED BETTER CONCURRENCY CONTROL IMPLEMENTATION!!!"); + //TODO: further action + } + } + //end of sanity checking + + //now work with localBeforeDelinkGroups, delinkPrims, and incomingAfterDelinkGroups + List localAfterDelinkGroups = new List(); + List remoteOldGroups = new List(); + List remoteNewGroups = new List(); + + foreach (SceneObjectGroup remoteGroup in incomingAfterDelinkGroups) + { + if (localBeforeDelinkGroups.ContainsKey(remoteGroup.UUID)) + { + remoteOldGroups.Add(remoteGroup); + } + else + { + remoteNewGroups.Add(remoteGroup); + } + } + + //update parts in old groups + foreach (SceneObjectGroup remoteGroupCopy in remoteOldGroups) + { + SceneObjectGroup localGroupCopy = localBeforeDelinkGroups[remoteGroupCopy.UUID]; + //update the parts in local copy with those in the remote copy + + + } + + //add new groups + + + } + finally + { + Monitor.Exit(m_updateLock); + } + } + + //update the parts in local copy with those in the updated copy + private void UpdatePartsInGroup(SceneObjectGroup localGroup, SceneObjectGroup updatedGroup) + { + //caller of this function should already lock on m_updateLock, hence no locking here + foreach (SceneObjectPart part in localGroup.Parts){ + + } + } + * */ + + #endregion //SYMMETRIC SYNC } } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 0b03d2c242..605e591bf8 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -3685,7 +3685,7 @@ namespace OpenSim.Region.Framework.Scenes } //Similar actions with DelinkFromGroup, except that m_scene.AddNewSceneObjectBySync is called - private SceneObjectGroup DelinkFromGroupBySync(SceneObjectPart linkPart, bool sendEvents) + public SceneObjectGroup DelinkFromGroupBySync(SceneObjectPart linkPart, bool sendEvents) { // m_log.DebugFormat( // "[SCENE OBJECT GROUP]: Delinking part {0}, {1} from group with root part {2}, {3}", diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 8aa4ca4516..61583160ef 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -5093,7 +5093,7 @@ namespace OpenSim.Region.Framework.Scenes m_parentGroup.Scene.EventManager.TriggerAggregateScriptEvents(this); } - m_log.Debug("SceneObjectPart Name-" +Name+", localID-" + m_localId + " updated"); + m_log.Debug("SceneObjectPart Name-" +Name+", UUID-"+UUID+" localID-" + m_localId + " updated"); return partUpdateResult; }