diff --git a/OpenSim/Client/Linden/LLClientStackModule.cs b/OpenSim/Client/Linden/LLClientStackModule.cs deleted file mode 100644 index f882d5d301..0000000000 --- a/OpenSim/Client/Linden/LLClientStackModule.cs +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using System.Net; -using System.Reflection; -using System.Text; -using log4net; -using Nini.Config; -using OpenMetaverse; -using OpenSim.Region.ClientStack; -using OpenSim.Region.ClientStack.LindenUDP; -using OpenSim.Framework; -using OpenSim.Region.Framework.Scenes; -using OpenSim.Region.Framework.Interfaces; - -namespace OpenSim.Client.Linden -{ - /// - /// Linden UDP Stack Region Module - /// - public class LLClientStackModule : INonSharedRegionModule - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - #region IRegionModule Members - - /// - /// Scene that contains the region's data - /// - protected Scene m_scene; - protected bool m_createClientStack = false; - protected IClientNetworkServer m_clientServer; - protected ClientStackManager m_clientStackManager; - protected IConfigSource m_source; - - protected string m_clientStackDll = "OpenSim.Region.ClientStack.LindenUDP.dll"; - - public void Initialise(IConfigSource source) - { - if (m_scene == null) - { - m_source = source; - - IConfig startupConfig = m_source.Configs["Startup"]; - if (startupConfig != null) - { - m_clientStackDll = startupConfig.GetString("clientstack_plugin", "OpenSim.Region.ClientStack.LindenUDP.dll"); - } - } - } - - public void AddRegion(Scene scene) - { - - } - - public void RemoveRegion(Scene scene) - { - - } - - public void RegionLoaded(Scene scene) - { - if (m_scene == null) - { - m_scene = scene; - } - - if ((m_scene != null) && (m_createClientStack)) - { - m_log.Info("[LLClientStackModule] Starting up LLClientStack."); - IPEndPoint endPoint = m_scene.RegionInfo.InternalEndPoint; - - uint port = (uint)endPoint.Port; - m_clientStackManager = new ClientStackManager(m_clientStackDll); - - m_clientServer - = m_clientStackManager.CreateServer(endPoint.Address, - ref port, m_scene.RegionInfo.ProxyOffset, m_scene.RegionInfo.m_allow_alternate_ports, m_source, - m_scene.AuthenticateHandler); - - m_clientServer.AddScene(m_scene); - - m_clientServer.Start(); - } - } - - public void Close() - { - - } - - public Type ReplaceableInterface - { - get { return null; } - } - - public string Name - { - get { return "LLClientStackModule"; } - } - - public bool IsSharedModule - { - get { return false; } - } - - #endregion - } -} diff --git a/OpenSim/Client/Linden/Resources/LindenModules.addin.xml b/OpenSim/Client/Linden/Resources/LindenModules.addin.xml deleted file mode 100644 index af41e98cea..0000000000 --- a/OpenSim/Client/Linden/Resources/LindenModules.addin.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/OpenSim/Data/MySQL/MySQLInventoryData.cs b/OpenSim/Data/MySQL/MySQLInventoryData.cs index 2dca3eb6a9..9d70acbeeb 100644 --- a/OpenSim/Data/MySQL/MySQLInventoryData.cs +++ b/OpenSim/Data/MySQL/MySQLInventoryData.cs @@ -867,7 +867,7 @@ namespace OpenSim.Data.MySQL dbcon.Open(); using (MySqlCommand sqlCmd = new MySqlCommand( - "SELECT * FROM inventoryitems WHERE avatarId = ?uuid AND assetType = ?type and flags = 1", dbcon)) + "SELECT * FROM inventoryitems WHERE avatarId = ?uuid AND assetType = ?type and flags & 1", dbcon)) { sqlCmd.Parameters.AddWithValue("?uuid", avatarID.ToString()); sqlCmd.Parameters.AddWithValue("?type", (int)AssetType.Gesture); diff --git a/OpenSim/Data/MySQL/MySQLXInventoryData.cs b/OpenSim/Data/MySQL/MySQLXInventoryData.cs index 3c73095fc3..287c4dda7b 100644 --- a/OpenSim/Data/MySQL/MySQLXInventoryData.cs +++ b/OpenSim/Data/MySQL/MySQLXInventoryData.cs @@ -130,7 +130,7 @@ namespace OpenSim.Data.MySQL { using (MySqlCommand cmd = new MySqlCommand()) { - cmd.CommandText = String.Format("select * from inventoryitems where avatarId = ?uuid and assetType = ?type and flags = 1", m_Realm); + cmd.CommandText = String.Format("select * from inventoryitems where avatarId = ?uuid and assetType = ?type and flags & 1", m_Realm); cmd.Parameters.AddWithValue("?uuid", principalID.ToString()); cmd.Parameters.AddWithValue("?type", (int)AssetType.Gesture); diff --git a/OpenSim/Framework/Capabilities/Caps.cs b/OpenSim/Framework/Capabilities/Caps.cs index dbb07817f6..c2f9c3ab7b 100644 --- a/OpenSim/Framework/Capabilities/Caps.cs +++ b/OpenSim/Framework/Capabilities/Caps.cs @@ -765,8 +765,8 @@ namespace OpenSim.Framework.Capabilities { try { - m_log.Debug("[CAPS]: UploadBakedTexture Request in region: " + - m_regionName); +// m_log.Debug("[CAPS]: UploadBakedTexture Request in region: " + +// m_regionName); string capsBase = "/CAPS/" + m_capsObjectPath; string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); @@ -1332,7 +1332,7 @@ namespace OpenSim.Framework.Capabilities newAssetID = UUID.Random(); uploaderPath = path; httpListener = httpServer; - m_log.InfoFormat("[CAPS] baked texture upload starting for {0}",newAssetID); +// m_log.InfoFormat("[CAPS] baked texture upload starting for {0}",newAssetID); } /// @@ -1360,7 +1360,7 @@ namespace OpenSim.Framework.Capabilities httpListener.RemoveStreamHandler("POST", uploaderPath); - m_log.InfoFormat("[CAPS] baked texture upload completed for {0}",newAssetID); +// m_log.InfoFormat("[CAPS] baked texture upload completed for {0}",newAssetID); return res; } diff --git a/OpenSim/Framework/Console/ConsoleBase.cs b/OpenSim/Framework/Console/ConsoleBase.cs index 22ce88060c..3ef76cf588 100755 --- a/OpenSim/Framework/Console/ConsoleBase.cs +++ b/OpenSim/Framework/Console/ConsoleBase.cs @@ -75,6 +75,11 @@ namespace OpenSim.Framework.Console { System.Console.WriteLine(text); } + + public virtual void OutputFormat(string format, params string[] components) + { + Output(string.Format(format, components)); + } public string CmdPrompt(string p) { diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs index cbab2dbc6a..b28ad69a5e 100644 --- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs @@ -175,7 +175,7 @@ namespace OpenSim.Framework.Servers m_console.Commands.AddCommand("base", false, "show info", "show info", - "Show general information", HandleShow); + "Show general information about the server", HandleShow); m_console.Commands.AddCommand("base", false, "show stats", "show stats", @@ -371,8 +371,7 @@ namespace OpenSim.Framework.Servers switch (showParams[0]) { case "info": - Notice("Version: " + m_version); - Notice("Startup directory: " + m_startupDirectory); + ShowInfo(); break; case "stats": @@ -389,18 +388,30 @@ namespace OpenSim.Framework.Servers break; case "version": - Notice( - String.Format( - "Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion)); + Notice(GetVersionText()); break; } } + + protected void ShowInfo() + { + Notice(GetVersionText()); + Notice("Startup directory: " + m_startupDirectory); + if (null != m_consoleAppender) + Notice(String.Format("Console log level: {0}", m_consoleAppender.Threshold)); + } + + protected string GetVersionText() + { + return String.Format("Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion); + } /// /// Console output is only possible if a console has been established. /// That is something that cannot be determined within this class. So /// all attempts to use the console MUST be verified. /// + /// protected void Notice(string msg) { if (m_console != null) @@ -408,6 +419,19 @@ namespace OpenSim.Framework.Servers m_console.Output(msg); } } + + /// + /// Console output is only possible if a console has been established. + /// That is something that cannot be determined within this class. So + /// all attempts to use the console MUST be verified. + /// + /// + /// + protected void Notice(string format, params string[] components) + { + if (m_console != null) + m_console.OutputFormat(format, components); + } /// /// Enhance the version string with extra information if it's available. diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index 3343f608a5..d4ee7ba56d 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -346,9 +346,15 @@ namespace OpenSim.Framework.Servers.HttpServer /// public virtual void HandleRequest(OSHttpRequest request, OSHttpResponse response) { + string reqnum = "unknown"; + int tickstart = Environment.TickCount; + try { - //m_log.Debug("[BASE HTTP SERVER]: Handling request to " + request.RawUrl); + // OpenSim.Framework.WebUtil.OSHeaderRequestID + if (request.Headers["opensim-request-id"] != null) + reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]); + // m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl); Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", true); @@ -576,6 +582,15 @@ namespace OpenSim.Framework.Servers.HttpServer m_log.ErrorFormat("[BASE HTTP SERVER]: HandleRequest() threw {0}", e); SendHTML500(response); } + finally + { + // Every month or so this will wrap and give bad numbers, not really a problem + // since its just for reporting, 200ms limit can be adjusted + int tickdiff = Environment.TickCount - tickstart; + if (tickdiff > 500) + m_log.InfoFormat( + "[BASE HTTP SERVER]: slow request <{0}> for {1} took {2} ms", reqnum, request.RawUrl, tickdiff); + } } private bool TryGetStreamHandler(string handlerKey, out IRequestHandler streamHandler) diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 8d1671a885..d1d8736062 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -46,7 +46,7 @@ using System.Threading; using log4net; using Nini.Config; using Nwc.XmlRpc; -using BclExtras; +// using BclExtras; using OpenMetaverse; using OpenMetaverse.StructuredData; using Amib.Threading; @@ -1375,8 +1375,29 @@ namespace OpenSim.Framework /// /// Created to work around a limitation in Mono with nested delegates /// - private class FireAndForgetWrapper + private sealed class FireAndForgetWrapper { + private static volatile FireAndForgetWrapper instance; + private static object syncRoot = new Object(); + + public static FireAndForgetWrapper Instance { + get { + + if (instance == null) + { + lock (syncRoot) + { + if (instance == null) + { + instance = new FireAndForgetWrapper(); + } + } + } + + return instance; + } + } + public void FireAndForget(System.Threading.WaitCallback callback) { callback.BeginInvoke(null, EndFireAndForget, callback); @@ -1445,7 +1466,7 @@ namespace OpenSim.Framework ThreadPool.QueueUserWorkItem(callback, obj); break; case FireAndForgetMethod.BeginInvoke: - FireAndForgetWrapper wrapper = Singleton.GetInstance(); + FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance; wrapper.FireAndForget(callback, obj); break; case FireAndForgetMethod.SmartThreadPool: diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index 1c856af89e..d731ac5882 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -50,6 +50,16 @@ namespace OpenSim.Framework LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); + private static int m_requestNumber = 0; + + // this is the header field used to communicate the local request id + // used for performance and debugging + public const string OSHeaderRequestID = "opensim-request-id"; + + // number of milliseconds a call can take before it is considered + // a "long" call for warning & debugging purposes + public const int LongCallTime = 500; + /// /// Send LLSD to an HTTP client in application/llsd+json form /// @@ -122,27 +132,187 @@ namespace OpenSim.Framework return new OSDMap { { "Message", OSD.FromString("Service request failed. " + errorMessage) } }; } + /// + /// PUT JSON-encoded data to a web service that returns LLSD or + /// JSON data + /// + public static OSDMap PutToService(string url, OSDMap data) + { + return ServiceOSDRequest(url,data,"PUT",10000); + } + + public static OSDMap PostToService(string url, OSDMap data) + { + return ServiceOSDRequest(url,data,"POST",10000); + } + + public static OSDMap GetFromService(string url) + { + return ServiceOSDRequest(url,null,"GET",10000); + } + + public static OSDMap ServiceOSDRequest(string url, OSDMap data, string method, int timeout) + { + int reqnum = m_requestNumber++; + // m_log.DebugFormat("[WEB UTIL]: <{0}> start osd request for {1}, method {2}",reqnum,url,method); + + string errorMessage = "unknown error"; + int tickstart = Util.EnvironmentTickCount(); + int tickdata = 0; + + try + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.Method = method; + request.Timeout = timeout; + request.KeepAlive = false; + request.MaximumAutomaticRedirections = 10; + request.ReadWriteTimeout = timeout / 4; + request.Headers[OSHeaderRequestID] = reqnum.ToString(); + + // If there is some input, write it into the request + if (data != null) + { + string strBuffer = OSDParser.SerializeJsonString(data); + byte[] buffer = System.Text.Encoding.UTF8.GetBytes(strBuffer); + + request.ContentType = "application/json"; + request.ContentLength = buffer.Length; //Count bytes to send + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(buffer, 0, buffer.Length); //Send it + } + + // capture how much time was spent writing, this may seem silly + // but with the number concurrent requests, this often blocks + tickdata = Util.EnvironmentTickCountSubtract(tickstart); + + using (WebResponse response = request.GetResponse()) + { + using (Stream responseStream = response.GetResponseStream()) + { + string responseStr = null; + responseStr = responseStream.GetStreamString(); + // m_log.DebugFormat("[WEB UTIL]: <{0}> response is <{1}>",reqnum,responseStr); + return CanonicalizeResults(responseStr); + } + } + } + catch (WebException we) + { + errorMessage = we.Message; + if (we.Status == WebExceptionStatus.ProtocolError) + { + HttpWebResponse webResponse = (HttpWebResponse)we.Response; + errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription); + } + } + catch (Exception ex) + { + errorMessage = ex.Message; + } + finally + { + // This just dumps a warning for any operation that takes more than 100 ms + int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); + if (tickdiff > LongCallTime) + m_log.InfoFormat("[WEB UTIL]: osd request <{0}> (URI:{1}, METHOD:{2}) took {3}ms overall, {4}ms writing", + reqnum,url,method,tickdiff,tickdata); + } + + m_log.WarnFormat("[WEB UTIL] <{0}> osd request failed: {1}",reqnum,errorMessage); + return ErrorResponseMap(errorMessage); + } + + /// + /// Since there are no consistencies in the way web requests are + /// formed, we need to do a little guessing about the result format. + /// Keys: + /// Success|success == the success fail of the request + /// _RawResult == the raw string that came back + /// _Result == the OSD unpacked string + /// + private static OSDMap CanonicalizeResults(string response) + { + OSDMap result = new OSDMap(); + + // Default values + result["Success"] = OSD.FromBoolean(true); + result["success"] = OSD.FromBoolean(true); + result["_RawResult"] = OSD.FromString(response); + result["_Result"] = new OSDMap(); + + if (response.Equals("true",System.StringComparison.OrdinalIgnoreCase)) + return result; + + if (response.Equals("false",System.StringComparison.OrdinalIgnoreCase)) + { + result["Success"] = OSD.FromBoolean(false); + result["success"] = OSD.FromBoolean(false); + return result; + } + + try + { + OSD responseOSD = OSDParser.Deserialize(response); + if (responseOSD.Type == OSDType.Map) + { + result["_Result"] = (OSDMap)responseOSD; + return result; + } + } + catch (Exception e) + { + // don't need to treat this as an error... we're just guessing anyway + m_log.DebugFormat("[WEB UTIL] couldn't decode <{0}>: {1}",response,e.Message); + } + + return result; + } + /// /// POST URL-encoded form data to a web service that returns LLSD or /// JSON data /// public static OSDMap PostToService(string url, NameValueCollection data) { - string errorMessage; + return ServiceFormRequest(url,data,10000); + } + + public static OSDMap ServiceFormRequest(string url, NameValueCollection data, int timeout) + { + int reqnum = m_requestNumber++; + string method = (data != null && data["RequestMethod"] != null) ? data["RequestMethod"] : "unknown"; + // m_log.DebugFormat("[WEB UTIL]: <{0}> start form request for {1}, method {2}",reqnum,url,method); + + string errorMessage = "unknown error"; + int tickstart = Util.EnvironmentTickCount(); + int tickdata = 0; try { - string queryString = BuildQueryString(data); - byte[] requestData = System.Text.Encoding.UTF8.GetBytes(queryString); - + HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); request.Method = "POST"; - request.ContentLength = requestData.Length; - request.ContentType = "application/x-www-form-urlencoded"; + request.Timeout = timeout; + request.KeepAlive = false; + request.MaximumAutomaticRedirections = 10; + request.ReadWriteTimeout = timeout / 4; + request.Headers[OSHeaderRequestID] = reqnum.ToString(); + + if (data != null) + { + string queryString = BuildQueryString(data); + byte[] buffer = System.Text.Encoding.UTF8.GetBytes(queryString); + + request.ContentLength = buffer.Length; + request.ContentType = "application/x-www-form-urlencoded"; + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(buffer, 0, buffer.Length); + } - Stream requestStream = request.GetRequestStream(); - requestStream.Write(requestData, 0, requestData.Length); - requestStream.Close(); + // capture how much time was spent writing, this may seem silly + // but with the number concurrent requests, this often blocks + tickdata = Util.EnvironmentTickCountSubtract(tickstart); using (WebResponse response = request.GetResponse()) { @@ -150,34 +320,50 @@ namespace OpenSim.Framework { string responseStr = null; - try - { - responseStr = responseStream.GetStreamString(); - OSD responseOSD = OSDParser.Deserialize(responseStr); - if (responseOSD.Type == OSDType.Map) - return (OSDMap)responseOSD; - else - errorMessage = "Response format was invalid."; - } - catch (Exception ex) - { - if (!String.IsNullOrEmpty(responseStr)) - errorMessage = "Failed to parse the response:\n" + responseStr; - else - errorMessage = "Failed to retrieve the response: " + ex.Message; - } + responseStr = responseStream.GetStreamString(); + OSD responseOSD = OSDParser.Deserialize(responseStr); + if (responseOSD.Type == OSDType.Map) + return (OSDMap)responseOSD; } } } + catch (WebException we) + { + errorMessage = we.Message; + if (we.Status == WebExceptionStatus.ProtocolError) + { + HttpWebResponse webResponse = (HttpWebResponse)we.Response; + errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription); + } + } catch (Exception ex) { - m_log.Warn("POST to URL " + url + " failed: " + ex); errorMessage = ex.Message; } + finally + { + int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); + if (tickdiff > LongCallTime) + m_log.InfoFormat("[WEB UTIL]: form request <{0}> (URI:{1}, METHOD:{2}) took {3}ms overall, {4}ms writing", + reqnum,url,method,tickdiff,tickdata); + } - return new OSDMap { { "Message", OSD.FromString("Service request failed. " + errorMessage) } }; + m_log.WarnFormat("[WEB UTIL]: <{0}> form request failed: {1}",reqnum,errorMessage); + return ErrorResponseMap(errorMessage); } + /// + /// Create a response map for an error, trying to keep + /// the result formats consistent + /// + private static OSDMap ErrorResponseMap(string msg) + { + OSDMap result = new OSDMap(); + result["Success"] = "False"; + result["Message"] = OSD.FromString("Service request failed: " + msg); + return result; + } + #region Uri /// diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index b3813c7807..5a6965b011 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -283,10 +283,6 @@ namespace OpenSim "kick user [message]", "Kick a user off the simulator", KickUserCommand); - m_console.Commands.AddCommand("region", false, "show assets", - "show assets", - "Show asset data", HandleShow); - m_console.Commands.AddCommand("region", false, "show users", "show users [full]", "Show user data for users currently on the region", @@ -305,13 +301,6 @@ namespace OpenSim m_console.Commands.AddCommand("region", false, "show regions", "show regions", "Show region data", HandleShow); - - m_console.Commands.AddCommand("region", false, "show queues", - "show queues [full]", - "Show queue data for each client", - "Without the 'full' option, only users actually on the region are shown." - + " With the 'full' option child agents of users in neighbouring regions are also shown.", - HandleShow); m_console.Commands.AddCommand("region", false, "show ratings", "show ratings", @@ -335,16 +324,19 @@ namespace OpenSim "Restart all sims in this instance", RunCommand); m_console.Commands.AddCommand("region", false, "config set", - "config set
", - "Set a config option", HandleConfig); + "config set
", + "Set a config option. In most cases this is not useful since changed parameters are not dynamically reloaded. Neither do changed parameters persist - you will have to change a config file manually and restart.", HandleConfig); m_console.Commands.AddCommand("region", false, "config get", - "config get
", - "Read a config option", HandleConfig); + "config get [
] []", + "Show a config option", + "If neither section nor field are specified, then the whole current configuration is printed." + Environment.NewLine + + "If a section is given but not a field, then all fields in that section are printed.", + HandleConfig); m_console.Commands.AddCommand("region", false, "config save", - "config save", - "Save current configuration", HandleConfig); + "config save ", + "Save current configuration to a file at the given path", HandleConfig); m_console.Commands.AddCommand("region", false, "command-script", "command-script