コード例 #1
0
        public static void Deserialize(ScriptData instance, ScriptEngine engine, StateSave save)
        {
            Dictionary<string, object> vars = save.Variables as Dictionary<string, object>;
            instance.State = save.State;
            instance.Running = save.Running;

            if (vars != null && vars.Count != 0)
                instance.Script.SetStoreVars(vars);

            if(save.Plugins is object[])
                instance.PluginData = (object[])save.Plugins;
            else
                instance.PluginData = new object[1] {(object)save.Plugins};

            if (save.Permissions != " " && save.Permissions != "")
            {
                instance.InventoryItem.PermsGranter = new UUID(save.Permissions.Split(',')[0]);
                instance.InventoryItem.PermsMask = int.Parse(save.Permissions.Split(',')[1], NumberStyles.Integer, Culture.NumberFormatInfo);
            }
            instance.EventDelayTicks = (long)save.MinEventDelay;
            instance.AssemblyName = save.AssemblyName;
            instance.Disabled = save.Disabled;
            instance.UserInventoryItemID = save.UserInventoryID;
            // Add it to our script memstruct
            ScriptEngine.ScriptProtection.AddNewScript(instance);
        }
コード例 #2
0
        public void SaveStateTo (ScriptData script)
        {
            StateSave stateSave = new StateSave ();
            stateSave.State = script.State;
            stateSave.ItemID = script.ItemID;
            stateSave.Running = script.Running;
            stateSave.MinEventDelay = script.EventDelayTicks;
            stateSave.Disabled = script.Disabled;
            stateSave.UserInventoryID = script.UserInventoryItemID;
            //Allow for the full path to be put down, not just the assembly name itself
            stateSave.AssemblyName = script.AssemblyName;
            stateSave.Source = script.Source;
            if (script.InventoryItem != null)
            {
                stateSave.PermsGranter = script.InventoryItem.PermsGranter;
                stateSave.PermsMask = script.InventoryItem.PermsMask;
            }
            else
            {
                stateSave.PermsGranter = UUID.Zero;
                stateSave.PermsMask = 0;
            }
            stateSave.TargetOmegaWasSet = script.TargetOmegaWasSet;

            //Vars
            Dictionary<string, Object> vars = new Dictionary<string, object> ();
            if (script.Script != null)
                vars = script.Script.GetStoreVars ();
            stateSave.Variables = WebUtils.BuildXmlResponse (vars);

            //Plugins
            stateSave.Plugins = m_module.GetSerializationData (script.ItemID, script.Part.UUID);

            CreateOSDMapForState (script, stateSave);
        }
コード例 #3
0
        public static void SaveState(ScriptData instance, ScriptEngine engine)
        {
            StateSave Insert = new StateSave();
            Insert.State = instance.State;
            Insert.ItemID = instance.ItemID;
            string source = instance.Source.Replace("\n", " ");
            Insert.Source = source.Replace("'", " ");
            Insert.Running = instance.Running;
            //Vars
            Dictionary<string, Object> vars = new Dictionary<string, object>();
            if (instance.Script != null)
                vars = instance.Script.GetVars();
            string varsmap = "";
            foreach (KeyValuePair<string, Object> var in vars)
            {
                varsmap += var.Key + "," + var.Value + "\n";
            }
            Insert.Variables = varsmap;
            //Plugins
            object[] Plugins = engine.GetSerializationData( instance.ItemID, instance.part.UUID);
            string plugins = "";
            foreach (object plugin in Plugins)
                plugins += plugin + ",";
            Insert.Plugins = plugins;

            //perms
            string perms = "";
            if (instance.InventoryItem != null)
            {
                if (instance.InventoryItem.PermsMask != 0 && instance.InventoryItem.PermsGranter != UUID.Zero)
                {
                    perms += instance.InventoryItem.PermsGranter.ToString() + "," + instance.InventoryItem.PermsMask.ToString();

                }
            }
            Insert.Permissions = perms;

            Insert.MinEventDelay = instance.EventDelayTicks;
            string[] AN = instance.AssemblyName.Split('\\');
            if(AN.Length > 2)
                Insert.AssemblyName = instance.AssemblyName.Split('\\')[2];
            else
            	Insert.AssemblyName = instance.AssemblyName;
            Insert.Disabled = instance.Disabled;
            Insert.UserInventoryID = instance.UserInventoryItemID;
            IScriptDataConnector ScriptFrontend = Aurora.DataManager.DataManager.RequestPlugin<IScriptDataConnector>();
            if(ScriptFrontend != null)
                ScriptFrontend.SaveStateSave(Insert);
        }
