diff options
Diffstat (limited to 'OpenSim/Region/Environment/Modules/ContentManagementSystem/CMController.cs')
-rw-r--r-- | OpenSim/Region/Environment/Modules/ContentManagementSystem/CMController.cs | 1359 |
1 files changed, 698 insertions, 661 deletions
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMController.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMController.cs index d3ca3cf..a179fb1 100644 --- a/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMController.cs +++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMController.cs | |||
@@ -1,684 +1,721 @@ | |||
1 | #region Header | ||
2 | |||
1 | // CMController.cs | 3 | // CMController.cs |
2 | // User: bongiojp | 4 | // User: bongiojp |
3 | // | 5 | // |
4 | 6 | ||
7 | #endregion Header | ||
8 | |||
5 | using System; | 9 | using System; |
6 | using System.Collections.Generic; | ||
7 | using System.Collections; | 10 | using System.Collections; |
11 | using System.Collections.Generic; | ||
12 | using System.Diagnostics; | ||
13 | using System.Threading; | ||
14 | |||
8 | using libsecondlife; | 15 | using libsecondlife; |
16 | |||
9 | using OpenSim; | 17 | using OpenSim; |
10 | using OpenSim.Framework; | 18 | using OpenSim.Framework; |
11 | using OpenSim.Region.Environment.Interfaces; | 19 | using OpenSim.Region.Environment.Interfaces; |
12 | using OpenSim.Region.Environment.Scenes; | 20 | using OpenSim.Region.Environment.Scenes; |
13 | using log4net; | ||
14 | using OpenSim.Region.Physics.Manager; | 21 | using OpenSim.Region.Physics.Manager; |
22 | |||
23 | using log4net; | ||
24 | |||
15 | using Axiom.Math; | 25 | using Axiom.Math; |
16 | using System.Threading; | ||
17 | using System.Diagnostics; | ||
18 | 26 | ||
19 | namespace OpenSim.Region.Environment.Modules.ContentManagement | 27 | namespace OpenSim.Region.Environment.Modules.ContentManagement |
20 | { | 28 | { |
21 | 29 | /// <summary> | |
22 | /// <summary> | 30 | /// The controller in a Model-View-Controller framework. This controller catches actions by the avatars, creates work packets, loops through these work packets in a separate thread, |
23 | /// The controller in a Model-View-Controller framework. This controller catches actions by the avatars, creates work packets, loops through these work packets in a separate thread, | 31 | /// then dictates to the model how the data should change and dictates to the view which data should be displayed. The main mechanism for interaction is through the simchat system. |
24 | /// then dictates to the model how the data should change and dictates to the view which data should be displayed. The main mechanism for interaction is through the simchat system. | 32 | /// </summary> |
25 | /// </summary> | 33 | public class CMController |
26 | public class CMController | 34 | { |
27 | { | 35 | #region Static Fields |
28 | 36 | ||
29 | /// <value> | 37 | private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); |
30 | /// The structure that defines the basic unit of work which is produced when a user sends commands to the ContentMangaementSystem. | 38 | |
31 | /// </value> | 39 | /// <value> |
32 | private struct Work | 40 | /// The queue that keeps track of which actions have happened. The MainLoop thread eats through this queue. |
33 | { | 41 | /// </value> |
34 | public WorkType Type; | 42 | private static OpenSim.Framework.BlockingQueue<Work> m_WorkQueue = new OpenSim.Framework.BlockingQueue<Work>(); |
35 | public Object Data1; //Just space for holding data. | 43 | |
36 | public Object Data2; //Just more space for holding data. | 44 | #endregion Static Fields |
37 | public uint LocalId; //Convenient | 45 | |
38 | public LLUUID UUID; //Convenient | 46 | #region Fields |
39 | } | 47 | |
40 | 48 | bool init = false; | |
41 | /// <value> | 49 | int m_channel = -1; |
42 | /// Identifies what the data in struct Work should be used for. | 50 | |
43 | /// </value> | 51 | /// <value> |
44 | private enum WorkType | 52 | /// The estate module is used to identify which clients are estateManagers. Presently, the controller only pays attention to estate managers. |
45 | { | 53 | /// </value> |
46 | NONE, | 54 | IEstateModule m_estateModule = null; |
47 | OBJECTATTRIBUTECHANGE, | 55 | |
48 | PRIMITIVEADDED, | 56 | //These have to be global variables, threading doesn't allow for passing parameters. (Used in MainLoop) |
49 | OBJECTDUPLICATED, | 57 | CMModel m_model = null; |
50 | OBJECTKILLED, | 58 | |
51 | UNDODID, | 59 | /// <value> |
52 | NEWCLIENT, | 60 | /// A list of all the scenes that should be revisioned. Controller is the only class that keeps track of all scenes in the region. |
53 | SIMCHAT | 61 | /// </value> |
54 | } | 62 | Hashtable m_sceneList = Hashtable.Synchronized(new Hashtable()); |
55 | 63 | State m_state = State.NONE; | |
56 | /// <value> | 64 | Thread m_thread = null; |
57 | /// Used to keep track of whether a list has been produced yet and whether that list is up-to-date compard to latest revision on disk. | 65 | CMView m_view = null; |
58 | /// </value> | 66 | |
59 | [Flags] | 67 | #endregion Fields |
60 | private enum State | 68 | |
61 | { | 69 | #region Constructors |
62 | NONE = 0, | 70 | |
63 | DIRTY = 1, // The meta entities may not correctly represent the last revision. | 71 | /// <summary> |
64 | SHOWING_CHANGES = 1<<1 // The meta entities are being shown to user. | 72 | /// Initializes a work thread with an initial scene. Additional scenes should be added through the RegisterNewRegion method. |
65 | } | 73 | /// </summary> |
66 | 74 | /// <param name="model"> | |
67 | private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | 75 | /// <see cref="CMModel"/> |
68 | 76 | /// </param> | |
69 | /// <value> | 77 | /// <param name="view"> |
70 | /// The queue that keeps track of which actions have happened. The MainLoop thread eats through this queue. | 78 | /// <see cref="CMView"/> |
71 | /// </value> | 79 | /// </param> |
72 | private static OpenSim.Framework.BlockingQueue<Work> m_WorkQueue = new OpenSim.Framework.BlockingQueue<Work>(); | 80 | /// <param name="scene"> |
73 | 81 | /// The first scene to keep track of. <see cref="Scene"/> | |
74 | /// <value> | 82 | /// </param> |
75 | /// A list of all the scenes that should be revisioned. Controller is the only class that keeps track of all scenes in the region. | 83 | /// <param name="channel"> |
76 | /// </value> | 84 | /// The simchat channel number to listen to for instructions <see cref="System.Int32"/> |
77 | Hashtable m_sceneList = Hashtable.Synchronized(new Hashtable()); | 85 | /// </param> |
78 | 86 | public CMController(CMModel model, CMView view, Scene scene, int channel) | |
79 | /// <value> | 87 | { |
80 | /// The estate module is used to identify which clients are estateManagers. Presently, the controller only pays attention to estate managers. | 88 | m_model = model; m_view = view; m_channel = channel; |
81 | /// </value> | 89 | RegisterNewRegion(scene); |
82 | IEstateModule m_estateModule = null; | 90 | Initialize(model, view, scene, channel); |
83 | 91 | } | |
84 | Thread m_thread = null; | 92 | |
85 | State m_state = State.NONE; | 93 | #endregion Constructors |
86 | bool init = false; | 94 | |
87 | 95 | #region Private Methods | |
88 | //These have to be global variables, threading doesn't allow for passing parameters. (Used in MainLoop) | 96 | |
89 | CMModel m_model = null; | 97 | //------------------------------------------------ EVENTS ----------------------------------------------------// |
90 | CMView m_view = null; | 98 | private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, LLUUID regionID) |
91 | int m_channel = -1; | 99 | { |
92 | 100 | } | |
93 | /// <summary> | 101 | |
94 | /// Initializes a work thread with an initial scene. Additional scenes should be added through the RegisterNewRegion method. | 102 | /// <summary> |
95 | /// </summary> | 103 | /// Searches in all scenes for a SceneObjectGroup that contains a part with a specific localID. If found, the object is returned. Else null is returned. |
96 | /// <param name="model"> | 104 | /// </summary> |
97 | /// <see cref="CMModel"/> | 105 | private SceneObjectGroup GetGroupByPrim(uint localID) |
98 | /// </param> | 106 | { |
99 | /// <param name="view"> | 107 | foreach(Object currScene in m_sceneList.Values) |
100 | /// <see cref="CMView"/> | 108 | { |
101 | /// </param> | 109 | foreach (EntityBase ent in ((Scene)currScene).GetEntities()) |
102 | /// <param name="scene"> | 110 | { |
103 | /// The first scene to keep track of. <see cref="Scene"/> | 111 | if (ent is SceneObjectGroup) |
104 | /// </param> | 112 | { |
105 | /// <param name="channel"> | 113 | if (((SceneObjectGroup)ent).HasChildPrim(localID)) |
106 | /// The simchat channel number to listen to for instructions <see cref="System.Int32"/> | 114 | return (SceneObjectGroup)ent; |
107 | /// </param> | 115 | } |
108 | public CMController(CMModel model, CMView view, Scene scene, int channel) | 116 | } |
109 | { | 117 | } |
110 | m_model = model; m_view = view; m_channel = channel; | ||
111 | RegisterNewRegion(scene); | ||
112 | Initialize(model, view, scene, channel); | ||
113 | } | ||
114 | |||
115 | private void Initialize(CMModel model, CMView view, Scene scene, int channel) | ||
116 | { | ||
117 | lock(this) | ||
118 | { | ||
119 | m_estateModule = scene.RequestModuleInterface<IEstateModule>(); | ||
120 | m_thread = new Thread( MainLoop ); | ||
121 | m_thread.Name = "Content Management"; | ||
122 | m_thread.IsBackground = true; | ||
123 | m_thread.Start(); | ||
124 | ThreadTracker.Add(m_thread); | ||
125 | m_state = State.NONE; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | /// <summary> | ||
130 | /// Register a new scene object to keep track of for revisioning. Starts the controller monitoring actions of clients within the given scene. | ||
131 | /// </summary> | ||
132 | /// <param name="scene"> | ||
133 | /// A <see cref="Scene"/> | ||
134 | /// </param> | ||
135 | public void RegisterNewRegion(Scene scene) | ||
136 | { | ||
137 | m_sceneList.Add(scene.RegionInfo.RegionID, scene); | ||
138 | |||
139 | m_log.Debug("[CONTENT MANAGEMENT] Registering new region: " + scene.RegionInfo.RegionID); | ||
140 | m_log.Debug("[CONTENT MANAGEMENT] Initializing Content Management System."); | ||
141 | |||
142 | scene.EventManager.OnNewClient += StartManaging; | ||
143 | scene.EventManager.OnRemovePresence += StopManaging; | ||
144 | // scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel; | ||
145 | scene.EventManager.OnObjectBeingRemovedFromScene += GroupBeingDeleted; | ||
146 | } | ||
147 | |||
148 | /// <summary> | ||
149 | /// Run in a thread of its own. A endless loop that consumes (or blocks on) and work queue. Thw work queue is filled through client actions. | ||
150 | /// </summary> | ||
151 | private void MainLoop() | ||
152 | { | ||
153 | CMModel model = m_model; CMView view = m_view; int channel = m_channel; | ||
154 | Work currentJob = new Work(); | ||
155 | while(true) | ||
156 | { | ||
157 | currentJob = m_WorkQueue.Dequeue(); | ||
158 | m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- DeQueued a request"); | ||
159 | m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- Work type: " + currentJob.Type); | ||
160 | switch(currentJob.Type) | ||
161 | { | ||
162 | case WorkType.NONE: | ||
163 | break; | ||
164 | case WorkType.OBJECTATTRIBUTECHANGE: | ||
165 | ObjectAttributeChanged(model, view, currentJob.LocalId); | ||
166 | break; | ||
167 | case WorkType.PRIMITIVEADDED: | ||
168 | PrimitiveAdded(model, view, currentJob); | ||
169 | break; | ||
170 | case WorkType.OBJECTDUPLICATED: | ||
171 | ObjectDuplicated(model, view, currentJob.LocalId); | ||
172 | break; | ||
173 | case WorkType.OBJECTKILLED: | ||
174 | ObjectKilled(model, view, (SceneObjectGroup) currentJob.Data1); | ||
175 | break; | ||
176 | case WorkType.UNDODID: | ||
177 | UndoDid(model, view, currentJob.UUID); | ||
178 | break; | ||
179 | case WorkType.NEWCLIENT: | ||
180 | NewClient(view, (IClientAPI) currentJob.Data1); | ||
181 | break; | ||
182 | case WorkType.SIMCHAT: | ||
183 | m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- Message received: " + ((OSChatMessage) currentJob.Data1).Message); | ||
184 | SimChat(model, view, (OSChatMessage) currentJob.Data1, channel); | ||
185 | break; | ||
186 | default: | ||
187 | m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- uuuuuuuuuh, what?"); | ||
188 | break; | ||
189 | } | ||
190 | } | ||
191 | } | ||
192 | |||
193 | /// <summary> | ||
194 | /// Only called by the MainLoop. Updates the view of a new client with metaentities if diff-mode is currently enabled. | ||
195 | /// </summary> | ||
196 | private void NewClient(CMView view, IClientAPI client) | ||
197 | { | ||
198 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
199 | view.SendMetaEntitiesToNewClient(client); | ||
200 | } | ||
201 | |||
202 | /// <summary> | ||
203 | /// Only called by the MainLoop. Displays new green auras over the newly created part when a part is shift copied. | ||
204 | /// </summary> | ||
205 | private void ObjectDuplicated(CMModel model, CMView view, uint localId) | ||
206 | { | ||
207 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
208 | view.DisplayAuras(model.CheckForNewEntitiesMissingAuras( GetGroupByPrim(localId).Scene )); | ||
209 | } | ||
210 | |||
211 | /// <summary> | ||
212 | /// Only called by the MainLoop. | ||
213 | /// </summary> | ||
214 | private void ObjectKilled(CMModel model, CMView view, SceneObjectGroup group) | ||
215 | { | ||
216 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
217 | { | ||
218 | view.RemoveOrUpdateDeletedEntity(group); | ||
219 | model.RemoveOrUpdateDeletedEntity(group); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | /// <summary> | ||
224 | /// Only called by the MainLoop. | ||
225 | /// </summary> | ||
226 | private void UndoDid(CMModel model, CMView view, LLUUID uuid) | ||
227 | { | ||
228 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
229 | { | ||
230 | ContentManagementEntity ent = model.FindMetaEntityAffectedByUndo(uuid); | ||
231 | if (ent != null) | ||
232 | view.DisplayEntity(ent); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | /// <summary> | ||
237 | /// Only called by the MainLoop. | ||
238 | /// </summary> | ||
239 | private void ObjectAttributeChanged(CMModel model, CMView view, uint LocalId) | ||
240 | { | ||
241 | SceneObjectGroup group = null; | ||
242 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
243 | { | ||
244 | group = GetGroupByPrim(LocalId); | ||
245 | if (group != null) | ||
246 | { | ||
247 | view.DisplayAuras( model.UpdateNormalEntityEffects(group) ); //Might be a normal entity (green aura) | ||
248 | m_view.DisplayMetaEntity(group.UUID); //Might be a meta entity (blue aura) | ||
249 | } | ||
250 | } | ||
251 | } | ||
252 | |||
253 | /// <summary> | ||
254 | /// Only called by the MainLoop. | ||
255 | /// </summary> | ||
256 | private void PrimitiveAdded(CMModel model, CMView view, Work currentJob) | ||
257 | { | ||
258 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
259 | { | ||
260 | foreach(Object scene in m_sceneList.Values) | ||
261 | m_view.DisplayAuras(model.CheckForNewEntitiesMissingAuras((Scene) scene)); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | /// <summary> | ||
266 | /// Only called by the MainLoop. Takes the message from a user sent to the channel and executes the proper command. | ||
267 | /// </summary> | ||
268 | public void SimChat(CMModel model, CMView view, OSChatMessage e, int channel) | ||
269 | { | ||
270 | if (e.Channel != channel) | ||
271 | return; | ||
272 | if (e.Sender == null) | ||
273 | return; | ||
274 | |||
275 | m_log.Debug("[CONTENT MANAGEMENT] Message received: " + e.Message); | ||
276 | |||
277 | IClientAPI client = e.Sender; | ||
278 | Scene scene = (Scene) e.Scene; | ||
279 | string message = e.Message; | ||
280 | string[] args = e.Message.Split(new char[] {' '}); | ||
281 | |||
282 | ScenePresence avatar = scene.GetScenePresence(client.AgentId); | ||
283 | |||
284 | if (!(m_estateModule.IsManager(avatar.UUID))) | ||
285 | { | ||
286 | m_log.Debug("[CONTENT MANAGEMENT] Message sent from non Estate Manager ... ignoring."); | ||
287 | view.SendSimChatMessage(scene, "You must be an estate manager to perform that action."); | ||
288 | return; | ||
289 | } | ||
290 | |||
291 | switch(args[0]) | ||
292 | { | ||
293 | case "ci": | ||
294 | case "commit": | ||
295 | commit(message, scene, model, view); | ||
296 | break; | ||
297 | case "dm": | ||
298 | case "diff-mode": | ||
299 | diffmode(scene, model, view); | ||
300 | break; | ||
301 | case "rb": | ||
302 | case "rollback": | ||
303 | rollback(scene, model, view); | ||
304 | break; | ||
305 | case "help": | ||
306 | m_view.DisplayHelpMenu(scene); | ||
307 | break; | ||
308 | default: | ||
309 | view.SendSimChatMessage(scene, "Command not found: " + args[0]); | ||
310 | break; | ||
311 | } | ||
312 | } | ||
313 | |||
314 | /// <summary> | ||
315 | /// Only called from within the SimChat method. Hides all auras and meta entities, | ||
316 | /// retrieves the current scene object list with the most recent revision retrieved from the model for each scene, | ||
317 | /// then lets the view update the clients of the new objects. | ||
318 | /// </summary> | ||
319 | protected void rollback(Scene scene, CMModel model, CMView view) | ||
320 | { | ||
321 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
322 | { | ||
323 | view.HideAllAuras(); | ||
324 | view.HideAllMetaEntities(); | ||
325 | } | ||
326 | |||
327 | System.Collections.Generic.List<Scene> proximitySceneList = ScenesInOrderOfProximity( m_sceneList, scene); | ||
328 | foreach(Scene currScene in proximitySceneList) | ||
329 | model.RollbackRegion(currScene); | ||
330 | |||
331 | if ((m_state & State.DIRTY) != 0 ) | ||
332 | { | ||
333 | model.DeleteAllMetaObjects(); | ||
334 | foreach(Scene currScene in proximitySceneList) | ||
335 | model.UpdateCMEntities(currScene); | ||
336 | } | ||
337 | |||
338 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
339 | view.DisplayRecentChanges(); | ||
340 | |||
341 | } | ||
342 | |||
343 | /// <summary> | ||
344 | /// Only called from within the SimChat method. | ||
345 | /// </summary> | ||
346 | protected void diffmode(Scene scene, CMModel model, CMView view) | ||
347 | { | ||
348 | System.Collections.Generic.List<Scene> proximitySceneList = ScenesInOrderOfProximity( m_sceneList, scene); | ||
349 | |||
350 | if ((m_state & State.SHOWING_CHANGES) > 0) // TURN OFF | ||
351 | { | ||
352 | view.SendSimChatMessage(scene, "Hiding all meta objects."); | ||
353 | view.HideAllMetaEntities(); | ||
354 | view.HideAllAuras(); | ||
355 | view.SendSimChatMessage(scene, "Diff-mode = OFF"); | ||
356 | |||
357 | m_state &= ~State.SHOWING_CHANGES; | ||
358 | return; | ||
359 | } | ||
360 | else // TURN ON | ||
361 | { | ||
362 | if ((m_state & State.DIRTY) != 0 || m_state == State.NONE) | ||
363 | { | ||
364 | view.SendSimChatMessage(scene, "Hiding meta objects and replacing with latest revision"); | ||
365 | //Hide objects from users and Forget about them | ||
366 | view.HideAllMetaEntities(); | ||
367 | view.HideAllAuras(); | ||
368 | model.DeleteAllMetaObjects(); | ||
369 | //Recreate them from backend files | ||
370 | foreach(Object currScene in m_sceneList.Values) | ||
371 | model.UpdateCMEntities((Scene) currScene); | ||
372 | } | ||
373 | else if ((m_state & State.DIRTY) != 0) { | ||
374 | view.SendSimChatMessage(scene, "Forming list of meta entities with latest revision"); | ||
375 | foreach(Scene currScene in proximitySceneList) | ||
376 | model.UpdateCMEntities(currScene); | ||
377 | } | ||
378 | |||
379 | view.SendSimChatMessage(scene, "Displaying differences between last revision and current environment"); | ||
380 | foreach(Scene currScene in proximitySceneList) | ||
381 | model.CheckForNewEntitiesMissingAuras(currScene); | ||
382 | view.DisplayRecentChanges(); | ||
383 | |||
384 | view.SendSimChatMessage(scene, "Diff-mode = ON"); | ||
385 | m_state |= State.SHOWING_CHANGES; | ||
386 | m_state &= ~State.DIRTY; | ||
387 | } | ||
388 | } | ||
389 | |||
390 | /// <summary> | ||
391 | /// Only called from within the SimChat method. | ||
392 | /// </summary> | ||
393 | protected void commit(string message, Scene scene, CMModel model, CMView view) | ||
394 | { | ||
395 | System.Collections.Generic.List<Scene> proximitySceneList = ScenesInOrderOfProximity( m_sceneList, scene); | ||
396 | |||
397 | string[] args = message.Split(new char[] {' '}); | ||
398 | |||
399 | char[] logMessage = {' '}; | ||
400 | if (args.Length > 1) | ||
401 | { | ||
402 | logMessage = new char[message.Length - (args[0].Length)]; | ||
403 | message.CopyTo(args[0].Length, logMessage, 0, message.Length - (args[0].Length)); | ||
404 | } | ||
405 | |||
406 | m_log.Debug("[CONTENT MANAGEMENT] Saving terrain and objects of region."); | ||
407 | foreach(Scene currScene in proximitySceneList) | ||
408 | { | ||
409 | model.CommitRegion(currScene, new String(logMessage)); | ||
410 | view.SendSimChatMessage(scene, "Region Saved Successfully: " + currScene.RegionInfo.RegionName); | ||
411 | } | ||
412 | |||
413 | view.SendSimChatMessage(scene, "Successfully saved all regions."); | ||
414 | m_state |= State.DIRTY; | ||
415 | |||
416 | if ((m_state & State.SHOWING_CHANGES) > 0) //DISPLAY NEW CHANGES INSTEAD OF OLD CHANGES | ||
417 | { | ||
418 | view.SendSimChatMessage(scene, "Updating differences between new revision and current environment."); | ||
419 | //Hide objects from users and Forget about them | ||
420 | view.HideAllMetaEntities(); | ||
421 | view.HideAllAuras(); | ||
422 | model.DeleteAllMetaObjects(); | ||
423 | |||
424 | //Recreate them from backend files | ||
425 | foreach(Scene currScene in proximitySceneList) | ||
426 | { | ||
427 | model.UpdateCMEntities(currScene); | ||
428 | view.SendSimChatMessage(scene, "Finished updating differences between current scene and last revision: " + currScene.RegionInfo.RegionName); | ||
429 | } | ||
430 | |||
431 | //Display new objects to users1 | ||
432 | view.DisplayRecentChanges(); | ||
433 | view.SendSimChatMessage(scene, "Finished updating for DIFF-MODE."); | ||
434 | m_state &= ~(State.DIRTY); | ||
435 | m_state |= State.SHOWING_CHANGES; | ||
436 | } | ||
437 | } | ||
438 | |||
439 | /// <summary> | ||
440 | /// Takes a list of scenes and forms a new orderd list according to the proximity of scenes to the second argument. | ||
441 | /// </summary> | ||
442 | protected static System.Collections.Generic.List<Scene> ScenesInOrderOfProximity( Hashtable sceneList, Scene scene) | ||
443 | { | ||
444 | int somethingAddedToList = 1; | ||
445 | System.Collections.Generic.List<Scene> newList = new List<Scene>(); | ||
446 | newList.Add(scene); | ||
447 | |||
448 | if (! sceneList.ContainsValue(scene)) | ||
449 | { | ||
450 | foreach(Object sceneObj in sceneList) | ||
451 | newList.Add((Scene) sceneObj); | ||
452 | return newList; | ||
453 | } | ||
454 | |||
455 | while(somethingAddedToList > 0) | ||
456 | { | ||
457 | somethingAddedToList = 0; | ||
458 | for(int i = 0; i < newList.Count; i++) | ||
459 | { | ||
460 | foreach(Object sceneObj in sceneList.Values) | ||
461 | { | ||
462 | if (newList[i].CheckNeighborRegion(((Scene)sceneObj).RegionInfo) && (! newList.Contains((Scene)sceneObj)) ) | ||
463 | { | ||
464 | newList.Add((Scene)sceneObj); | ||
465 | somethingAddedToList++; | ||
466 | } | ||
467 | } | ||
468 | } | ||
469 | } | ||
470 | |||
471 | foreach(Object sceneObj in sceneList.Values) | ||
472 | if (! newList.Contains((Scene)sceneObj)) | ||
473 | newList.Add((Scene)sceneObj); | ||
474 | |||
475 | return newList; | ||
476 | } | ||
477 | |||
478 | /// <summary> | ||
479 | /// Searches in all scenes for a SceneObjectGroup that contains a part with a specific localID. If found, the object is returned. Else null is returned. | ||
480 | /// </summary> | ||
481 | private SceneObjectGroup GetGroupByPrim(uint localID) | ||
482 | { | ||
483 | foreach(Object currScene in m_sceneList.Values) | ||
484 | { | ||
485 | foreach (EntityBase ent in ((Scene)currScene).GetEntities()) | ||
486 | { | ||
487 | if (ent is SceneObjectGroup) | ||
488 | { | ||
489 | if (((SceneObjectGroup)ent).HasChildPrim(localID)) | ||
490 | return (SceneObjectGroup)ent; | ||
491 | } | ||
492 | } | ||
493 | } | ||
494 | return null; | 118 | return null; |
495 | } | 119 | } |
496 | //------------------------------------------------ EVENTS ----------------------------------------------------// | 120 | |
497 | 121 | private void Initialize(CMModel model, CMView view, Scene scene, int channel) | |
498 | private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, LLUUID regionID) | 122 | { |
499 | { | 123 | lock(this) |
500 | } | 124 | { |
501 | 125 | m_estateModule = scene.RequestModuleInterface<IEstateModule>(); | |
502 | /// <summary> | 126 | m_thread = new Thread( MainLoop ); |
503 | /// Adds extra handlers to a number of events so that the controller can produce work based on the client's actions. | 127 | m_thread.Name = "Content Management"; |
504 | /// </summary> | 128 | m_thread.IsBackground = true; |
505 | protected void StartManaging(IClientAPI client) | 129 | m_thread.Start(); |
506 | { | 130 | ThreadTracker.Add(m_thread); |
507 | m_log.Debug("[CONTENT MANAGEMENT] Registering channel with chat services."); | 131 | m_state = State.NONE; |
508 | client.OnChatFromViewer += SimChatSent; | 132 | } |
509 | init = true; | 133 | } |
510 | 134 | ||
511 | OnNewClient(client); | 135 | /// <summary> |
512 | 136 | /// Run in a thread of its own. A endless loop that consumes (or blocks on) and work queue. Thw work queue is filled through client actions. | |
513 | m_log.Debug("[CONTENT MANAGEMENT] Adding handlers to client."); | 137 | /// </summary> |
514 | client.OnUpdatePrimScale += UpdateSingleScale; | 138 | private void MainLoop() |
139 | { | ||
140 | CMModel model = m_model; CMView view = m_view; int channel = m_channel; | ||
141 | Work currentJob = new Work(); | ||
142 | while(true) | ||
143 | { | ||
144 | currentJob = m_WorkQueue.Dequeue(); | ||
145 | m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- DeQueued a request"); | ||
146 | m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- Work type: " + currentJob.Type); | ||
147 | switch(currentJob.Type) | ||
148 | { | ||
149 | case WorkType.NONE: | ||
150 | break; | ||
151 | case WorkType.OBJECTATTRIBUTECHANGE: | ||
152 | ObjectAttributeChanged(model, view, currentJob.LocalId); | ||
153 | break; | ||
154 | case WorkType.PRIMITIVEADDED: | ||
155 | PrimitiveAdded(model, view, currentJob); | ||
156 | break; | ||
157 | case WorkType.OBJECTDUPLICATED: | ||
158 | ObjectDuplicated(model, view, currentJob.LocalId); | ||
159 | break; | ||
160 | case WorkType.OBJECTKILLED: | ||
161 | ObjectKilled(model, view, (SceneObjectGroup) currentJob.Data1); | ||
162 | break; | ||
163 | case WorkType.UNDODID: | ||
164 | UndoDid(model, view, currentJob.UUID); | ||
165 | break; | ||
166 | case WorkType.NEWCLIENT: | ||
167 | NewClient(view, (IClientAPI) currentJob.Data1); | ||
168 | break; | ||
169 | case WorkType.SIMCHAT: | ||
170 | m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- Message received: " + ((OSChatMessage) currentJob.Data1).Message); | ||
171 | SimChat(model, view, (OSChatMessage) currentJob.Data1, channel); | ||
172 | break; | ||
173 | default: | ||
174 | m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- uuuuuuuuuh, what?"); | ||
175 | break; | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | |||
180 | /// <summary> | ||
181 | /// Only called by the MainLoop. Updates the view of a new client with metaentities if diff-mode is currently enabled. | ||
182 | /// </summary> | ||
183 | private void NewClient(CMView view, IClientAPI client) | ||
184 | { | ||
185 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
186 | view.SendMetaEntitiesToNewClient(client); | ||
187 | } | ||
188 | |||
189 | /// <summary> | ||
190 | /// Only called by the MainLoop. | ||
191 | /// </summary> | ||
192 | private void ObjectAttributeChanged(CMModel model, CMView view, uint LocalId) | ||
193 | { | ||
194 | SceneObjectGroup group = null; | ||
195 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
196 | { | ||
197 | group = GetGroupByPrim(LocalId); | ||
198 | if (group != null) | ||
199 | { | ||
200 | view.DisplayAuras( model.UpdateNormalEntityEffects(group) ); //Might be a normal entity (green aura) | ||
201 | m_view.DisplayMetaEntity(group.UUID); //Might be a meta entity (blue aura) | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | |||
206 | /// <summary> | ||
207 | /// Only called by the MainLoop. Displays new green auras over the newly created part when a part is shift copied. | ||
208 | /// </summary> | ||
209 | private void ObjectDuplicated(CMModel model, CMView view, uint localId) | ||
210 | { | ||
211 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
212 | view.DisplayAuras(model.CheckForNewEntitiesMissingAuras( GetGroupByPrim(localId).Scene )); | ||
213 | } | ||
214 | |||
215 | /// <summary> | ||
216 | /// Only called by the MainLoop. | ||
217 | /// </summary> | ||
218 | private void ObjectKilled(CMModel model, CMView view, SceneObjectGroup group) | ||
219 | { | ||
220 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
221 | { | ||
222 | view.RemoveOrUpdateDeletedEntity(group); | ||
223 | model.RemoveOrUpdateDeletedEntity(group); | ||
224 | } | ||
225 | } | ||
226 | |||
227 | /// <summary> | ||
228 | /// Only called by the MainLoop. | ||
229 | /// </summary> | ||
230 | private void PrimitiveAdded(CMModel model, CMView view, Work currentJob) | ||
231 | { | ||
232 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
233 | { | ||
234 | foreach(Object scene in m_sceneList.Values) | ||
235 | m_view.DisplayAuras(model.CheckForNewEntitiesMissingAuras((Scene) scene)); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /// <summary> | ||
240 | /// Only called by the MainLoop. | ||
241 | /// </summary> | ||
242 | private void UndoDid(CMModel model, CMView view, LLUUID uuid) | ||
243 | { | ||
244 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
245 | { | ||
246 | ContentManagementEntity ent = model.FindMetaEntityAffectedByUndo(uuid); | ||
247 | if (ent != null) | ||
248 | view.DisplayEntity(ent); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | #endregion Private Methods | ||
253 | |||
254 | #region Protected Methods | ||
255 | |||
256 | protected void GroupBeingDeleted(SceneObjectGroup group) | ||
257 | { | ||
258 | m_log.Debug("[CONTENT MANAGEMENT] Something was deleted!!!"); | ||
259 | Work moreWork = new Work(); | ||
260 | moreWork.Type = WorkType.OBJECTKILLED; | ||
261 | moreWork.Data1 = group.Copy(); | ||
262 | m_WorkQueue.Enqueue(moreWork); | ||
263 | } | ||
264 | |||
265 | protected void ObjectDuplicated(uint localID, LLVector3 offset, uint dupeFlags, LLUUID AgentID, LLUUID GroupID) | ||
266 | { | ||
267 | Work moreWork = new Work(); | ||
268 | moreWork.Type = WorkType.OBJECTDUPLICATED; | ||
269 | moreWork.LocalId = localID; | ||
270 | m_WorkQueue.Enqueue(moreWork); | ||
271 | m_log.Debug("[CONTENT MANAGEMENT] dup queue"); | ||
272 | } | ||
273 | |||
274 | protected void ObjectDuplicatedOnRay(uint localID, uint dupeFlags, LLUUID AgentID, LLUUID GroupID, | ||
275 | LLUUID RayTargetObj, LLVector3 RayEnd, LLVector3 RayStart, | ||
276 | bool BypassRaycast, bool RayEndIsIntersection, bool CopyCenters, bool CopyRotates) | ||
277 | { | ||
278 | Work moreWork = new Work(); | ||
279 | moreWork.Type = WorkType.OBJECTDUPLICATED; | ||
280 | moreWork.LocalId = localID; | ||
281 | m_WorkQueue.Enqueue(moreWork); | ||
282 | m_log.Debug("[CONTENT MANAGEMENT] dup queue"); | ||
283 | } | ||
284 | |||
285 | protected void OnNewClient(IClientAPI client) | ||
286 | { | ||
287 | Work moreWork = new Work(); | ||
288 | moreWork.Type = WorkType.NEWCLIENT; | ||
289 | moreWork.Data1 = client; | ||
290 | m_WorkQueue.Enqueue(moreWork); | ||
291 | m_log.Debug("[CONTENT MANAGEMENT] new client"); | ||
292 | } | ||
293 | |||
294 | protected void OnUnDid(IClientAPI remoteClient, LLUUID primId) | ||
295 | { | ||
296 | Work moreWork = new Work(); | ||
297 | moreWork.Type = WorkType.UNDODID; | ||
298 | moreWork.UUID = primId; | ||
299 | m_WorkQueue.Enqueue(moreWork); | ||
300 | m_log.Debug("[CONTENT MANAGEMENT] undid"); | ||
301 | } | ||
302 | |||
303 | /// <summary> | ||
304 | /// Takes a list of scenes and forms a new orderd list according to the proximity of scenes to the second argument. | ||
305 | /// </summary> | ||
306 | protected static System.Collections.Generic.List<Scene> ScenesInOrderOfProximity( Hashtable sceneList, Scene scene) | ||
307 | { | ||
308 | int somethingAddedToList = 1; | ||
309 | System.Collections.Generic.List<Scene> newList = new List<Scene>(); | ||
310 | newList.Add(scene); | ||
311 | |||
312 | if (! sceneList.ContainsValue(scene)) | ||
313 | { | ||
314 | foreach(Object sceneObj in sceneList) | ||
315 | newList.Add((Scene) sceneObj); | ||
316 | return newList; | ||
317 | } | ||
318 | |||
319 | while(somethingAddedToList > 0) | ||
320 | { | ||
321 | somethingAddedToList = 0; | ||
322 | for(int i = 0; i < newList.Count; i++) | ||
323 | { | ||
324 | foreach(Object sceneObj in sceneList.Values) | ||
325 | { | ||
326 | if (newList[i].CheckNeighborRegion(((Scene)sceneObj).RegionInfo) && (! newList.Contains((Scene)sceneObj)) ) | ||
327 | { | ||
328 | newList.Add((Scene)sceneObj); | ||
329 | somethingAddedToList++; | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | |||
335 | foreach(Object sceneObj in sceneList.Values) | ||
336 | if (! newList.Contains((Scene)sceneObj)) | ||
337 | newList.Add((Scene)sceneObj); | ||
338 | |||
339 | return newList; | ||
340 | } | ||
341 | |||
342 | //This is stupid, the same information is contained in the first and second argument | ||
343 | protected void SimChatSent(Object x, OSChatMessage e) | ||
344 | { | ||
345 | m_log.Debug("[CONTENT MANAGEMENT] SIMCHAT SENT !!!!!!!"); | ||
346 | m_log.Debug("[CONTENT MANAGEMENT] message was: " + e.Message); | ||
347 | Work moreWork = new Work(); | ||
348 | moreWork.Type = WorkType.SIMCHAT; | ||
349 | moreWork.Data1 = e; | ||
350 | m_WorkQueue.Enqueue(moreWork); | ||
351 | } | ||
352 | |||
353 | /// <summary> | ||
354 | /// Adds extra handlers to a number of events so that the controller can produce work based on the client's actions. | ||
355 | /// </summary> | ||
356 | protected void StartManaging(IClientAPI client) | ||
357 | { | ||
358 | m_log.Debug("[CONTENT MANAGEMENT] Registering channel with chat services."); | ||
359 | client.OnChatFromViewer += SimChatSent; | ||
360 | init = true; | ||
361 | |||
362 | OnNewClient(client); | ||
363 | |||
364 | m_log.Debug("[CONTENT MANAGEMENT] Adding handlers to client."); | ||
365 | client.OnUpdatePrimScale += UpdateSingleScale; | ||
515 | client.OnUpdatePrimGroupScale += UpdateMultipleScale; | 366 | client.OnUpdatePrimGroupScale += UpdateMultipleScale; |
516 | client.OnUpdatePrimGroupPosition += UpdateMultiplePosition; | 367 | client.OnUpdatePrimGroupPosition += UpdateMultiplePosition; |
517 | client.OnUpdatePrimSinglePosition += UpdateSinglePosition; | 368 | client.OnUpdatePrimSinglePosition += UpdateSinglePosition; |
518 | client.OnUpdatePrimGroupRotation += UpdateMultipleRotation; | 369 | client.OnUpdatePrimGroupRotation += UpdateMultipleRotation; |
519 | client.OnUpdatePrimSingleRotation += UpdateSingleRotation; | 370 | client.OnUpdatePrimSingleRotation += UpdateSingleRotation; |
520 | client.OnAddPrim += UpdateNewParts; | 371 | client.OnAddPrim += UpdateNewParts; |
521 | client.OnObjectDuplicate += ObjectDuplicated; | 372 | client.OnObjectDuplicate += ObjectDuplicated; |
522 | client.OnObjectDuplicateOnRay += ObjectDuplicatedOnRay; | 373 | client.OnObjectDuplicateOnRay += ObjectDuplicatedOnRay; |
523 | client.OnUndo += OnUnDid; | 374 | client.OnUndo += OnUnDid; |
524 | //client.OnUpdatePrimGroupMouseRotation += m_innerScene.UpdatePrimRotation; | 375 | //client.OnUpdatePrimGroupMouseRotation += m_innerScene.UpdatePrimRotation; |
525 | } | 376 | } |
526 | 377 | ||
527 | /// <summary> | 378 | /// <summary> |
528 | /// | 379 | /// |
529 | /// </summary> | 380 | /// </summary> |
530 | protected void StopManaging(LLUUID clientUUID) | 381 | protected void StopManaging(LLUUID clientUUID) |
531 | { | 382 | { |
532 | foreach(Object sceneobj in m_sceneList.Values) | 383 | foreach(Object sceneobj in m_sceneList.Values) |
533 | { | 384 | { |
534 | ScenePresence presence = ((Scene)sceneobj).GetScenePresence(clientUUID); | 385 | ScenePresence presence = ((Scene)sceneobj).GetScenePresence(clientUUID); |
535 | if (presence != null) | 386 | if (presence != null) |
536 | { | 387 | { |
537 | IClientAPI client = presence.ControllingClient; | 388 | IClientAPI client = presence.ControllingClient; |
538 | m_log.Debug("[CONTENT MANAGEMENT] Unregistering channel with chat services."); | 389 | m_log.Debug("[CONTENT MANAGEMENT] Unregistering channel with chat services."); |
539 | client.OnChatFromViewer -= SimChatSent; | 390 | client.OnChatFromViewer -= SimChatSent; |
540 | 391 | ||
541 | m_log.Debug("[CONTENT MANAGEMENT] Removing handlers to client"); | 392 | m_log.Debug("[CONTENT MANAGEMENT] Removing handlers to client"); |
542 | client.OnUpdatePrimScale -= UpdateSingleScale; | 393 | client.OnUpdatePrimScale -= UpdateSingleScale; |
543 | client.OnUpdatePrimGroupScale -= UpdateMultipleScale; | 394 | client.OnUpdatePrimGroupScale -= UpdateMultipleScale; |
544 | client.OnUpdatePrimGroupPosition -= UpdateMultiplePosition; | 395 | client.OnUpdatePrimGroupPosition -= UpdateMultiplePosition; |
545 | client.OnUpdatePrimSinglePosition -= UpdateSinglePosition; | 396 | client.OnUpdatePrimSinglePosition -= UpdateSinglePosition; |
546 | client.OnUpdatePrimGroupRotation -= UpdateMultipleRotation; | 397 | client.OnUpdatePrimGroupRotation -= UpdateMultipleRotation; |
547 | client.OnUpdatePrimSingleRotation -= UpdateSingleRotation; | 398 | client.OnUpdatePrimSingleRotation -= UpdateSingleRotation; |
548 | client.OnAddPrim -= UpdateNewParts; | 399 | client.OnAddPrim -= UpdateNewParts; |
549 | client.OnObjectDuplicate -= ObjectDuplicated; | 400 | client.OnObjectDuplicate -= ObjectDuplicated; |
550 | client.OnObjectDuplicateOnRay -= ObjectDuplicatedOnRay; | 401 | client.OnObjectDuplicateOnRay -= ObjectDuplicatedOnRay; |
551 | client.OnUndo -= OnUnDid; | 402 | client.OnUndo -= OnUnDid; |
552 | //client.OnUpdatePrimGroupMouseRotation += m_innerScene.UpdatePrimRotation; | 403 | //client.OnUpdatePrimGroupMouseRotation += m_innerScene.UpdatePrimRotation; |
553 | return; | 404 | return; |
554 | } | 405 | } |
555 | } | 406 | } |
556 | } | 407 | } |
557 | 408 | ||
558 | protected void GroupBeingDeleted(SceneObjectGroup group) | 409 | protected void UpdateMultiplePosition(uint localID, LLVector3 pos, IClientAPI remoteClient) |
559 | { | 410 | { |
560 | m_log.Debug("[CONTENT MANAGEMENT] Something was deleted!!!"); | 411 | Work moreWork = new Work(); |
561 | Work moreWork = new Work(); | 412 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; |
562 | moreWork.Type = WorkType.OBJECTKILLED; | 413 | moreWork.LocalId = localID; |
563 | moreWork.Data1 = group.Copy(); | 414 | m_WorkQueue.Enqueue(moreWork); |
564 | m_WorkQueue.Enqueue(moreWork); | 415 | m_log.Debug("[CONTENT MANAGEMENT] pos"); |
565 | } | 416 | } |
566 | 417 | ||
567 | //This is stupid, the same information is contained in the first and second argument | 418 | protected void UpdateMultipleRotation(uint localID, LLQuaternion rot, IClientAPI remoteClient) |
568 | protected void SimChatSent(Object x, OSChatMessage e) | 419 | { |
569 | { | 420 | Work moreWork = new Work(); |
570 | m_log.Debug("[CONTENT MANAGEMENT] SIMCHAT SENT !!!!!!!"); | 421 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; |
571 | m_log.Debug("[CONTENT MANAGEMENT] message was: " + e.Message); | 422 | moreWork.LocalId = localID; |
572 | Work moreWork = new Work(); | 423 | m_WorkQueue.Enqueue(moreWork); |
573 | moreWork.Type = WorkType.SIMCHAT; | 424 | m_log.Debug("[CONTENT MANAGEMENT] rot"); |
574 | moreWork.Data1 = e; | 425 | } |
575 | m_WorkQueue.Enqueue(moreWork); | 426 | |
576 | } | 427 | protected void UpdateMultipleScale(uint localID, LLVector3 scale, IClientAPI remoteClient) |
577 | 428 | { | |
578 | protected void ObjectDuplicated(uint localID, LLVector3 offset, uint dupeFlags, LLUUID AgentID, LLUUID GroupID) | 429 | Work moreWork = new Work(); |
579 | { | 430 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; |
580 | Work moreWork = new Work(); | 431 | moreWork.LocalId = localID; |
581 | moreWork.Type = WorkType.OBJECTDUPLICATED; | 432 | m_WorkQueue.Enqueue(moreWork); |
582 | moreWork.LocalId = localID; | 433 | m_log.Debug("[CONTENT MANAGEMENT]scale"); |
583 | m_WorkQueue.Enqueue(moreWork); | 434 | } |
584 | m_log.Debug("[CONTENT MANAGEMENT] dup queue"); | 435 | |
585 | } | 436 | protected void UpdateNewParts(LLUUID ownerID, LLVector3 RayEnd, LLQuaternion rot, PrimitiveBaseShape shape, |
586 | 437 | byte bypassRaycast, LLVector3 RayStart, LLUUID RayTargetID, | |
587 | protected void ObjectDuplicatedOnRay(uint localID, uint dupeFlags, LLUUID AgentID, LLUUID GroupID, | 438 | byte RayEndIsIntersection) |
588 | LLUUID RayTargetObj, LLVector3 RayEnd, LLVector3 RayStart, | 439 | { |
589 | bool BypassRaycast, bool RayEndIsIntersection, bool CopyCenters, bool CopyRotates) | 440 | Work moreWork = new Work(); |
590 | { | 441 | moreWork.Type = WorkType.PRIMITIVEADDED; |
591 | Work moreWork = new Work(); | 442 | moreWork.UUID = ownerID; |
592 | moreWork.Type = WorkType.OBJECTDUPLICATED; | 443 | m_WorkQueue.Enqueue(moreWork); |
593 | moreWork.LocalId = localID; | 444 | m_log.Debug("[CONTENT MANAGEMENT] new parts"); |
594 | m_WorkQueue.Enqueue(moreWork); | 445 | } |
595 | m_log.Debug("[CONTENT MANAGEMENT] dup queue"); | 446 | |
596 | } | 447 | protected void UpdateSinglePosition(uint localID, LLVector3 pos, IClientAPI remoteClient) |
597 | 448 | { | |
598 | protected void OnNewClient(IClientAPI client) | 449 | Work moreWork = new Work(); |
599 | { | 450 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; |
600 | Work moreWork = new Work(); | 451 | moreWork.LocalId = localID; |
601 | moreWork.Type = WorkType.NEWCLIENT; | 452 | m_WorkQueue.Enqueue(moreWork); |
602 | moreWork.Data1 = client; | 453 | m_log.Debug("[CONTENT MANAGEMENT] move"); |
603 | m_WorkQueue.Enqueue(moreWork); | 454 | } |
604 | m_log.Debug("[CONTENT MANAGEMENT] new client"); | 455 | |
605 | } | 456 | /// <summary> |
606 | 457 | /// | |
607 | protected void OnUnDid(IClientAPI remoteClient, LLUUID primId) | 458 | /// </summary> |
608 | { | 459 | protected void UpdateSingleRotation(uint localID, LLQuaternion rot, IClientAPI remoteClient) |
609 | Work moreWork = new Work(); | 460 | { |
610 | moreWork.Type = WorkType.UNDODID; | 461 | Work moreWork = new Work(); |
611 | moreWork.UUID = primId; | 462 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; |
612 | m_WorkQueue.Enqueue(moreWork); | 463 | moreWork.LocalId = localID; |
613 | m_log.Debug("[CONTENT MANAGEMENT] undid"); | 464 | m_WorkQueue.Enqueue(moreWork); |
614 | } | 465 | m_log.Debug("[CONTENT MANAGEMENT] rot"); |
615 | 466 | } | |
616 | protected void UpdateSinglePosition(uint localID, LLVector3 pos, IClientAPI remoteClient) | 467 | |
617 | { | 468 | protected void UpdateSingleScale(uint localID, LLVector3 scale, IClientAPI remoteClient) |
618 | Work moreWork = new Work(); | 469 | { |
619 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; | 470 | Work moreWork = new Work(); |
620 | moreWork.LocalId = localID; | 471 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; |
621 | m_WorkQueue.Enqueue(moreWork); | 472 | moreWork.LocalId = localID; |
622 | m_log.Debug("[CONTENT MANAGEMENT] move"); | 473 | m_WorkQueue.Enqueue(moreWork); |
623 | } | 474 | m_log.Debug("[CONTENT MANAGEMENT] scale"); |
624 | 475 | } | |
625 | /// <summary> | 476 | |
626 | /// | 477 | /// <summary> |
627 | /// </summary> | 478 | /// Only called from within the SimChat method. |
628 | protected void UpdateSingleRotation(uint localID, LLQuaternion rot, IClientAPI remoteClient) | 479 | /// </summary> |
629 | { | 480 | protected void commit(string message, Scene scene, CMModel model, CMView view) |
630 | Work moreWork = new Work(); | 481 | { |
631 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; | 482 | System.Collections.Generic.List<Scene> proximitySceneList = ScenesInOrderOfProximity( m_sceneList, scene); |
632 | moreWork.LocalId = localID; | 483 | |
633 | m_WorkQueue.Enqueue(moreWork); | 484 | string[] args = message.Split(new char[] {' '}); |
634 | m_log.Debug("[CONTENT MANAGEMENT] rot"); | 485 | |
635 | } | 486 | char[] logMessage = {' '}; |
636 | 487 | if (args.Length > 1) | |
637 | protected void UpdateSingleScale(uint localID, LLVector3 scale, IClientAPI remoteClient) | 488 | { |
638 | { | 489 | logMessage = new char[message.Length - (args[0].Length)]; |
639 | Work moreWork = new Work(); | 490 | message.CopyTo(args[0].Length, logMessage, 0, message.Length - (args[0].Length)); |
640 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; | 491 | } |
641 | moreWork.LocalId = localID; | 492 | |
642 | m_WorkQueue.Enqueue(moreWork); | 493 | m_log.Debug("[CONTENT MANAGEMENT] Saving terrain and objects of region."); |
643 | m_log.Debug("[CONTENT MANAGEMENT] scale"); | 494 | foreach(Scene currScene in proximitySceneList) |
644 | } | 495 | { |
645 | 496 | model.CommitRegion(currScene, new String(logMessage)); | |
646 | protected void UpdateMultiplePosition(uint localID, LLVector3 pos, IClientAPI remoteClient) | 497 | view.SendSimChatMessage(scene, "Region Saved Successfully: " + currScene.RegionInfo.RegionName); |
647 | { | 498 | } |
648 | Work moreWork = new Work(); | 499 | |
649 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; | 500 | view.SendSimChatMessage(scene, "Successfully saved all regions."); |
650 | moreWork.LocalId = localID; | 501 | m_state |= State.DIRTY; |
651 | m_WorkQueue.Enqueue(moreWork); | 502 | |
652 | m_log.Debug("[CONTENT MANAGEMENT] pos"); | 503 | if ((m_state & State.SHOWING_CHANGES) > 0) //DISPLAY NEW CHANGES INSTEAD OF OLD CHANGES |
653 | } | 504 | { |
654 | 505 | view.SendSimChatMessage(scene, "Updating differences between new revision and current environment."); | |
655 | protected void UpdateMultipleRotation(uint localID, LLQuaternion rot, IClientAPI remoteClient) | 506 | //Hide objects from users and Forget about them |
656 | { | 507 | view.HideAllMetaEntities(); |
657 | Work moreWork = new Work(); | 508 | view.HideAllAuras(); |
658 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; | 509 | model.DeleteAllMetaObjects(); |
659 | moreWork.LocalId = localID; | 510 | |
660 | m_WorkQueue.Enqueue(moreWork); | 511 | //Recreate them from backend files |
661 | m_log.Debug("[CONTENT MANAGEMENT] rot"); | 512 | foreach(Scene currScene in proximitySceneList) |
662 | } | 513 | { |
663 | 514 | model.UpdateCMEntities(currScene); | |
664 | protected void UpdateMultipleScale(uint localID, LLVector3 scale, IClientAPI remoteClient) | 515 | view.SendSimChatMessage(scene, "Finished updating differences between current scene and last revision: " + currScene.RegionInfo.RegionName); |
665 | { | 516 | } |
666 | Work moreWork = new Work(); | 517 | |
667 | moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE; | 518 | //Display new objects to users1 |
668 | moreWork.LocalId = localID; | 519 | view.DisplayRecentChanges(); |
669 | m_WorkQueue.Enqueue(moreWork); | 520 | view.SendSimChatMessage(scene, "Finished updating for DIFF-MODE."); |
670 | m_log.Debug("[CONTENT MANAGEMENT]scale"); | 521 | m_state &= ~(State.DIRTY); |
671 | } | 522 | m_state |= State.SHOWING_CHANGES; |
672 | 523 | } | |
673 | protected void UpdateNewParts(LLUUID ownerID, LLVector3 RayEnd, LLQuaternion rot, PrimitiveBaseShape shape, | 524 | } |
674 | byte bypassRaycast, LLVector3 RayStart, LLUUID RayTargetID, | 525 | |
675 | byte RayEndIsIntersection) | 526 | /// <summary> |
676 | { | 527 | /// Only called from within the SimChat method. |
677 | Work moreWork = new Work(); | 528 | /// </summary> |
678 | moreWork.Type = WorkType.PRIMITIVEADDED; | 529 | protected void diffmode(Scene scene, CMModel model, CMView view) |
679 | moreWork.UUID = ownerID; | 530 | { |
680 | m_WorkQueue.Enqueue(moreWork); | 531 | System.Collections.Generic.List<Scene> proximitySceneList = ScenesInOrderOfProximity( m_sceneList, scene); |
681 | m_log.Debug("[CONTENT MANAGEMENT] new parts"); | 532 | |
682 | } | 533 | if ((m_state & State.SHOWING_CHANGES) > 0) // TURN OFF |
683 | } | 534 | { |
684 | } | 535 | view.SendSimChatMessage(scene, "Hiding all meta objects."); |
536 | view.HideAllMetaEntities(); | ||
537 | view.HideAllAuras(); | ||
538 | view.SendSimChatMessage(scene, "Diff-mode = OFF"); | ||
539 | |||
540 | m_state &= ~State.SHOWING_CHANGES; | ||
541 | return; | ||
542 | } | ||
543 | else // TURN ON | ||
544 | { | ||
545 | if ((m_state & State.DIRTY) != 0 || m_state == State.NONE) | ||
546 | { | ||
547 | view.SendSimChatMessage(scene, "Hiding meta objects and replacing with latest revision"); | ||
548 | //Hide objects from users and Forget about them | ||
549 | view.HideAllMetaEntities(); | ||
550 | view.HideAllAuras(); | ||
551 | model.DeleteAllMetaObjects(); | ||
552 | //Recreate them from backend files | ||
553 | foreach(Object currScene in m_sceneList.Values) | ||
554 | model.UpdateCMEntities((Scene) currScene); | ||
555 | } | ||
556 | else if ((m_state & State.DIRTY) != 0) { | ||
557 | view.SendSimChatMessage(scene, "Forming list of meta entities with latest revision"); | ||
558 | foreach(Scene currScene in proximitySceneList) | ||
559 | model.UpdateCMEntities(currScene); | ||
560 | } | ||
561 | |||
562 | view.SendSimChatMessage(scene, "Displaying differences between last revision and current environment"); | ||
563 | foreach(Scene currScene in proximitySceneList) | ||
564 | model.CheckForNewEntitiesMissingAuras(currScene); | ||
565 | view.DisplayRecentChanges(); | ||
566 | |||
567 | view.SendSimChatMessage(scene, "Diff-mode = ON"); | ||
568 | m_state |= State.SHOWING_CHANGES; | ||
569 | m_state &= ~State.DIRTY; | ||
570 | } | ||
571 | } | ||
572 | |||
573 | /// <summary> | ||
574 | /// Only called from within the SimChat method. Hides all auras and meta entities, | ||
575 | /// retrieves the current scene object list with the most recent revision retrieved from the model for each scene, | ||
576 | /// then lets the view update the clients of the new objects. | ||
577 | /// </summary> | ||
578 | protected void rollback(Scene scene, CMModel model, CMView view) | ||
579 | { | ||
580 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
581 | { | ||
582 | view.HideAllAuras(); | ||
583 | view.HideAllMetaEntities(); | ||
584 | } | ||
585 | |||
586 | System.Collections.Generic.List<Scene> proximitySceneList = ScenesInOrderOfProximity( m_sceneList, scene); | ||
587 | foreach(Scene currScene in proximitySceneList) | ||
588 | model.RollbackRegion(currScene); | ||
589 | |||
590 | if ((m_state & State.DIRTY) != 0 ) | ||
591 | { | ||
592 | model.DeleteAllMetaObjects(); | ||
593 | foreach(Scene currScene in proximitySceneList) | ||
594 | model.UpdateCMEntities(currScene); | ||
595 | } | ||
596 | |||
597 | if ((m_state & State.SHOWING_CHANGES) > 0) | ||
598 | view.DisplayRecentChanges(); | ||
599 | } | ||
600 | |||
601 | #endregion Protected Methods | ||
602 | |||
603 | #region Public Methods | ||
604 | |||
605 | /// <summary> | ||
606 | /// Register a new scene object to keep track of for revisioning. Starts the controller monitoring actions of clients within the given scene. | ||
607 | /// </summary> | ||
608 | /// <param name="scene"> | ||
609 | /// A <see cref="Scene"/> | ||
610 | /// </param> | ||
611 | public void RegisterNewRegion(Scene scene) | ||
612 | { | ||
613 | m_sceneList.Add(scene.RegionInfo.RegionID, scene); | ||
614 | |||
615 | m_log.Debug("[CONTENT MANAGEMENT] Registering new region: " + scene.RegionInfo.RegionID); | ||
616 | m_log.Debug("[CONTENT MANAGEMENT] Initializing Content Management System."); | ||
617 | |||
618 | scene.EventManager.OnNewClient += StartManaging; | ||
619 | scene.EventManager.OnRemovePresence += StopManaging; | ||
620 | // scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel; | ||
621 | scene.EventManager.OnObjectBeingRemovedFromScene += GroupBeingDeleted; | ||
622 | } | ||
623 | |||
624 | /// <summary> | ||
625 | /// Only called by the MainLoop. Takes the message from a user sent to the channel and executes the proper command. | ||
626 | /// </summary> | ||
627 | public void SimChat(CMModel model, CMView view, OSChatMessage e, int channel) | ||
628 | { | ||
629 | if (e.Channel != channel) | ||
630 | return; | ||
631 | if (e.Sender == null) | ||
632 | return; | ||
633 | |||
634 | m_log.Debug("[CONTENT MANAGEMENT] Message received: " + e.Message); | ||
635 | |||
636 | IClientAPI client = e.Sender; | ||
637 | Scene scene = (Scene) e.Scene; | ||
638 | string message = e.Message; | ||
639 | string[] args = e.Message.Split(new char[] {' '}); | ||
640 | |||
641 | ScenePresence avatar = scene.GetScenePresence(client.AgentId); | ||
642 | |||
643 | if (!(m_estateModule.IsManager(avatar.UUID))) | ||
644 | { | ||
645 | m_log.Debug("[CONTENT MANAGEMENT] Message sent from non Estate Manager ... ignoring."); | ||
646 | view.SendSimChatMessage(scene, "You must be an estate manager to perform that action."); | ||
647 | return; | ||
648 | } | ||
649 | |||
650 | switch(args[0]) | ||
651 | { | ||
652 | case "ci": | ||
653 | case "commit": | ||
654 | commit(message, scene, model, view); | ||
655 | break; | ||
656 | case "dm": | ||
657 | case "diff-mode": | ||
658 | diffmode(scene, model, view); | ||
659 | break; | ||
660 | case "rb": | ||
661 | case "rollback": | ||
662 | rollback(scene, model, view); | ||
663 | break; | ||
664 | case "help": | ||
665 | m_view.DisplayHelpMenu(scene); | ||
666 | break; | ||
667 | default: | ||
668 | view.SendSimChatMessage(scene, "Command not found: " + args[0]); | ||
669 | break; | ||
670 | } | ||
671 | } | ||
672 | |||
673 | #endregion Public Methods | ||
674 | |||
675 | #region Other | ||
676 | |||
677 | /// <value> | ||
678 | /// Used to keep track of whether a list has been produced yet and whether that list is up-to-date compard to latest revision on disk. | ||
679 | /// </value> | ||
680 | [Flags] | ||
681 | private enum State | ||
682 | { | ||
683 | NONE = 0, | ||
684 | DIRTY = 1, // The meta entities may not correctly represent the last revision. | ||
685 | SHOWING_CHANGES = 1<<1 // The meta entities are being shown to user. | ||
686 | } | ||
687 | |||
688 | /// <value> | ||
689 | /// The structure that defines the basic unit of work which is produced when a user sends commands to the ContentMangaementSystem. | ||
690 | /// </value> | ||
691 | private struct Work | ||
692 | { | ||
693 | #region Fields | ||
694 | |||
695 | public Object Data1; //Just space for holding data. | ||
696 | public Object Data2; //Just more space for holding data. | ||
697 | public uint LocalId; //Convenient | ||
698 | public WorkType Type; | ||
699 | public LLUUID UUID; //Convenient | ||
700 | |||
701 | #endregion Fields | ||
702 | } | ||
703 | |||
704 | /// <value> | ||
705 | /// Identifies what the data in struct Work should be used for. | ||
706 | /// </value> | ||
707 | private enum WorkType | ||
708 | { | ||
709 | NONE, | ||
710 | OBJECTATTRIBUTECHANGE, | ||
711 | PRIMITIVEADDED, | ||
712 | OBJECTDUPLICATED, | ||
713 | OBJECTKILLED, | ||
714 | UNDODID, | ||
715 | NEWCLIENT, | ||
716 | SIMCHAT | ||
717 | } | ||
718 | |||
719 | #endregion Other | ||
720 | } | ||
721 | } \ No newline at end of file | ||