예제 #1
0
        /// <summary>
        /// Only called from within the SimChat method. Hides all auras and meta entities,
        /// retrieves the current scene object list with the most recent revision retrieved from the model for each scene,
        /// then lets the view update the clients of the new objects.
        /// </summary>
        protected void rollback(Scene scene, CMModel model, CMView view)
        {
            if ((m_state & State.SHOWING_CHANGES) > 0)
            {
                view.HideAllAuras();
                view.HideAllMetaEntities();
            }

            System.Collections.Generic.List <Scene> proximitySceneList = ScenesInOrderOfProximity(m_sceneList, scene);
            foreach (Scene currScene in proximitySceneList)
            {
                model.RollbackRegion(currScene);
            }

            if ((m_state & State.DIRTY) != 0)
            {
                model.DeleteAllMetaObjects();
                foreach (Scene currScene in proximitySceneList)
                {
                    model.UpdateCMEntities(currScene);
                }
            }

            if ((m_state & State.SHOWING_CHANGES) > 0)
            {
                view.DisplayRecentChanges();
            }
        }
예제 #2
0
 /// <summary>
 /// Only called by the MainLoop. Displays new green auras over the newly created part when a part is shift copied.
 /// </summary>
 private void ObjectDuplicated(CMModel model, CMView view, uint localId)
 {
     if ((m_state & State.SHOWING_CHANGES) > 0)
     {
         view.DisplayAuras(model.CheckForNewEntitiesMissingAuras(GetGroupByPrim(localId).Scene));
     }
 }
예제 #3
0
        /// <summary>
        /// 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.
        /// </summary>
        private void MainLoop()
        {
            try
            {
                CMModel model = m_model; CMView view = m_view; int channel = m_channel;
                Work    currentJob = new Work();
                while (true)
                {
                    currentJob = m_WorkQueue.Dequeue();
                    m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- DeQueued a request");
                    m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- Work type: " + currentJob.Type);
                    switch (currentJob.Type)
                    {
                    case WorkType.NONE:
                        break;

                    case WorkType.OBJECTATTRIBUTECHANGE:
                        ObjectAttributeChanged(model, view, currentJob.LocalId);
                        break;

                    case WorkType.PRIMITIVEADDED:
                        PrimitiveAdded(model, view, currentJob);
                        break;

                    case WorkType.OBJECTDUPLICATED:
                        ObjectDuplicated(model, view, currentJob.LocalId);
                        break;

                    case WorkType.OBJECTKILLED:
                        ObjectKilled(model, view, (SceneObjectGroup)currentJob.Data1);
                        break;

                    case WorkType.UNDODID:
                        UndoDid(model, view, currentJob.UUID);
                        break;

                    case WorkType.NEWCLIENT:
                        NewClient(view, (IClientAPI)currentJob.Data1);
                        break;

                    case WorkType.SIMCHAT:
                        m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- Message received:  " + ((OSChatMessage)currentJob.Data1).Message);
                        SimChat(model, view, (OSChatMessage)currentJob.Data1, channel);
                        break;

                    default:
                        m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- uuuuuuuuuh, what?");
                        break;
                    }
                }
            }
            catch (Exception e)
            {
                // TODO: Let users in the sim and those entering it and possibly an external watchdog know what has happened
                m_log.ErrorFormat(
                    "[CONTENT MANAGEMENT]: Content management thread terminating with exception.  PLEASE REBOOT YOUR SIM - CONTENT MANAGEMENT WILL NOT BE AVAILABLE UNTIL YOU DO.  Exception is {0}",
                    e);
            }
        }
예제 #4
0
 /// <summary>
 /// Only called by the MainLoop.
 /// </summary>
 private void ObjectKilled(CMModel model, CMView view, SceneObjectGroup group)
 {
     if ((m_state & State.SHOWING_CHANGES) > 0)
     {
         view.RemoveOrUpdateDeletedEntity(group);
         model.RemoveOrUpdateDeletedEntity(group);
     }
 }
예제 #5
0
 private void Initialize(CMModel model, CMView view, Scene scene, int channel)
 {
     lock (this)
     {
         m_estateModule = scene.RequestModuleInterface <IEstateModule>();
         Watchdog.StartThread(MainLoop, "Content Management", ThreadPriority.Normal, true);
         m_state = State.NONE;
     }
 }