コード例 #4
0
        public void CacheStateSaves()
        {
            List<string> Query = GD.Query("", "", "auroradotnetstatesaves", "*");

            //Save this, as the query count changes over time
            int count = Query.Count;
            for (int i = 0; i < count; i += 11)
            {
                StateSave s = new StateSave();
                s = BuildStateSave(Query);
                m_cachedStateSaves[s.ItemID] = s;
                if(s.UserInventoryID != UUID.Zero)
                    m_cachedStateSaves[s.UserInventoryID] = s;
                Query.RemoveRange(0, 11);
            }
        }
コード例 #5
0
        /// <summary>
        /// Get the last state save the script has
        /// </summary>
        /// <param name="itemID"></param>
        /// <param name="UserInventoryItemID"></param>
        /// <param name="onlyCached)"></param>
        /// <returns></returns>
        public StateSave GetStateSave(UUID itemID, UUID UserInventoryItemID, bool onlyCached)
        {
            StateSave StateSave = new StateSave();
            //Check the caches
            if (m_cachedStateSaves.TryGetValue(itemID, out StateSave))
                return StateSave;
            else if (UserInventoryItemID != UUID.Zero &&
                m_cachedStateSaves.TryGetValue(UserInventoryItemID, out StateSave))
                return StateSave;
            else if (onlyCached)
                return null;
            else
                StateSave = new StateSave();


            try
            {
                List<string> StateSaveRetVals;

                //Use the UserInventoryItemID over the ItemID as the UserInventory is set when coming out of inventory and it overrides any other settings.
                if (UserInventoryItemID != UUID.Zero)
                    StateSaveRetVals = GD.Query("UserInventoryItemID", UserInventoryItemID.ToString(), "auroradotnetstatesaves", "*");
                else
                    StateSaveRetVals = GD.Query("ItemID", itemID.ToString(), "auroradotnetstatesaves", "*");
               
                if (StateSaveRetVals.Count == 0)
                    return null;

                StateSave = BuildStateSave(StateSaveRetVals);
                return StateSave;
            }
            catch
            {
                return null;
            }
		}
コード例 #6
0
        public void SaveStateTo(ScriptData script, bool forced, bool saveBackup)
        {
            if (!forced)
            {
                if (script.Script == null)
                    return; //If it didn't compile correctly, this happens
                if (!script.Script.NeedsStateSaved)
                    return; //If it doesn't need a state save, don't save one
            }
            if (script.Script != null)
                script.Script.NeedsStateSaved = false;
            if (saveBackup)
                script.Part.ParentEntity.HasGroupChanged = true;
            StateSave stateSave = new StateSave
                                      {
                                          State = script.State,
                                          ItemID = script.ItemID,
                                          Running = script.Running,
                                          MinEventDelay = script.EventDelayTicks,
                                          Disabled = script.Disabled,
                                          UserInventoryID = script.UserInventoryItemID,
                                          AssemblyName = script.AssemblyName,
                                          Compiled = script.Compiled,
                                          Source = script.Source
                                      };
            //Allow for the full path to be put down, not just the assembly name itself
            if (script.InventoryItem != null)
            {
                stateSave.PermsGranter = script.InventoryItem.PermsGranter;
                stateSave.PermsMask = script.InventoryItem.PermsMask;
            }
            else
            {
                stateSave.PermsGranter = UUID.Zero;
                stateSave.PermsMask = 0;
            }
            stateSave.TargetOmegaWasSet = script.TargetOmegaWasSet;

            //Vars
            Dictionary<string, Object> vars = new Dictionary<string, object>();
            if (script.Script != null)
                vars = script.Script.GetStoreVars();
            try
            {
                stateSave.Variables = WebUtils.BuildXmlResponse(vars);
            }
            catch
            {
            }

            //Plugins
            stateSave.Plugins = m_module.GetSerializationData(script.ItemID, script.Part.UUID);

            CreateOSDMapForState(script, stateSave);
        }
