From e77f91311678154390d09b586fbd8162cd3bb601 Mon Sep 17 00:00:00 2001
From: UbitUmarov
Date: Wed, 16 Nov 2016 22:34:56 +0000
Subject: minor: dont let rcvd agentupdates time jump back
---
OpenSim/Framework/AgentUpdateArgs.cs | 1 +
1 file changed, 1 insertion(+)
(limited to 'OpenSim/Framework')
diff --git a/OpenSim/Framework/AgentUpdateArgs.cs b/OpenSim/Framework/AgentUpdateArgs.cs
index eaa7902..f04d692 100644
--- a/OpenSim/Framework/AgentUpdateArgs.cs
+++ b/OpenSim/Framework/AgentUpdateArgs.cs
@@ -82,6 +82,7 @@ namespace OpenSim.Framework
public Vector3 ClientAgentPosition;
public bool UseClientAgentPosition;
public bool NeedsCameraCollision;
+ public uint lastpacketSequence;
public AgentUpdateArgs()
{
--
cgit v1.1
From 8599a9a1cae6dc7a0760448b4ac8e9a773348c1e Mon Sep 17 00:00:00 2001
From: UbitUmarov
Date: Thu, 17 Nov 2016 17:37:22 +0000
Subject: add a missing dispose
---
OpenSim/Framework/TaskInventoryDictionary.cs | 7 +++++++
1 file changed, 7 insertions(+)
(limited to 'OpenSim/Framework')
diff --git a/OpenSim/Framework/TaskInventoryDictionary.cs b/OpenSim/Framework/TaskInventoryDictionary.cs
index 2c20ef7..c270d98 100644
--- a/OpenSim/Framework/TaskInventoryDictionary.cs
+++ b/OpenSim/Framework/TaskInventoryDictionary.cs
@@ -64,6 +64,13 @@ namespace OpenSim.Framework
///
private volatile System.Threading.ReaderWriterLockSlim m_itemLock = new System.Threading.ReaderWriterLockSlim();
+
+ ~TaskInventoryDictionary()
+ {
+ m_itemLock.Dispose();
+ m_itemLock = null;
+ }
+
///
/// Are we readlocked by the calling thread?
///
--
cgit v1.1
From 35b37510fca537d12c2eabc68be51e370dc6d6ce Mon Sep 17 00:00:00 2001
From: UbitUmarov
Date: Thu, 17 Nov 2016 19:15:28 +0000
Subject: explicitly remove some references, and other useless changes
---
OpenSim/Framework/WebUtil.cs | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
(limited to 'OpenSim/Framework')
diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs
index 51d87bd..2bbf785 100644
--- a/OpenSim/Framework/WebUtil.cs
+++ b/OpenSim/Framework/WebUtil.cs
@@ -1062,11 +1062,10 @@ namespace OpenSim.Framework
if (WebUtil.DebugLevel >= 5)
WebUtil.LogOutgoingDetail("SEND", reqnum, System.Text.Encoding.UTF8.GetString(data));
- Stream requestStream = null;
try
{
- requestStream = request.GetRequestStream();
- requestStream.Write(data, 0, length);
+ using(Stream requestStream = request.GetRequestStream())
+ requestStream.Write(data,0,length);
}
catch (Exception e)
{
@@ -1076,9 +1075,6 @@ namespace OpenSim.Framework
}
finally
{
- if (requestStream != null)
- requestStream.Dispose();
-
// capture how much time was spent writing
tickdata = Util.EnvironmentTickCountSubtract(tickstart);
}
--
cgit v1.1
From e21ac8b3c40665449e8dc1bbc5647b93c1a59f6c Mon Sep 17 00:00:00 2001
From: UbitUmarov
Date: Thu, 17 Nov 2016 20:59:13 +0000
Subject: counting issus safeguard
---
OpenSim/Framework/LocklessQueue.cs | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
(limited to 'OpenSim/Framework')
diff --git a/OpenSim/Framework/LocklessQueue.cs b/OpenSim/Framework/LocklessQueue.cs
index 9bd9baf..7ccbba7 100644
--- a/OpenSim/Framework/LocklessQueue.cs
+++ b/OpenSim/Framework/LocklessQueue.cs
@@ -93,7 +93,10 @@ namespace OpenSim.Framework
if (oldHead == oldTail)
{
if (oldHeadNext == null)
+ {
+ count = 0;
return false;
+ }
CAS(ref tail, oldTail, oldHeadNext);
}
@@ -118,8 +121,7 @@ namespace OpenSim.Framework
{
// ugly
T item;
- while(count > 0)
- Dequeue(out item);
+ while(Dequeue(out item));
Init();
}
--
cgit v1.1
From ba7904a3a80713f726d434fc82e9a80306372e2c Mon Sep 17 00:00:00 2001
From: UbitUmarov
Date: Fri, 18 Nov 2016 00:12:09 +0000
Subject: a few more changes on potencial mem issues
---
OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs | 5 +++++
1 file changed, 5 insertions(+)
(limited to 'OpenSim/Framework')
diff --git a/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs b/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs
index 9056548..4ff8cba 100644
--- a/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs
+++ b/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs
@@ -55,6 +55,11 @@ namespace OpenSim.Framework
Dictionary2 = new Dictionary(capacity);
}
+ ~DoubleDictionaryThreadAbortSafe()
+ {
+ rwLock.Dispose();
+ }
+
public void Add(TKey1 key1, TKey2 key2, TValue value)
{
bool gotLock = false;
--
cgit v1.1
From 28f7d429fc5cc6a8e52766d06d6a261825d792c2 Mon Sep 17 00:00:00 2001
From: Melanie Thielker
Date: Sat, 19 Nov 2016 02:27:31 +0000
Subject: REST console v2. This is an incompatible protocol change. It degrades
gracefully.
---
OpenSim/Framework/Console/RemoteConsole.cs | 321 ++++++++++++++++++++++++-----
1 file changed, 265 insertions(+), 56 deletions(-)
(limited to 'OpenSim/Framework')
diff --git a/OpenSim/Framework/Console/RemoteConsole.cs b/OpenSim/Framework/Console/RemoteConsole.cs
index 8ad7b0d..9049b4b 100644
--- a/OpenSim/Framework/Console/RemoteConsole.cs
+++ b/OpenSim/Framework/Console/RemoteConsole.cs
@@ -34,6 +34,7 @@ using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
+using System.Timers;
using OpenMetaverse;
using Nini.Config;
using OpenSim.Framework.Servers.HttpServer;
@@ -41,90 +42,232 @@ using log4net;
namespace OpenSim.Framework.Console
{
- public class ConsoleConnection
- {
- public int last;
- public long lastLineSeen;
- public bool newConnection = true;
- }
-
// A console that uses REST interfaces
//
public class RemoteConsole : CommandConsole
{
- private IHttpServer m_Server = null;
- private IConfigSource m_Config = null;
-
- private List m_Scrollback = new List();
- private ManualResetEvent m_DataEvent = new ManualResetEvent(false);
- private List m_InputData = new List();
- private long m_LineNumber = 0;
- private Dictionary m_Connections =
+ // Connection specific data, indexed by a session ID
+ // we create when a client connects.
+ protected class ConsoleConnection
+ {
+ // Last activity from the client
+ public int last;
+
+ // Last line of scrollback posted to this client
+ public long lastLineSeen;
+
+ // True if this is a new connection, e.g. has never
+ // displayed a prompt to the user.
+ public bool newConnection = true;
+ }
+
+ // A line in the scrollback buffer.
+ protected class ScrollbackEntry
+ {
+ // The line number of this entry
+ public long lineNumber;
+
+ // The text to send to the client
+ public string text;
+
+ // The level this should be logged as. Omitted for
+ // prompts and input echo.
+ public string level;
+
+ // True if the text above is a prompt, e.g. the
+ // client should turn on the cursor / accept input
+ public bool isPrompt;
+
+ // True if the requested input is a command. A
+ // client may offer help or validate input if
+ // this is set. If false, input should be sent
+ // as typed.
+ public bool isCommand;
+
+ // True if this text represents a line of text that
+ // was input in response to a prompt. A client should
+ // turn off the cursor and refrain from sending commands
+ // until a new prompt is received.
+ public bool isInput;
+ }
+
+ // Data that is relevant to all connections
+
+ // The scrollback buffer
+ protected List m_Scrollback = new List();
+
+ // Monotonously incrementing line number. This may eventually
+ // wrap. No provision is made for that case because 64 bits
+ // is a long, long time.
+ protected long m_lineNumber = 0;
+
+ // These two variables allow us to send the correct
+ // information about the prompt status to the client,
+ // irrespective of what may have run off the top of the
+ // scrollback buffer;
+ protected bool m_expectingInput = false;
+ protected bool m_expectingCommand = true;
+ protected string m_lastPromptUsed;
+
+ // This is the list of things received from clients.
+ // Note: Race conditions can happen. If a client sends
+ // something while nothing is expected, it will be
+ // intepreted as input to the next prompt. For
+ // commands this is largely correct. For other prompts,
+ // YMMV.
+ // TODO: Find a better way to fix this
+ protected List m_InputData = new List();
+
+ // Event to allow ReadLine to wait synchronously even though
+ // everthing else is asynchronous here.
+ protected ManualResetEvent m_DataEvent = new ManualResetEvent(false);
+
+ // The list of sessions we maintain. Unlike other console types,
+ // multiple users on the same console are explicitly allowed.
+ protected Dictionary m_Connections =
new Dictionary();
- private string m_UserName = String.Empty;
- private string m_Password = String.Empty;
- private string m_AllowedOrigin = String.Empty;
+
+ // Timer to control expiration of sessions that have been
+ // disconnected.
+ protected System.Timers.Timer m_expireTimer = new System.Timers.Timer(5000);
+
+ // The less interesting stuff that makes the actual server
+ // work.
+ protected IHttpServer m_Server = null;
+ protected IConfigSource m_Config = null;
+
+ protected string m_UserName = String.Empty;
+ protected string m_Password = String.Empty;
+ protected string m_AllowedOrigin = String.Empty;
+
public RemoteConsole(string defaultPrompt) : base(defaultPrompt)
{
+ // There is something wrong with this architecture.
+ // A prompt is sent on every single input, so why have this?
+ // TODO: Investigate and fix.
+ m_lastPromptUsed = defaultPrompt;
+
+ // Start expiration of sesssions.
+ m_expireTimer.Elapsed += DoExpire;
+ m_expireTimer.Start();
}
public void ReadConfig(IConfigSource config)
{
m_Config = config;
+ // We're pulling this from the 'Network' section for legacy
+ // compatibility. However, this is so essentially insecure
+ // that TLS and client certs should be used instead of
+ // a username / password.
IConfig netConfig = m_Config.Configs["Network"];
+
if (netConfig == null)
return;
+ // Get the username and password.
m_UserName = netConfig.GetString("ConsoleUser", String.Empty);
m_Password = netConfig.GetString("ConsolePass", String.Empty);
+
+ // Woefully underdocumented, this is what makes javascript
+ // console clients work. Set to "*" for anywhere or (better)
+ // to specific addresses.
m_AllowedOrigin = netConfig.GetString("ConsoleAllowedOrigin", String.Empty);
}
public void SetServer(IHttpServer server)
{
+ // This is called by the framework to give us the server
+ // instance (means: port) to work with.
m_Server = server;
+ // Add our handlers
m_Server.AddHTTPHandler("/StartSession/", HandleHttpStartSession);
m_Server.AddHTTPHandler("/CloseSession/", HandleHttpCloseSession);
m_Server.AddHTTPHandler("/SessionCommand/", HandleHttpSessionCommand);
}
public override void Output(string text, string level)
+ {
+ Output(text, level, false, false, false);
+ }
+
+ protected void Output(string text, string level, bool isPrompt, bool isCommand, bool isInput)
{
+ // Increment the line number. It was 0 and they start at 1
+ // so we need to pre-increment.
+ m_lineNumber++;
+
+ // Create and populate the new entry.
+ ScrollbackEntry newEntry = new ScrollbackEntry();
+
+ newEntry.lineNumber = m_lineNumber;
+ newEntry.text = text;
+ newEntry.level = level;
+ newEntry.isPrompt = isPrompt;
+ newEntry.isCommand = isCommand;
+ newEntry.isInput = isInput;
+
+ // Add a line to the scrollback. In some cases, that may not
+ // actually be a line of text.
lock (m_Scrollback)
{
+ // Prune the scrollback to the length se send as connect
+ // burst to give the user some context.
while (m_Scrollback.Count >= 1000)
m_Scrollback.RemoveAt(0);
- m_LineNumber++;
- m_Scrollback.Add(String.Format("{0}", m_LineNumber)+":"+level+":"+text);
+
+ m_Scrollback.Add(newEntry);
}
+
+ // Let the rest of the system know we have output something.
FireOnOutput(text.Trim());
+
+ // Also display it for debugging.
System.Console.WriteLine(text.Trim());
}
public override void Output(string text)
{
- Output(text, "normal");
+ // Output plain (non-logging style) text.
+ Output(text, String.Empty, false, false, false);
}
public override string ReadLine(string p, bool isCommand, bool e)
{
- if (isCommand)
- Output("+++"+p);
- else
- Output("-++"+p);
-
+ // Output the prompt an prepare to wait. This
+ // is called on a dedicated console thread and
+ // needs to be synchronous. Old architecture but
+ // not worth upgrading.
+ if (isCommand)
+ {
+ m_expectingInput = true;
+ m_expectingCommand = true;
+ Output(p, String.Empty, true, true, false);
+ m_lastPromptUsed = p;
+ }
+ else
+ {
+ m_expectingInput = true;
+ Output(p, String.Empty, true, false, false);
+ }
+
+
+ // Here is where we wait for the user to input something.
m_DataEvent.WaitOne();
string cmdinput;
+ // Check for empty input. Read input if not empty.
lock (m_InputData)
{
if (m_InputData.Count == 0)
{
m_DataEvent.Reset();
+ m_expectingInput = false;
+ m_expectingCommand = false;
+
return "";
}
@@ -135,8 +278,19 @@ namespace OpenSim.Framework.Console
}
+ m_expectingInput = false;
+ m_expectingCommand = false;
+
+ // Echo to all the other users what we have done. This
+ // will also go to ourselves.
+ Output (cmdinput, String.Empty, false, false, true);
+
+ // If this is a command, we need to resolve and execute it.
if (isCommand)
{
+ // This call will actually execute the command and create
+ // any output associated with it. The core just gets an
+ // empty string so it will call again immediately.
string[] cmd = Commands.Resolve(Parser.Parse(cmdinput));
if (cmd.Length != 0)
@@ -151,18 +305,23 @@ namespace OpenSim.Framework.Console
return String.Empty;
}
}
+
+ // Return the raw input string if not a command.
return cmdinput;
}
- private Hashtable CheckOrigin(Hashtable result)
+ // Very simplistic static access control header.
+ protected Hashtable CheckOrigin(Hashtable result)
{
if (!string.IsNullOrEmpty(m_AllowedOrigin))
result["access_control_allow_origin"] = m_AllowedOrigin;
+
return result;
}
+
/* TODO: Figure out how PollServiceHTTPHandler can access the request headers
* in order to use m_AllowedOrigin as a regular expression
- private Hashtable CheckOrigin(Hashtable headers, Hashtable result)
+ protected Hashtable CheckOrigin(Hashtable headers, Hashtable result)
{
if (!string.IsNullOrEmpty(m_AllowedOrigin))
{
@@ -177,18 +336,23 @@ namespace OpenSim.Framework.Console
}
*/
- private void DoExpire()
+ protected void DoExpire(Object sender, ElapsedEventArgs e)
{
+ // Iterate the list of console connections and find those we
+ // haven't heard from for longer then the longpoll interval.
+ // Remove them.
List expired = new List();
lock (m_Connections)
{
+ // Mark the expired ones
foreach (KeyValuePair kvp in m_Connections)
{
if (System.Environment.TickCount - kvp.Value.last > 500000)
expired.Add(kvp.Key);
}
+ // Delete them
foreach (UUID id in expired)
{
m_Connections.Remove(id);
@@ -197,10 +361,10 @@ namespace OpenSim.Framework.Console
}
}
- private Hashtable HandleHttpStartSession(Hashtable request)
+ // Start a new session.
+ protected Hashtable HandleHttpStartSession(Hashtable request)
{
- DoExpire();
-
+ // The login is in the form of a http form post
Hashtable post = DecodePostString(request["body"].ToString());
Hashtable reply = new Hashtable();
@@ -208,6 +372,7 @@ namespace OpenSim.Framework.Console
reply["int_response_code"] = 401;
reply["content_type"] = "text/plain";
+ // Check user name and password
if (m_UserName == String.Empty)
return reply;
@@ -220,22 +385,28 @@ namespace OpenSim.Framework.Console
return reply;
}
+ // Set up the new console connection record
ConsoleConnection c = new ConsoleConnection();
c.last = System.Environment.TickCount;
c.lastLineSeen = 0;
+ // Assign session ID
UUID sessionID = UUID.Random();
+ // Add connection to list.
lock (m_Connections)
{
m_Connections[sessionID] = c;
}
+ // This call is a CAP. The URL is the authentication.
string uri = "/ReadResponses/" + sessionID.ToString() + "/";
m_Server.AddPollServiceHTTPHandler(
uri, new PollServiceEventArgs(null, uri, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout
+ // Our reply is an XML document.
+ // TODO: Change this to Linq.Xml
XmlDocument xmldoc = new XmlDocument();
XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration,
"", "");
@@ -252,12 +423,13 @@ namespace OpenSim.Framework.Console
rootElement.AppendChild(id);
XmlElement prompt = xmldoc.CreateElement("", "Prompt", "");
- prompt.AppendChild(xmldoc.CreateTextNode(DefaultPrompt));
+ prompt.AppendChild(xmldoc.CreateTextNode(m_lastPromptUsed));
rootElement.AppendChild(prompt);
rootElement.AppendChild(MainConsole.Instance.Commands.GetXml(xmldoc));
+ // Set up the response and check origin
reply["str_response_string"] = xmldoc.InnerXml;
reply["int_response_code"] = 200;
reply["content_type"] = "text/xml";
@@ -266,10 +438,9 @@ namespace OpenSim.Framework.Console
return reply;
}
- private Hashtable HandleHttpCloseSession(Hashtable request)
+ // Client closes session. Clean up.
+ protected Hashtable HandleHttpCloseSession(Hashtable request)
{
- DoExpire();
-
Hashtable post = DecodePostString(request["body"].ToString());
Hashtable reply = new Hashtable();
@@ -316,10 +487,9 @@ namespace OpenSim.Framework.Console
return reply;
}
- private Hashtable HandleHttpSessionCommand(Hashtable request)
+ // Command received from the client.
+ protected Hashtable HandleHttpSessionCommand(Hashtable request)
{
- DoExpire();
-
Hashtable post = DecodePostString(request["body"].ToString());
Hashtable reply = new Hashtable();
@@ -327,6 +497,7 @@ namespace OpenSim.Framework.Console
reply["int_response_code"] = 404;
reply["content_type"] = "text/plain";
+ // Check the ID
if (post["ID"] == null)
return reply;
@@ -334,21 +505,25 @@ namespace OpenSim.Framework.Console
if (!UUID.TryParse(post["ID"].ToString(), out id))
return reply;
+ // Find the connection for that ID.
lock (m_Connections)
{
if (!m_Connections.ContainsKey(id))
return reply;
}
+ // Empty post. Just error out.
if (post["COMMAND"] == null)
return reply;
+ // Place the input data in the buffer.
lock (m_InputData)
{
m_DataEvent.Set();
m_InputData.Add(post["COMMAND"].ToString());
}
+ // Create the XML reply document.
XmlDocument xmldoc = new XmlDocument();
XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration,
"", "");
@@ -372,7 +547,8 @@ namespace OpenSim.Framework.Console
return reply;
}
- private Hashtable DecodePostString(string data)
+ // Decode a HTTP form post to a Hashtable
+ protected Hashtable DecodePostString(string data)
{
Hashtable result = new Hashtable();
@@ -396,6 +572,7 @@ namespace OpenSim.Framework.Console
return result;
}
+ // Close the CAP receiver for the responses for a given client.
public void CloseConnection(UUID id)
{
try
@@ -409,7 +586,9 @@ namespace OpenSim.Framework.Console
}
}
- private bool HasEvents(UUID RequestID, UUID sessionID)
+ // Check if there is anything to send. Return true if this client has
+ // lines pending.
+ protected bool HasEvents(UUID RequestID, UUID sessionID)
{
ConsoleConnection c = null;
@@ -420,13 +599,15 @@ namespace OpenSim.Framework.Console
c = m_Connections[sessionID];
}
c.last = System.Environment.TickCount;
- if (c.lastLineSeen < m_LineNumber)
+ if (c.lastLineSeen < m_lineNumber)
return true;
return false;
}
- private Hashtable GetEvents(UUID RequestID, UUID sessionID)
+ // Send all pending output to the client.
+ protected Hashtable GetEvents(UUID RequestID, UUID sessionID)
{
+ // Find the connection that goes with this client.
ConsoleConnection c = null;
lock (m_Connections)
@@ -435,12 +616,15 @@ namespace OpenSim.Framework.Console
return NoEvents(RequestID, UUID.Zero);
c = m_Connections[sessionID];
}
+
+ // If we have nothing to send, send the no events response.
c.last = System.Environment.TickCount;
- if (c.lastLineSeen >= m_LineNumber)
+ if (c.lastLineSeen >= m_lineNumber)
return NoEvents(RequestID, UUID.Zero);
Hashtable result = new Hashtable();
+ // Create the response document.
XmlDocument xmldoc = new XmlDocument();
XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration,
"", "");
@@ -449,30 +633,53 @@ namespace OpenSim.Framework.Console
XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession",
"");
- if (c.newConnection)
- {
- c.newConnection = false;
- Output("+++" + DefaultPrompt);
- }
+ //if (c.newConnection)
+ //{
+ // c.newConnection = false;
+ // Output("+++" + DefaultPrompt);
+ //}
lock (m_Scrollback)
{
- long startLine = m_LineNumber - m_Scrollback.Count;
+ long startLine = m_lineNumber - m_Scrollback.Count;
long sendStart = startLine;
if (sendStart < c.lastLineSeen)
sendStart = c.lastLineSeen;
- for (long i = sendStart ; i < m_LineNumber ; i++)
+ for (long i = sendStart ; i < m_lineNumber ; i++)
{
+ ScrollbackEntry e = m_Scrollback[(int)(i - startLine)];
+
XmlElement res = xmldoc.CreateElement("", "Line", "");
- long line = i + 1;
- res.SetAttribute("Number", line.ToString());
- res.AppendChild(xmldoc.CreateTextNode(m_Scrollback[(int)(i - startLine)]));
+ res.SetAttribute("Number", e.lineNumber.ToString());
+ res.SetAttribute("Level", e.level);
+ // Don't include these for the scrollback, we'll send the
+ // real state later.
+ if (!c.newConnection)
+ {
+ res.SetAttribute("Prompt", e.isPrompt ? "true" : "false");
+ res.SetAttribute("Command", e.isCommand ? "true" : "false");
+ res.SetAttribute("Input", e.isInput ? "true" : "false");
+ }
+ else if (i == m_lineNumber - 1) // Last line for a new connection
+ {
+ res.SetAttribute("Prompt", m_expectingInput ? "true" : "false");
+ res.SetAttribute("Command", m_expectingCommand ? "true" : "false");
+ res.SetAttribute("Input", (!m_expectingInput) ? "true" : "false");
+ }
+ else
+ {
+ res.SetAttribute("Input", e.isInput ? "true" : "false");
+ }
+
+ res.AppendChild(xmldoc.CreateTextNode(e.text));
rootElement.AppendChild(res);
}
}
- c.lastLineSeen = m_LineNumber;
+
+ c.lastLineSeen = m_lineNumber;
+ c.newConnection = false;
xmldoc.AppendChild(rootElement);
@@ -486,7 +693,9 @@ namespace OpenSim.Framework.Console
return result;
}
- private Hashtable NoEvents(UUID RequestID, UUID id)
+ // This is really just a no-op. It generates what is sent
+ // to the client if the poll times out without any events.
+ protected Hashtable NoEvents(UUID RequestID, UUID id)
{
Hashtable result = new Hashtable();
--
cgit v1.1