예제 #6
0
        public void Initialise(Scene scene, IConfigSource source)
        {
            string databaseDir = "./";
            string database    = "FileSystemDatabase";
            int    channel     = 345;

            try
            {
                if (source.Configs["CMS"] == null)
                {
                    return;
                }

                m_enabled   = source.Configs["CMS"].GetBoolean("enabled", false);
                databaseDir = source.Configs["CMS"].GetString("directory", databaseDir);
                database    = source.Configs["CMS"].GetString("database", database);
                channel     = source.Configs["CMS"].GetInt("channel", channel);

                if (database != "FileSystemDatabase" && database != "GitDatabase")
                {
                    m_log.ErrorFormat("[Content Management]: The Database attribute must be defined as either FileSystemDatabase or GitDatabase");
                    m_enabled = false;
                }
            }
            catch (Exception e)
            {
                m_log.ErrorFormat("[Content Management]: Exception thrown while reading parameters from configuration file. Message: " + e);
                m_enabled = false;
            }

            if (!m_enabled)
            {
                m_log.Info("[Content Management]: Content Management System is not Enabled.");
                return;
            }

            lock (this)
            {
                if (!initialised) //only init once
                {
                    m_view    = new CMView();
                    m_model   = new CMModel();
                    m_control = new CMController(m_model, m_view, scene, channel);
                    m_model.Initialise(database);
                    m_view.Initialise(m_model);

                    initialised = true;
                    m_model.InitialiseDatabase(scene, databaseDir);
                }
                else
                {
                    m_model.InitialiseDatabase(scene, databaseDir);
                    m_control.RegisterNewRegion(scene);
                }
            }
        }
예제 #7
0
 /// <summary>
 /// Only called by the MainLoop.
 /// </summary>
 private void PrimitiveAdded(CMModel model, CMView view, Work currentJob)
 {
     if ((m_state & State.SHOWING_CHANGES) > 0)
     {
         foreach (Object scene in m_sceneList.Values)
         {
             m_view.DisplayAuras(model.CheckForNewEntitiesMissingAuras((Scene)scene));
         }
     }
 }
예제 #8
0
 /// <summary>
 /// Only called by the MainLoop.
 /// </summary>
 private void UndoDid(CMModel model, CMView view, UUID uuid)
 {
     if ((m_state & State.SHOWING_CHANGES) > 0)
     {
         ContentManagementEntity ent = model.FindMetaEntityAffectedByUndo(uuid);
         if (ent != null)
         {
             view.DisplayEntity(ent);
         }
     }
 }
예제 #9
0
        /// <summary>
        /// Only called by the MainLoop. Takes the message from a user sent to the channel and executes the proper command.
        /// </summary>
        public void SimChat(CMModel model, CMView view, OSChatMessage e, int channel)
        {
            if (e.Channel != channel)
            {
                return;
            }
            if (e.Sender == null)
            {
                return;
            }

            m_log.Debug("[CONTENT MANAGEMENT] Message received:  " + e.Message);

            IClientAPI client  = e.Sender;
            Scene      scene   = (Scene)e.Scene;
            string     message = e.Message;

            string[] args = e.Message.Split(new  char[] { ' ' });

            ScenePresence avatar = scene.GetScenePresence(client.AgentId);

            if (!(m_estateModule.IsManager(avatar.UUID)))
            {
                m_log.Debug("[CONTENT MANAGEMENT] Message sent from non Estate Manager ... ignoring.");
                view.SendSimChatMessage(scene, "You must be an estate manager to perform that action.");
                return;
            }

            switch (args[0])
            {
            case "ci":
            case "commit":
                commit(message, scene, model, view);
                break;

            case "dm":
            case "diff-mode":
                diffmode(scene, model, view);
                break;

            case "rb":
            case "rollback":
                rollback(scene, model, view);
                break;

            case "help":
                m_view.DisplayHelpMenu(scene);
                break;

            default:
                view.SendSimChatMessage(scene, "Command not found: " + args[0]);
                break;
            }
        }
예제 #10
0
 private void Initialize(CMModel model, CMView view, Scene scene, int channel)
 {
     lock (this)
     {
         m_estateModule        = scene.RequestModuleInterface <IEstateModule>();
         m_thread              = new Thread(MainLoop);
         m_thread.Name         = "Content Management";
         m_thread.IsBackground = true;
         m_thread.Start();
         ThreadTracker.Add(m_thread);
         m_state = State.NONE;
     }
 }