コード例 #7
0
        private void CreateOSDMapForState(ScriptData script, StateSave save)
        {
            //Get any previous state saves from the component manager
            OSDMap component = m_manager.GetComponentState(script.Part, m_componentName) as OSDMap;
            if (component == null)
                component = new OSDMap();

            //Add our state to the list of all scripts in this object
            component[script.ItemID.ToString()] = save.ToOSD();

            //Now resave it
            m_manager.SetComponentState(script.Part, m_componentName, component);
        }
コード例 #8
0
 public StateSave FindScriptStateSave(ScriptData script)
 {
     OSDMap component = m_manager.GetComponentState(script.Part, m_componentName) as OSDMap;
     //Attempt to find the state saves we have
     if (component != null)
     {
         OSD o;
         //If we have one for this item, deserialize it
         if (!component.TryGetValue(script.ItemID.ToString(), out o))
         {
             if (!component.TryGetValue(script.InventoryItem.OldItemID.ToString(), out o))
             {
                 if (!component.TryGetValue(script.InventoryItem.ItemID.ToString(), out o))
                 {
                     return null;
                 }
             }
         }
         StateSave save = new StateSave();
         save.FromOSD((OSDMap)o);
         return save;
     }
     return null;
 }
コード例 #9
0
        public void Deserialize(ScriptData instance, StateSave save)
        {
            instance.State = save.State;
            instance.Running = save.Running;
            instance.EventDelayTicks = (long)save.MinEventDelay;
            instance.AssemblyName = save.AssemblyName;
            instance.Disabled = save.Disabled;
            instance.UserInventoryItemID = save.UserInventoryID;
            instance.PluginData = save.Plugins;
            m_module.CreateFromData(instance.Part.UUID, instance.ItemID, instance.Part.UUID,
                                    instance.PluginData);
            instance.Source = save.Source;
            instance.InventoryItem.PermsGranter = save.PermsGranter;
            instance.InventoryItem.PermsMask = save.PermsMask;
            instance.TargetOmegaWasSet = save.TargetOmegaWasSet;

            try
            {
                Dictionary<string, object> vars = WebUtils.ParseXmlResponse(save.Variables);
                if (vars != null && vars.Count != 0 || instance.Script != null)
                    instance.Script.SetStoreVars(vars);
            }
            catch
            {
            }
        }
コード例 #10
0
        /// <summary>
        /// Save the current script state.
        /// </summary>
        /// <param name="state"></param>
		public void SaveStateSave(StateSave state)
        {
            m_cachedStateSaves[state.ItemID] = state;
            if(state.UserInventoryID != UUID.Zero) //Too many scripts have this and is dangerous to pull from this
                m_cachedStateSaves[state.UserInventoryID] = state;
            List<string> Keys = new List<string>();
            Keys.Add("State");
            Keys.Add("ItemID");
            Keys.Add("Source");
            Keys.Add("Running");
            Keys.Add("Variables");
            Keys.Add("Plugins");
            Keys.Add("Permissions");
            Keys.Add("MinEventDelay");
            Keys.Add("AssemblyName");
            Keys.Add("Disabled");
            Keys.Add("UserInventoryItemID");

            List<object> Insert = new List<object>();
            Insert.Add(state.State);
            Insert.Add(state.ItemID);
            Insert.Add(state.Source);
            Insert.Add(state.Running ? 1 : 0);
            Insert.Add(state.Variables);
            Insert.Add(state.Plugins);
            Insert.Add(state.Permissions);
            Insert.Add(state.MinEventDelay);
            Insert.Add(state.AssemblyName);
            Insert.Add(state.Disabled ? 1 : 0);
            Insert.Add(state.UserInventoryID);
            GD.Replace("auroradotnetstatesaves", Keys.ToArray(), Insert.ToArray());
		}
