aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Application/OpenSimMainConsole.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Application/OpenSimMainConsole.cs716
1 files changed, 716 insertions, 0 deletions
diff --git a/OpenSim/Region/Application/OpenSimMainConsole.cs b/OpenSim/Region/Application/OpenSimMainConsole.cs
new file mode 100644
index 0000000..53e3583
--- /dev/null
+++ b/OpenSim/Region/Application/OpenSimMainConsole.cs
@@ -0,0 +1,716 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Diagnostics;
31using System.IO;
32using System.Text;
33using System.Threading;
34using System.Timers;
35using Nini.Config;
36using OpenSim.Framework;
37using OpenSim.Framework.Communications.Cache;
38using OpenSim.Framework.Console;
39using OpenSim.Framework.Servers;
40using OpenSim.Framework.Statistics;
41using OpenSim.Region.ClientStack;
42using OpenSim.Region.Communications.Local;
43using OpenSim.Region.Communications.OGS1;
44using OpenSim.Region.Environment;
45using OpenSim.Region.Environment.Interfaces;
46using OpenSim.Region.Environment.Scenes;
47using OpenSim.Region.Physics.Manager;
48using Timer=System.Timers.Timer;
49using System.Net;
50using Nwc.XmlRpc;
51using System.Collections;
52using System.Reflection;
53using libsecondlife;
54using Mono.Addins;
55using Mono.Addins.Description;
56
57namespace OpenSim
58{
59 public delegate void ConsoleCommand(string[] comParams);
60
61 public class OpenSimMainConsole : OpenSimMain, conscmd_callback
62 {
63 private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
64
65 protected string m_startupCommandsFile;
66 protected string m_shutdownCommandsFile;
67
68 private string m_timedScript = "disabled";
69 private Timer m_scriptTimer;
70
71
72 public OpenSimMainConsole(IConfigSource configSource)
73 : base(configSource)
74 {
75 }
76
77 protected override void ReadConfigSettings()
78 {
79 IConfig startupConfig = m_config.Configs["Startup"];
80
81 if (startupConfig != null)
82 {
83 m_startupCommandsFile = startupConfig.GetString("startup_console_commands_file", String.Empty);
84 m_shutdownCommandsFile = startupConfig.GetString("shutdown_console_commands_file", String.Empty);
85
86 m_timedScript = startupConfig.GetString("timer_Script", "disabled");
87 }
88 base.ReadConfigSettings();
89 }
90
91 /// <summary>
92 /// Performs initialisation of the scene, such as loading configuration from disk.
93 /// </summary>
94 public override void StartUp()
95 {
96 //
97 // Called from app startup (OpenSim.Application)
98 //
99
100 m_log.Info("====================================================================");
101 m_log.Info("========================= STARTING OPENSIM =========================");
102 m_log.Info("====================================================================");
103 m_log.InfoFormat("[OPENSIM MAIN]: Running in {0} mode", (m_sandbox ? "sandbox" : "grid"));
104
105 m_console = CreateConsole();
106 MainConsole.Instance = m_console;
107 InternalStartUp();
108
109 //Run Startup Commands
110 if (m_startupCommandsFile != String.Empty)
111 {
112 RunCommandScript(m_startupCommandsFile);
113 }
114 else
115 {
116 m_log.Info("[STARTUP]: No startup command script specified. Moving on...");
117 }
118
119 // Start timer script (run a script every xx seconds)
120 if (m_timedScript != "disabled")
121 {
122 m_scriptTimer = new Timer();
123 m_scriptTimer.Enabled = true;
124 m_scriptTimer.Interval = (int)(1200 * 1000);
125 m_scriptTimer.Elapsed += new ElapsedEventHandler(RunAutoTimerScript);
126 }
127 PrintFileToConsole("startuplogo.txt");
128 }
129
130 protected ConsoleBase CreateConsole()
131 {
132 return new ConsoleBase("Region", this);
133 }
134
135 /// <summary>
136 /// Performs any last-minute sanity checking and shuts down the region server
137 /// </summary>
138 public override void Shutdown()
139 {
140 if (m_startupCommandsFile != String.Empty)
141 {
142 RunCommandScript(m_shutdownCommandsFile);
143 }
144 InternalShutdown();
145 m_console.Close();
146 Environment.Exit(0);
147 }
148
149 private void RunAutoTimerScript(object sender, EventArgs e)
150 {
151 if (m_timedScript != "disabled")
152 {
153 RunCommandScript(m_timedScript);
154 }
155 }
156
157 #region Console Commands
158
159 /// <summary>
160 ///
161 /// </summary>
162 /// <param name="fileName"></param>
163 private void RunCommandScript(string fileName)
164 {
165 m_log.Info("[COMMANDFILE]: Running " + fileName);
166 if (File.Exists(fileName))
167 {
168 StreamReader readFile = File.OpenText(fileName);
169 string currentCommand = String.Empty;
170 while ((currentCommand = readFile.ReadLine()) != null)
171 {
172 if (currentCommand != String.Empty)
173 {
174 m_log.Info("[COMMANDFILE]: Running '" + currentCommand + "'");
175 m_console.RunCommand(currentCommand);
176 }
177 }
178 }
179 else
180 {
181 m_log.Error("[COMMANDFILE]: Command script missing. Can not run commands");
182 }
183 }
184
185 private void PrintFileToConsole(string fileName)
186 {
187 if (File.Exists(fileName))
188 {
189 StreamReader readFile = File.OpenText(fileName);
190 string currentLine = String.Empty;
191 while ((currentLine = readFile.ReadLine()) != null)
192 {
193 m_log.Info("[!]" + currentLine);
194 }
195 }
196 }
197
198
199 /// <summary>
200 /// Runs commands issued by the server console from the operator
201 /// </summary>
202 /// <param name="command">The first argument of the parameter (the command)</param>
203 /// <param name="cmdparams">Additional arguments passed to the command</param>
204 public override void RunCmd(string command, string[] cmdparams)
205 {
206 base.RunCmd(command, cmdparams);
207
208 switch (command)
209 {
210 case "clear-assets":
211 m_assetCache.Clear();
212 break;
213
214 case "set-time":
215 m_sceneManager.SetCurrentSceneTimePhase(Convert.ToInt32(cmdparams[0]));
216 break;
217
218 case "force-update":
219 m_console.Notice("Updating all clients");
220 m_sceneManager.ForceCurrentSceneClientUpdate();
221 break;
222
223 case "edit-scale":
224 if (cmdparams.Length == 4)
225 {
226 m_sceneManager.HandleEditCommandOnCurrentScene(cmdparams);
227 }
228 break;
229
230 case "debug":
231 if (cmdparams.Length > 0)
232 {
233 Debug(cmdparams);
234 }
235 break;
236
237 case "scene-debug":
238 if (cmdparams.Length == 3)
239 {
240 if (m_sceneManager.CurrentScene == null)
241 {
242 m_console.Error("CONSOLE", "Please use 'change-region <regioname>' first");
243 }
244 else
245 {
246 m_sceneManager.CurrentScene.SetSceneCoreDebug(!System.Convert.ToBoolean(cmdparams[0]), !System.Convert.ToBoolean(cmdparams[1]), !System.Convert.ToBoolean(cmdparams[2]));
247 }
248 }
249 else
250 {
251 m_console.Error("scene-debug <scripting> <collisions> <physics> (where inside <> is true/false)");
252 }
253 break;
254
255 case "help":
256 m_console.Notice("alert - send alert to a designated user or all users.");
257 m_console.Notice(" alert [First] [Last] [Message] - send an alert to a user. Case sensitive.");
258 m_console.Notice(" alert general [Message] - send an alert to all users.");
259 m_console.Notice("backup - trigger a simulator backup");
260 m_console.Notice("clear-assets - clear asset cache");
261 m_console.Notice("create-region <name> <regionfile.xml> - creates a new region");
262 m_console.Notice("create user - adds a new user.");
263 m_console.Notice("change-region [name] - sets the region that many of these commands affect.");
264 m_console.Notice("command-script [filename] - Execute command in a file.");
265 m_console.Notice("debug - debugging commands");
266 m_console.Notice(" packet 0..255 - print incoming/outgoing packets (0=off)");
267 m_console.Notice("scene-debug [scripting] [collision] [physics] - Enable/Disable debug stuff, each can be True/False");
268 m_console.Notice("edit-scale [prim name] [x] [y] [z] - resize given prim");
269 m_console.Notice("export-map [filename] - save image of world map");
270 m_console.Notice("force-update - force an update of prims in the scene");
271 m_console.Notice("load-xml [filename] - load prims from XML");
272 m_console.Notice("load-xml2 [filename] - load prims from XML using version 2 format");
273 m_console.Notice("permissions [true/false] - turn on/off permissions on the scene");
274 m_console.Notice("quit - equivalent to shutdown.");
275 m_console.Notice("restart - disconnects all clients and restarts the sims in the instance.");
276 m_console.Notice("remove-region [name] - remove a region");
277 m_console.Notice("save-xml [filename] - save prims to XML");
278 m_console.Notice("save-xml2 [filename] - save prims to XML using version 2 format");
279 m_console.Notice("script - manually trigger scripts? or script commands?");
280 m_console.Notice("set-time [x] - set the current scene time phase");
281 m_console.Notice("show assets - show state of asset cache.");
282 m_console.Notice("show users - show info about connected users.");
283 m_console.Notice("show modules - shows info about loaded modules.");
284 m_console.Notice("show stats - statistical information for this server not displayed in the client");
285 m_console.Notice("threads - list threads");
286 m_console.Notice("shutdown - disconnect all clients and shutdown.");
287 m_console.Notice("config set section field value - set a config value");
288 m_console.Notice("config get section field - get a config value");
289 m_console.Notice("config save - save OpenSim.ini");
290 m_console.Notice("terrain help - show help for terrain commands.");
291 break;
292
293 case "threads":
294// m_console.Notice("THREAD", Process.GetCurrentProcess().Threads.Count + " threads running:");
295// int _tc = 0;
296
297// foreach (ProcessThread pt in Process.GetCurrentProcess().Threads)
298// {
299// _tc++;
300// m_console.Notice("THREAD", _tc + ": ID: " + pt.Id + ", Started: " + pt.StartTime.ToString() + ", CPU time: " + pt.TotalProcessorTime + ", Pri: " + pt.BasePriority.ToString() + ", State: " + pt.ThreadState.ToString());
301// }
302
303 List<Thread> threads = OpenSim.Framework.ThreadTracker.GetThreads();
304 if (threads == null)
305 {
306 m_console.Notice("THREAD", "Thread tracking is only enabled in DEBUG mode.");
307 }
308 else
309 {
310 int _tc = 0;
311 m_console.Notice("THREAD", threads.Count + " threads are being tracked:");
312 foreach (Thread t in threads)
313 {
314 _tc++;
315 m_console.Notice("THREAD", _tc + ": ID: " + t.ManagedThreadId.ToString() + ", Name: " + t.Name + ", Alive: " + t.IsAlive.ToString() + ", Pri: " + t.Priority.ToString() + ", State: " + t.ThreadState.ToString());
316 }
317 }
318
319 break;
320 case "save-xml":
321 if (cmdparams.Length > 0)
322 {
323 m_sceneManager.SaveCurrentSceneToXml(cmdparams[0]);
324 }
325 else
326 {
327 m_sceneManager.SaveCurrentSceneToXml(DEFAULT_PRIM_BACKUP_FILENAME);
328 }
329 break;
330
331 case "load-xml":
332 LLVector3 loadOffset = new LLVector3(0, 0, 0);
333 if (cmdparams.Length > 0)
334 {
335 bool generateNewIDS = false;
336 if (cmdparams.Length > 1)
337 {
338 if (cmdparams[1] == "-newUID")
339 {
340 generateNewIDS = true;
341 }
342 if (cmdparams.Length > 2)
343 {
344 loadOffset.X = (float) Convert.ToDecimal(cmdparams[2]);
345 if (cmdparams.Length > 3)
346 {
347 loadOffset.Y = (float) Convert.ToDecimal(cmdparams[3]);
348 }
349 if (cmdparams.Length > 4)
350 {
351 loadOffset.Z = (float) Convert.ToDecimal(cmdparams[4]);
352 }
353 m_console.Error("loadOffsets <X,Y,Z> = <" + loadOffset.X + "," + loadOffset.Y + "," +
354 loadOffset.Z + ">");
355 }
356 }
357 m_sceneManager.LoadCurrentSceneFromXml(cmdparams[0], generateNewIDS, loadOffset);
358 }
359 else
360 {
361 m_sceneManager.LoadCurrentSceneFromXml(DEFAULT_PRIM_BACKUP_FILENAME, false, loadOffset);
362 }
363 break;
364
365 case "save-xml2":
366 if (cmdparams.Length > 0)
367 {
368 m_sceneManager.SaveCurrentSceneToXml2(cmdparams[0]);
369 }
370 else
371 {
372 m_sceneManager.SaveCurrentSceneToXml2(DEFAULT_PRIM_BACKUP_FILENAME);
373 }
374 break;
375
376 case "load-xml2":
377 if (cmdparams.Length > 0)
378 {
379 m_sceneManager.LoadCurrentSceneFromXml2(cmdparams[0]);
380 }
381 else
382 {
383 m_sceneManager.LoadCurrentSceneFromXml2(DEFAULT_PRIM_BACKUP_FILENAME);
384 }
385 break;
386
387 case "plugin":
388 m_sceneManager.SendCommandToPluginModules(cmdparams);
389 break;
390
391 case "command-script":
392 if (cmdparams.Length > 0)
393 {
394 RunCommandScript(cmdparams[0]);
395 }
396 break;
397
398 case "permissions":
399 // Treats each user as a super-admin when disabled
400 bool permissions = Convert.ToBoolean(cmdparams[0]);
401 m_sceneManager.SetBypassPermissionsOnCurrentScene(!permissions);
402 break;
403
404 case "backup":
405 m_sceneManager.BackupCurrentScene();
406 break;
407
408 case "alert":
409 m_sceneManager.HandleAlertCommandOnCurrentScene(cmdparams);
410 break;
411
412 case "create":
413 CreateAccount(cmdparams);
414 break;
415
416 case "create-region":
417 CreateRegion(new RegionInfo(cmdparams[0], "Regions/" + cmdparams[1],false), true);
418 break;
419 case "remove-region":
420 string regName = CombineParams(cmdparams, 0);
421
422 Scene killScene;
423 if (m_sceneManager.TryGetScene(regName, out killScene))
424 {
425 // only need to check this if we are not at the
426 // root level
427 if ((m_sceneManager.CurrentScene != null) &&
428 (m_sceneManager.CurrentScene.RegionInfo.RegionID == killScene.RegionInfo.RegionID))
429 {
430 m_sceneManager.TrySetCurrentScene("..");
431 }
432 m_regionData.Remove(killScene.RegionInfo);
433 m_sceneManager.CloseScene(killScene);
434 }
435 break;
436
437 case "exit":
438 case "quit":
439 case "shutdown":
440 Shutdown();
441 break;
442
443 case "restart":
444 m_sceneManager.RestartCurrentScene();
445 break;
446
447 case "change-region":
448 if (cmdparams.Length > 0)
449 {
450 string regionName = CombineParams(cmdparams, 0);
451
452 if (!m_sceneManager.TrySetCurrentScene(regionName))
453 {
454 m_console.Error("Couldn't set current region to: " + regionName);
455 }
456 }
457
458 if (m_sceneManager.CurrentScene == null)
459 {
460 m_console.Error("CONSOLE", "Currently at Root level. To change region please use 'change-region <regioname>'");
461 }
462 else
463 {
464 m_console.Error("CONSOLE", "Current Region: " + m_sceneManager.CurrentScene.RegionInfo.RegionName +
465 ". To change region please use 'change-region <regioname>'");
466 }
467
468 break;
469
470 case "export-map":
471 if (cmdparams.Length > 0)
472 {
473 m_sceneManager.CurrentOrFirstScene.ExportWorldMap(cmdparams[0]);
474 }
475 else
476 {
477 m_sceneManager.CurrentOrFirstScene.ExportWorldMap("exportmap.jpg");
478 }
479 break;
480
481 case "config":
482 string n = command.ToUpper();
483 if (cmdparams.Length > 0)
484 {
485 switch (cmdparams[0].ToLower())
486 {
487 case "set":
488 if (cmdparams.Length < 4)
489 {
490 m_console.Error(n, "SYNTAX: " + n + " SET SECTION KEY VALUE");
491 m_console.Error(n, "EXAMPLE: " + n + " SET ScriptEngine.DotNetEngine NumberOfScriptThreads 5");
492 }
493 else
494 {
495 IConfig c = DefaultConfig().Configs[cmdparams[1]];
496 if (c == null)
497 c = DefaultConfig().AddConfig(cmdparams[1]);
498 string _value = String.Join(" ", cmdparams, 3, cmdparams.Length - 3);
499 c.Set(cmdparams[2], _value);
500 m_config.Merge(c.ConfigSource);
501
502 m_console.Error(n, n + " " + n + " " + cmdparams[1] + " " + cmdparams[2] + " " +
503 _value);
504 }
505 break;
506 case "get":
507 if (cmdparams.Length < 3)
508 {
509 m_console.Error(n, "SYNTAX: " + n + " GET SECTION KEY");
510 m_console.Error(n, "EXAMPLE: " + n + " GET ScriptEngine.DotNetEngine NumberOfScriptThreads");
511 }
512 else
513 {
514 IConfig c = DefaultConfig().Configs[cmdparams[1]];
515 if (c == null)
516 {
517 m_console.Notice(n, "Section \"" + cmdparams[1] + "\" does not exist.");
518 break;
519 }
520 else
521 {
522 m_console.Notice(n + " GET " + cmdparams[1] + " " + cmdparams[2] + ": " +
523 c.GetString(cmdparams[2]));
524 }
525 }
526
527 break;
528 case "save":
529 m_console.Notice("Saving configuration file: " + Application.iniFilePath);
530 m_config.Save(Application.iniFilePath);
531 break;
532 }
533 }
534 break;
535 case "modules":
536 if (cmdparams.Length > 0)
537 {
538 switch (cmdparams[0].ToLower())
539 {
540 case "list":
541 foreach (IRegionModule irm in m_moduleLoader.GetLoadedSharedModules)
542 {
543 m_console.Notice("Shared region module: " + irm.Name);
544 }
545 break;
546 case "unload":
547 if (cmdparams.Length > 1)
548 {
549 foreach (IRegionModule rm in new System.Collections.ArrayList(m_moduleLoader.GetLoadedSharedModules))
550 {
551 if (rm.Name.ToLower() == cmdparams[1].ToLower())
552 {
553 m_console.Notice("Unloading module: " + rm.Name);
554 m_moduleLoader.UnloadModule(rm);
555 }
556 }
557 }
558 break;
559 case "load":
560 if (cmdparams.Length > 1)
561 {
562 foreach (Scene s in new System.Collections.ArrayList(m_sceneManager.Scenes))
563 {
564
565 m_console.Notice("Loading module: " + cmdparams[1]);
566 m_moduleLoader.LoadRegionModules(cmdparams[1], s);
567 }
568 }
569 break;
570 }
571 }
572
573 break;
574
575 default:
576 string[] tmpPluginArgs = new string[cmdparams.Length + 1];
577 cmdparams.CopyTo(tmpPluginArgs, 1);
578 tmpPluginArgs[0] = command;
579
580 m_sceneManager.SendCommandToPluginModules(tmpPluginArgs);
581 break;
582 }
583 }
584
585 public void Debug(string[] args)
586 {
587 switch (args[0])
588 {
589 case "packet":
590 if (args.Length > 1)
591 {
592 int newDebug;
593 if (int.TryParse(args[1], out newDebug))
594 {
595 m_sceneManager.SetDebugPacketOnCurrentScene(newDebug);
596 }
597 else
598 {
599 m_console.Error("packet debug should be 0..2");
600 }
601 m_console.Notice("New packet debug: " + newDebug.ToString());
602 }
603
604 break;
605 default:
606 m_console.Error("Unknown debug");
607 break;
608 }
609 }
610
611 // see BaseOpenSimServer
612 public override void Show(string ShowWhat)
613 {
614 base.Show(ShowWhat);
615
616 switch (ShowWhat)
617 {
618 case "assets":
619 m_assetCache.ShowState();
620 break;
621
622 case "users":
623 IList agents = m_sceneManager.GetCurrentSceneAvatars();
624
625 m_console.Notice(String.Format("\nAgents connected: {0}\n", agents.Count));
626
627 m_console.Notice(
628 String.Format("{0,-16}{1,-16}{2,-37}{3,-16}{4,-22}{5,-16}{6,-15}", "Firstname", "Lastname",
629 "Agent ID", "Circuit", "IP", "Region", "Status"));
630
631 foreach (ScenePresence presence in agents)
632 {
633 RegionInfo regionInfo = m_sceneManager.GetRegionInfo(presence.RegionHandle);
634 string regionName;
635 System.Net.EndPoint ep = null;
636
637 if (regionInfo == null)
638 {
639 regionName = "Unresolvable";
640 }
641 else
642 {
643 regionName = regionInfo.RegionName;
644 }
645
646 for (int i = 0; i < m_udpServers.Count; i++)
647 {
648 if (m_udpServers[i].RegionHandle == presence.RegionHandle)
649 {
650
651 m_udpServers[i].clientCircuits_reverse.TryGetValue(presence.ControllingClient.CircuitCode, out ep);
652 }
653 }
654
655 m_console.Notice(
656 String.Format(
657 "{0,-16}{1,-16}{2,-37}{3,-16}{4,-22}{5,-16}{6,-15}",
658 presence.Firstname,
659 presence.Lastname,
660 presence.UUID,
661 presence.ControllingClient.CircuitCode,
662 ep,
663 regionName,
664 ((((ClientView)presence.ControllingClient).PacketProcessingEnabled)
665 ?"Active client":"Standby client")));
666 }
667
668 m_console.Notice("");
669
670 break;
671 case "modules":
672 m_console.Notice("The currently loaded shared modules are:");
673 foreach (IRegionModule module in m_moduleLoader.GetLoadedSharedModules)
674 {
675 m_console.Notice("Shared Module: " + module.Name);
676 }
677 break;
678
679 case "regions":
680 m_sceneManager.ForEachScene(
681 delegate(Scene scene)
682 {
683 m_console.Notice("Region Name: " + scene.RegionInfo.RegionName + " , Region XLoc: " +
684 scene.RegionInfo.RegionLocX + " , Region YLoc: " +
685 scene.RegionInfo.RegionLocY);
686 });
687 break;
688
689 case "stats":
690 if (StatsManager.SimExtraStats != null)
691 {
692 m_console.Notice(
693 "STATS", Environment.NewLine + StatsManager.SimExtraStats.Report());
694 }
695 else
696 {
697 m_console.Notice("Extra sim statistics collection has not been enabled");
698 }
699 break;
700 }
701 }
702
703 private string CombineParams(string[] commandParams, int pos)
704 {
705 string result = String.Empty;
706 for (int i = pos; i < commandParams.Length; i++)
707 {
708 result += commandParams[i] + " ";
709 }
710 result = result.TrimEnd(' ');
711 return result;
712 }
713
714 #endregion
715 }
716}