예제 #11
0
        /// <summary>
        /// Only called by the MainLoop.
        /// </summary>
        private void ObjectAttributeChanged(CMModel model, CMView view, uint LocalId)
        {
            SceneObjectGroup group = null;

            if ((m_state & State.SHOWING_CHANGES) > 0)
            {
                group = GetGroupByPrim(LocalId);
                if (group != null)
                {
                    view.DisplayAuras(model.UpdateNormalEntityEffects(group)); //Might be a normal entity (green aura)
                    m_view.DisplayMetaEntity(group.UUID);                      //Might be a meta entity (blue aura)
                }
            }
        }
예제 #12
0
        /// <summary>
        /// Only called from within the SimChat method.
        /// </summary>
        protected void diffmode(Scene scene, CMModel model, CMView view)
        {
            System.Collections.Generic.List <Scene> proximitySceneList = ScenesInOrderOfProximity(m_sceneList, scene);

            if ((m_state & State.SHOWING_CHANGES) > 0)  // TURN OFF
            {
                view.SendSimChatMessage(scene, "Hiding all meta objects.");
                view.HideAllMetaEntities();
                view.HideAllAuras();
                view.SendSimChatMessage(scene, "Diff-mode = OFF");

                m_state &= ~State.SHOWING_CHANGES;
                return;
            }
            else // TURN ON
            {
                if ((m_state & State.DIRTY) != 0 || m_state == State.NONE)
                {
                    view.SendSimChatMessage(scene, "Hiding meta objects and replacing with latest revision");
                    //Hide objects from users and Forget about them
                    view.HideAllMetaEntities();
                    view.HideAllAuras();
                    model.DeleteAllMetaObjects();
                    //Recreate them from backend files
                    foreach (Object currScene in m_sceneList.Values)
                    {
                        model.UpdateCMEntities((Scene)currScene);
                    }
                }
                else if ((m_state & State.DIRTY) != 0)
                {
                    view.SendSimChatMessage(scene, "Forming list of meta entities with latest revision");
                    foreach (Scene currScene in proximitySceneList)
                    {
                        model.UpdateCMEntities(currScene);
                    }
                }

                view.SendSimChatMessage(scene, "Displaying differences between last revision and current environment");
                foreach (Scene currScene in proximitySceneList)
                {
                    model.CheckForNewEntitiesMissingAuras(currScene);
                }
                view.DisplayRecentChanges();

                view.SendSimChatMessage(scene, "Diff-mode = ON");
                m_state |= State.SHOWING_CHANGES;
                m_state &= ~State.DIRTY;
            }
        }
예제 #13
0
        /// <summary>
        /// Only called from within the SimChat method.
        /// </summary>
        protected void commit(string message, Scene scene, CMModel model, CMView view)
        {
            System.Collections.Generic.List <Scene> proximitySceneList = ScenesInOrderOfProximity(m_sceneList, scene);

            string[] args = message.Split(new  char[] { ' ' });

            char[] logMessage = { ' ' };
            if (args.Length > 1)
            {
                logMessage = new char[message.Length - (args[0].Length)];
                message.CopyTo(args[0].Length, logMessage, 0, message.Length - (args[0].Length));
            }

            m_log.Debug("[CONTENT MANAGEMENT] Saving terrain and objects of region.");
            foreach (Scene currScene in proximitySceneList)
            {
                model.CommitRegion(currScene, new String(logMessage));
                view.SendSimChatMessage(scene, "Region Saved Successfully: " + currScene.RegionInfo.RegionName);
            }

            view.SendSimChatMessage(scene, "Successfully saved all regions.");
            m_state |= State.DIRTY;

            if ((m_state & State.SHOWING_CHANGES) > 0) //DISPLAY NEW CHANGES INSTEAD OF OLD CHANGES
            {
                view.SendSimChatMessage(scene, "Updating differences between new revision and current environment.");
                //Hide objects from users and Forget about them
                view.HideAllMetaEntities();
                view.HideAllAuras();
                model.DeleteAllMetaObjects();

                //Recreate them from backend files
                foreach (Scene currScene in proximitySceneList)
                {
                    model.UpdateCMEntities(currScene);
                    view.SendSimChatMessage(scene, "Finished updating differences between current scene and last revision: " + currScene.RegionInfo.RegionName);
                }

                //Display new objects to users1
                view.DisplayRecentChanges();
                view.SendSimChatMessage(scene, "Finished updating for DIFF-MODE.");
                m_state &= ~(State.DIRTY);
                m_state |= State.SHOWING_CHANGES;
            }
        }