コード例 #11
0
        private StateSave BuildStateSave(List<string> StateSaveRetVals)
        {
            StateSave StateSave = new StateSave();
            Dictionary<string, object> vars = new Dictionary<string, object>();
            StateSave.State = StateSaveRetVals[0];
            StateSave.ItemID = new UUID(StateSaveRetVals[1]);
            StateSave.Source = StateSaveRetVals[2];
            StateSave.Running = int.Parse(StateSaveRetVals[3]) == 1;

            string varsmap = StateSaveRetVals[4];
            if (varsmap != " " && varsmap != "")
            {
                varsmap = varsmap.Replace('\n', ';');
                foreach (string var in varsmap.Split(';'))
                {
                    if (var == "")
                        continue;
                    string[] values = var.Split(',');
                    string value = "";
                    int i = 0;
                    foreach (string val in values)
                    {
                        if (i != 0)
                        {
                            value += val + ",";
                        }
                        i++;
                    }
                    if (value == "")
                        continue;
                    value = value.Remove(value.Length - 1, 1);
                    vars[var.Split(',')[0]] = (object)value;
                }
            }
            StateSave.Variables = vars;

            List<object> plugins = new List<object>();
            object[] pluginsSaved = StateSaveRetVals[5].Split(',');
            if (pluginsSaved.Length != 1)
            {
                foreach (object plugin in pluginsSaved)
                {
                    if (plugin == null)
                        continue;
                    plugins.Add(plugin);
                }
            }
            StateSave.Plugins = plugins.ToArray();
            StateSave.Permissions = StateSaveRetVals[6];
            double.TryParse(StateSaveRetVals[7], NumberStyles.Float, Culture.NumberFormatInfo, out StateSave.MinEventDelay);
            StateSave.AssemblyName = StateSaveRetVals[8];
            StateSave.Disabled = int.Parse(StateSaveRetVals[9]) == 1;
            StateSave.UserInventoryID = UUID.Parse(StateSaveRetVals[10]);

            return StateSave;
        }
コード例 #12
0
 public override IDataTransferable Duplicate ()
 {
     StateSave m = new StateSave ();
     m.FromOSD (ToOSD ());
     return m;
 }
コード例 #13
0
		public StateSave GetStateSave(UUID itemID, UUID UserInventoryItemID)
		{
            try
            {
                StateSave StateSave = new StateSave();
                List<string> StateSaveRetVals = new List<string>();
                if (UserInventoryItemID != UUID.Zero)
                {
                    StateSaveRetVals = GD.Query("UserInventoryItemID", UserInventoryItemID.ToString(), "auroradotnetstatesaves", "*");
                }
                else
                {
                    StateSaveRetVals = GD.Query("ItemID", itemID.ToString(), "auroradotnetstatesaves", "*");
                }
                if (StateSaveRetVals.Count == 0)
                    return null;
                Dictionary<string, object> vars = new Dictionary<string, object>();
                StateSave.State = StateSaveRetVals[0];
                StateSave.ItemID = new UUID(StateSaveRetVals[1]);
                StateSave.Source = StateSaveRetVals[2];
                StateSave.Running = int.Parse(StateSaveRetVals[3]) == 1;

                string varsmap = StateSaveRetVals[4];
                if (varsmap != " " && varsmap != "")
                {
                    varsmap = varsmap.Replace('\n', ';');
                    foreach (string var in varsmap.Split(';'))
                    {
                        if (var == "")
                            continue;
                        string[] values = var.Split(',');
                        string value = "";
                        int i = 0;
                        foreach (string val in values)
                        {
                            if (i != 0)
                            {
                                value += val + ",";
                            }
                            i++;
                        }
                        value = value.Remove(value.Length - 1, 1);
                        vars.Add(var.Split(',')[0], (object)value);
                    }
                }
                StateSave.Variables = vars;

                List<object> plugins = new List<object>();
                object[] pluginsSaved = StateSaveRetVals[5].Split(',');
                if (pluginsSaved.Length != 1)
                {
                    foreach (object plugin in pluginsSaved)
                    {
                        if (plugin == null)
                            continue;
                        plugins.Add(plugin);
                    }
                }
                StateSave.Plugins = plugins.ToArray();
                StateSave.Permissions = StateSaveRetVals[6];
                double minEventDelay = 0.0;
                double.TryParse(StateSaveRetVals[7], NumberStyles.Float, Culture.NumberFormatInfo, out minEventDelay);
                StateSave.MinEventDelay = (long)minEventDelay;
                StateSave.AssemblyName = StateSaveRetVals[8];
                StateSave.Disabled = int.Parse(StateSaveRetVals[9]) == 1;
                StateSave.UserInventoryID = UUID.Parse(StateSaveRetVals[10]);

                return StateSave;
            }
            catch
            {
                return null;
            }
		}
コード例 #14
0
		public void SaveStateSave(StateSave state)
		{
            List<string> Keys = new List<string>();
            Keys.Add("State");
            Keys.Add("ItemID");
            Keys.Add("Source");
            Keys.Add("Running");
            Keys.Add("Variables");
            Keys.Add("Plugins");
            Keys.Add("Permissions");
            Keys.Add("MinEventDelay");
            Keys.Add("AssemblyName");
            Keys.Add("Disabled");
            Keys.Add("UserInventoryItemID");

            List<object> Insert = new List<object>();
            Insert.Add(state.State);
            Insert.Add(state.ItemID);
            Insert.Add(state.Source);
            Insert.Add(state.Running ? 1 : 0);
            Insert.Add(state.Variables);
            Insert.Add(state.Plugins);
            Insert.Add(state.Permissions);
            Insert.Add(state.MinEventDelay);
            Insert.Add(state.AssemblyName);
            Insert.Add(state.Disabled ? 1 : 0);
            Insert.Add(state.UserInventoryID);
            GD.Replace("auroradotnetstatesaves", Keys.ToArray(), Insert.ToArray());
		}
コード例 #15
0
ファイル: ScriptData.cs プロジェクト: shangcheng/Aurora
        /// <summary>
        /// This starts the script and sets up the variables.
        /// </summary>
        /// <returns></returns>
        public void Start(bool reupload)
        {
            DateTime StartTime = DateTime.Now.ToUniversalTime();

            //Clear out the removing of events for this script.
            VersionID++;

            //Reset this
            StartedFromSavedState = false;

            m_ScriptEngine.ScriptErrorReporter.RemoveError(ItemID);

            //Find the inventory item
            part.TaskInventory.TryGetValue(ItemID, out InventoryItem);

            //Try to see if this was rezzed from someone's inventory
            UserInventoryItemID = part.FromUserInventoryItemID;

            //Try to find the avatar who started this.
            presence = World.GetScenePresence(part.OwnerID);

            #region HTML Reader

            if (ScriptEngine.ScriptProtection.AllowHTMLLinking)
            {
                //Read the URL and load it.
                if (Source.Contains("#IncludeHTML "))
                {
                    string URL = "";
                    int line = Source.IndexOf("#IncludeHTML ");
                    URL = Source.Remove(0, line);
                    URL = URL.Replace("#IncludeHTML ", "");
                    URL = URL.Split('\n')[0];
                    string webSite = ReadExternalWebsite(URL);
                    Source = Source.Replace("#IncludeHTML " + URL, webSite);
                }
            }
            else
            {
                //Remove the line then
                if (Source.Contains("#IncludeHTML "))
                {
                    string URL = "";
                    int line = Source.IndexOf("#IncludeHTML ");
                    URL = Source.Remove(0, line);
                    URL = URL.Replace("#IncludeHTML ", "");
                    URL = URL.Split('\n')[0];
                    Source = Source.Replace("#IncludeHTML " + URL, "");
                }
            }

            #endregion

            // Attempt to find a state save
            if(ScriptFrontend != null)
                LastStateSave = ScriptFrontend.GetStateSave(ItemID, UserInventoryItemID);

            if (!reupload && Loading && LastStateSave != null
                && File.Exists(Path.Combine(m_ScriptEngine.ScriptEnginesPath, Path.Combine(
                    "Script",
                    LastStateSave.AssemblyName))))
            {
                //Retrive the previous assembly
                AssemblyName = Path.Combine(m_ScriptEngine.ScriptEnginesPath, Path.Combine(
                    "Script",
                    LastStateSave.AssemblyName));
            }
            else
            {
                LastStateSave = null;
                if (reupload)
                {
                    //Close the previous script
                    CloseAndDispose(true);

                    VersionID++;
                }

                //Try to find a previously compiled script in this instance
                string PreviouslyCompiledAssemblyName = ScriptEngine.ScriptProtection.TryGetPreviouslyCompiledScript(Source);
                if (PreviouslyCompiledAssemblyName != null)
                    AssemblyName = PreviouslyCompiledAssemblyName;
                else
                {
                    try
                    {
                        m_ScriptEngine.Compiler.PerformScriptCompile(Source, ItemID, part.OwnerID, VersionID, out AssemblyName);
                        #region Errors and Warnings

                        #region Errors

                        string[] compileerrors = m_ScriptEngine.Compiler.GetErrors();

                        if (compileerrors != null && compileerrors.Length != 0)
                        {
                            string error = string.Empty;
                            foreach(string compileerror in compileerrors)
                            {
                                error += compileerror;
                            }
                            DisplayUserNotification(error, "compiling", reupload, true);
                            return;
                        }

                        #endregion

                        #region Warnings

                        if (m_ScriptEngine.ShowWarnings)
                        {
                            string[] compilewarnings = m_ScriptEngine.Compiler.GetWarnings();

                            if (compilewarnings != null && compilewarnings.Length != 0)
                            {
                                string error = string.Empty;
                                foreach(string compileerror in compileerrors)
                                {
                                    error += compileerror;
                                }
                                DisplayUserNotification(error, "compiling", reupload, false);
                                return;
                            }
                        }

                        #endregion

                        #endregion
                    }
                    catch (Exception ex)
                    {
                        DisplayUserNotification(ex.Message, "compiling", reupload, true);
                        return;
                    }
                }
            }

            bool useDebug = false;
            if (useDebug)
                m_log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: Stage 1 compile: " + (DateTime.Now.ToUniversalTime() - StartTime).TotalSeconds);

            //Create the app domain if needed.
            try
            {
                Script = m_ScriptEngine.AppDomainManager.LoadScript(AssemblyName, "Script.ScriptClass", out AppDomain);
                ScriptEngine.ScriptProtection.AddPreviouslyCompiled(Source, this);
            }
            catch (System.IO.FileNotFoundException) // Not valid!!!
            {
                m_log.Error("[" + m_ScriptEngine.ScriptEngineName + "]: File not found in app domain creation. Corrupt state save! " + AssemblyName);
                ScriptEngine.ScriptProtection.RemovePreviouslyCompiled(Source);
                ScriptFrontend.DeleteStateSave(AssemblyName);
                Start(reupload); // Lets restart the script if this happens
                return;
            }
            catch (Exception ex)
            {
                DisplayUserNotification(ex.Message, "app domain creation", reupload, true);
                return;
            }

            ILease lease = (ILease)RemotingServices.GetLifetimeService(Script as MarshalByRefObject);
            if (lease != null)
                lease.Register(Script.Sponsor);

            //If its a reupload, an avatar is waiting for the script errors
            if (reupload)
                m_ScriptEngine.ScriptErrorReporter.AddError(ItemID, new ArrayList(new string[] { "SUCCESSFULL" }));

            if (useDebug)
                m_log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: Stage 2 compile: " + (DateTime.Now.ToUniversalTime() - StartTime).TotalSeconds);

            SetApis();

            //Set the event flags
            part.SetScriptEvents(ItemID, Script.GetStateEventFlags(State));

            //Now do the full state save finding now that we have an app domain.
            if (LastStateSave != null)
            {
                ScriptDataSQLSerializer.Deserialize(this, m_ScriptEngine, LastStateSave);

                m_ScriptEngine.CreateFromData(part.UUID, ItemID, part.UUID,
                    PluginData);

                // we get new rez events on sim restart, too
                // but if there is state, then we fire the change
                // event
                StartedFromSavedState = true;
            }
            else
            {

                //Make a new state save now
                m_ScriptEngine.MaintenanceThread.AddToStateSaverQueue(this, true);
            }

            // Add it to our script memstruct so it can be found by other scripts
            ScriptEngine.ScriptProtection.AddNewScript(this);

            //All done, compiled successfully
            Loading = false;

            TimeSpan time = (DateTime.Now.ToUniversalTime() - StartTime);

            if (presence != null)
                MainConsole.Instance.Output("[" + m_ScriptEngine.ScriptEngineName +
                    "]: Started Script " + InventoryItem.Name +
                    " in object " + part.Name +
                    " by " + presence.Name +
                    " in region " + part.ParentGroup.Scene.RegionInfo.RegionName +
                    " in " + time.TotalSeconds + " seconds.", "None");
            else
                MainConsole.Instance.Output("[" + m_ScriptEngine.ScriptEngineName +
                    "]: Started Script " + InventoryItem.Name +
                    " in object " + part.Name +
                    " in region " + part.ParentGroup.Scene.RegionInfo.RegionName +
                    " in " + time.TotalSeconds + " seconds.", "None");
        }