예제 #14
0
        /// <summary>
        /// Only called by the MainLoop. Takes the message from a user sent to the channel and executes the proper command.
        /// </summary>
        public void SimChat(CMModel model, CMView view, OSChatMessage e, int channel)
        {
            if (e.Channel != channel)
                return;
            if (e.Sender == null)
                return;

            m_log.Debug("[CONTENT MANAGEMENT] Message received:  " + e.Message);

            IClientAPI client = e.Sender;
            Scene scene = (Scene) e.Scene;
            string message = e.Message;
            string[] args = e.Message.Split(new  char[] {' '});

            ScenePresence avatar = scene.GetScenePresence(client.AgentId);

            if (!(m_estateModule.IsManager(avatar.UUID)))
            {
                m_log.Debug("[CONTENT MANAGEMENT] Message sent from non Estate Manager ... ignoring.");
                view.SendSimChatMessage(scene, "You must be an estate manager to perform that action.");
                return;
            }

            switch (args[0])
            {
            case "ci":
            case "commit":
                commit(message, scene, model, view);
                break;
            case "dm":
            case "diff-mode":
                diffmode(scene, model, view);
                break;
            case "rb":
            case "rollback":
                rollback(scene, model, view);
                break;
            case "help":
                m_view.DisplayHelpMenu(scene);
                break;
            default:
                view.SendSimChatMessage(scene, "Command not found: " + args[0]);
                break;
            }
        }
예제 #15
0
        /// <summary>
        /// Only called from within the SimChat method.
        /// </summary>
        protected void diffmode(Scene scene, CMModel model, CMView view)
        {
            System.Collections.Generic.List<Scene> proximitySceneList = ScenesInOrderOfProximity(m_sceneList, scene);

            if ((m_state & State.SHOWING_CHANGES) > 0)  // TURN OFF
            {
                view.SendSimChatMessage(scene, "Hiding all meta objects.");
                view.HideAllMetaEntities();
                view.HideAllAuras();
                view.SendSimChatMessage(scene, "Diff-mode = OFF");

                m_state &= ~State.SHOWING_CHANGES;
                return;
            }
            else // TURN ON
            {
                if ((m_state & State.DIRTY) != 0 || m_state == State.NONE)
                {
                    view.SendSimChatMessage(scene, "Hiding meta objects and replacing with latest revision");
                    //Hide objects from users and Forget about them
                    view.HideAllMetaEntities();
                    view.HideAllAuras();
                    model.DeleteAllMetaObjects();
                    //Recreate them from backend files
                    foreach (Object currScene in m_sceneList.Values)
                        model.UpdateCMEntities((Scene) currScene);
                }
                else if ((m_state & State.DIRTY) != 0) {
                    view.SendSimChatMessage(scene, "Forming list of meta entities with latest revision");
                    foreach (Scene currScene in proximitySceneList)
                        model.UpdateCMEntities(currScene);
                }

                view.SendSimChatMessage(scene, "Displaying differences between last revision and current environment");
                foreach (Scene currScene in proximitySceneList)
                    model.CheckForNewEntitiesMissingAuras(currScene);
                view.DisplayRecentChanges();

                view.SendSimChatMessage(scene, "Diff-mode = ON");
                m_state |= State.SHOWING_CHANGES;
                m_state &= ~State.DIRTY;
            }
        }
예제 #16
0
        /// <summary>
        /// Only called from within the SimChat method. Hides all auras and meta entities,
        /// retrieves the current scene object list with the most recent revision retrieved from the model for each scene,
        /// then lets the view update the clients of the new objects.
        /// </summary>
        protected void rollback(Scene scene, CMModel model, CMView view)
        {
            if ((m_state & State.SHOWING_CHANGES) > 0)
            {
                view.HideAllAuras();
                view.HideAllMetaEntities();
            }

            System.Collections.Generic.List<Scene> proximitySceneList = ScenesInOrderOfProximity(m_sceneList, scene);
            foreach (Scene currScene in proximitySceneList)
                model.RollbackRegion(currScene);

            if ((m_state & State.DIRTY) != 0)
            {
                model.DeleteAllMetaObjects();
                foreach (Scene currScene in proximitySceneList)
                    model.UpdateCMEntities(currScene);
            }

            if ((m_state & State.SHOWING_CHANGES) > 0)
                view.DisplayRecentChanges();
        }
예제 #17
0
 /// <summary>
 /// Only called by the MainLoop.
 /// </summary>
 private void UndoDid(CMModel model, CMView view, UUID uuid)
 {
     if ((m_state & State.SHOWING_CHANGES) > 0)
     {
         ContentManagementEntity ent = model.FindMetaEntityAffectedByUndo(uuid);
         if (ent != null)
             view.DisplayEntity(ent);
     }
 }
예제 #18
0
        /// <summary>
        /// Only called from within the SimChat method.
        /// </summary>
        protected void commit(string message, Scene scene, CMModel model, CMView view)
        {
            System.Collections.Generic.List<Scene> proximitySceneList = ScenesInOrderOfProximity(m_sceneList, scene);

            string[] args = message.Split(new  char[] {' '});

            char[] logMessage = {' '};
            if (args.Length > 1)
            {
                logMessage = new char[message.Length - (args[0].Length)];
                message.CopyTo(args[0].Length, logMessage, 0, message.Length - (args[0].Length));
            }

            m_log.Debug("[CONTENT MANAGEMENT] Saving terrain and objects of region.");
            foreach (Scene currScene in proximitySceneList)
            {
                model.CommitRegion(currScene, new String(logMessage));
                view.SendSimChatMessage(scene, "Region Saved Successfully: " + currScene.RegionInfo.RegionName);
            }

            view.SendSimChatMessage(scene, "Successfully saved all regions.");
            m_state |= State.DIRTY;

            if ((m_state & State.SHOWING_CHANGES) > 0) //DISPLAY NEW CHANGES INSTEAD OF OLD CHANGES
            {
                view.SendSimChatMessage(scene, "Updating differences between new revision and current environment.");
                //Hide objects from users and Forget about them
                view.HideAllMetaEntities();
                view.HideAllAuras();
                model.DeleteAllMetaObjects();

                //Recreate them from backend files
                foreach (Scene currScene in proximitySceneList)
                {
                    model.UpdateCMEntities(currScene);
                    view.SendSimChatMessage(scene, "Finished updating differences between current scene and last revision: " + currScene.RegionInfo.RegionName);
                }

                //Display new objects to users1
                view.DisplayRecentChanges();
                view.SendSimChatMessage(scene, "Finished updating for DIFF-MODE.");
                m_state &= ~(State.DIRTY);
                m_state |= State.SHOWING_CHANGES;
            }
        }
예제 #19
0
 /// <summary>
 /// Only called by the MainLoop.
 /// </summary>
 private void ObjectKilled(CMModel model, CMView view, SceneObjectGroup group)
 {
     if ((m_state & State.SHOWING_CHANGES) > 0)
     {
         view.RemoveOrUpdateDeletedEntity(group);
         model.RemoveOrUpdateDeletedEntity(group);
     }
 }
예제 #20
0
 /// <summary>
 /// Only called by the MainLoop.
 /// </summary>
 private void PrimitiveAdded(CMModel model, CMView view, Work currentJob)
 {
     if ((m_state & State.SHOWING_CHANGES) > 0)
     {
         foreach (Object scene in m_sceneList.Values)
             m_view.DisplayAuras(model.CheckForNewEntitiesMissingAuras((Scene) scene));
     }
 }
예제 #21
0
 /// <summary>
 /// Only called by the MainLoop.
 /// </summary>
 private void ObjectAttributeChanged(CMModel model, CMView view, uint LocalId)
 {
     SceneObjectGroup group = null;
     if ((m_state & State.SHOWING_CHANGES) > 0)
     {
         group = GetGroupByPrim(LocalId);
         if (group != null)
         {
             view.DisplayAuras(model.UpdateNormalEntityEffects(group)); //Might be a normal entity (green aura)
             m_view.DisplayMetaEntity(group.UUID); //Might be a meta entity (blue aura)
         }
     }
 }
예제 #22
0
 /// <summary>
 /// Only called by the MainLoop. Displays new green auras over the newly created part when a part is shift copied.
 /// </summary>
 private void ObjectDuplicated(CMModel model, CMView view, uint localId)
 {
     if ((m_state & State.SHOWING_CHANGES) > 0)
         view.DisplayAuras(model.CheckForNewEntitiesMissingAuras(GetGroupByPrim(localId).Scene));
 }
예제 #23
0
 /// <summary>
 /// Initializes a work thread with an initial scene. Additional scenes should be added through the RegisterNewRegion method.
 /// </summary>
 /// <param name="model">
 ///  <see cref="CMModel"/>
 /// </param>
 /// <param name="view">
 /// <see cref="CMView"/>
 /// </param>
 /// <param name="scene">
 /// The first scene to keep track of. <see cref="Scene"/>
 /// </param>
 /// <param name="channel">
 /// The simchat channel number to listen to for instructions <see cref="System.Int32"/>
 /// </param>
 public CMController(CMModel model, CMView view, Scene scene, int channel)
 {
     m_model = model; m_view = view; m_channel = channel;
     RegisterNewRegion(scene);
     Initialize(model, view, scene, channel);
 }
예제 #24
0
 private void Initialize(CMModel model, CMView view, Scene scene, int channel)
 {
     lock (this)
     {
         m_estateModule = scene.RequestModuleInterface<IEstateModule>();
         m_thread = new Thread(MainLoop);
         m_thread.Name = "Content Management";
         m_thread.IsBackground = true;
         m_thread.Start();
         ThreadTracker.Add(m_thread);
         m_state = State.NONE;
     }
 }
예제 #25
0
 private void Initialize(CMModel model, CMView view, Scene scene, int channel)
 {
     lock (this)
     {
         m_estateModule = scene.RequestModuleInterface<IEstateModule>();
         Watchdog.StartThread(MainLoop, "Content Management", ThreadPriority.Normal, true);
         m_state = State.NONE;
     }
 }
예제 #26
0
 public void Initialise(CMModel model)
 {
     m_model = model;
 }
        public void Initialise(Scene scene, IConfigSource source)
        {
            string databaseDir = "./";
            string database = "FileSystemDatabase";
            int channel = 345;
            try
            {
                if (source.Configs["CMS"] == null)
                    return;

                m_enabled = source.Configs["CMS"].GetBoolean("enabled", false);
                databaseDir = source.Configs["CMS"].GetString("directory", databaseDir);
                database = source.Configs["CMS"].GetString("database", database);
                channel = source.Configs["CMS"].GetInt("channel", channel);

                if (database != "FileSystemDatabase" && database != "GitDatabase")
                {
                    m_log.ErrorFormat("[Content Management]: The Database attribute must be defined as either FileSystemDatabase or GitDatabase");
                    m_enabled = false;
                }
            }
            catch (Exception e)
            {
                m_log.ErrorFormat("[Content Management]: Exception thrown while reading parameters from configuration file. Message: " + e);
                m_enabled = false;
            }

            if (!m_enabled)
            {
                m_log.Info("[Content Management]: Content Management System is not Enabled.");
                return;
            }

            lock (this)
            {
                if (!initialised) //only init once
                {
                    m_view = new CMView();
                    m_model = new CMModel();
                    m_control = new CMController(m_model, m_view, scene, channel);
                    m_model.Initialise(database);
                    m_view.Initialise(m_model);

                    initialised = true;
                    m_model.InitialiseDatabase(scene, databaseDir);
                }
                else
                {
                    m_model.InitialiseDatabase(scene, databaseDir);
                    m_control.RegisterNewRegion(scene);
                }
            }
        }
예제 #28
0
 public void Initialise(CMModel model)
 {
     m_model = model;
 }
예제 #29
0
 /// <summary>
 /// Initializes a work thread with an initial scene. Additional scenes should be added through the RegisterNewRegion method.
 /// </summary>
 /// <param name="model">
 ///  <see cref="CMModel"/>
 /// </param>
 /// <param name="view">
 /// <see cref="CMView"/>
 /// </param>
 /// <param name="scene">
 /// The first scene to keep track of. <see cref="Scene"/>
 /// </param>
 /// <param name="channel">
 /// The simchat channel number to listen to for instructions <see cref="System.Int32"/>
 /// </param>
 public CMController(CMModel model, CMView view, Scene scene, int channel)
 {
     m_model = model; m_view = view; m_channel = channel;
     RegisterNewRegion(scene);
     Initialize(model, view, scene, channel);
 }