");
+
+ //Set SMTP SERVER config
+ SmtpServer smtpServer=new SmtpServer(SMTP_SERVER_HOSTNAME,SMTP_SERVER_PORT);
+ //Authentication
+ smtpServer.SmtpAuthToken=new SmtpAuthToken(SMTP_SERVER_LOGIN, SMTP_SERVER_PASSWORD);
+ //Send Email Message
+ emailMessage.Send(smtpServer);
+ //Log
+ m_log.Info("[EMAIL] EMail sent to: " + address + " from object: " + objectID.ToString());
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[EMAIL] DefaultEmailModule Exception: "+e.Message);
+ return;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Email GetNextEmail(UUID objectID, string sender, string subject)
+ {
+ return null;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs
new file mode 100644
index 0000000..9f3bd09
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs
@@ -0,0 +1,437 @@
+/*
+ * 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 OpenSim 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.IO;
+using System.Net;
+using System.Text;
+using System.Threading;
+using OpenMetaverse;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Framework.Servers;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using System.Collections;
+
+/*****************************************************
+ *
+ * ScriptsHttpRequests
+ *
+ * Implements the llHttpRequest and http_response
+ * callback.
+ *
+ * Some stuff was already in LSLLongCmdHandler, and then
+ * there was this file with a stub class in it. So,
+ * I am moving some of the objects and functions out of
+ * LSLLongCmdHandler, such as the HttpRequestClass, the
+ * start and stop methods, and setting up pending and
+ * completed queues. These are processed in the
+ * LSLLongCmdHandler polling loop. Similiar to the
+ * XMLRPCModule, since that seems to work.
+ *
+ * //TODO
+ *
+ * This probably needs some throttling mechanism but
+ * it's wide open right now. This applies to both
+ * number of requests and data volume.
+ *
+ * Linden puts all kinds of header fields in the requests.
+ * Not doing any of that:
+ * User-Agent
+ * X-SecondLife-Shard
+ * X-SecondLife-Object-Name
+ * X-SecondLife-Object-Key
+ * X-SecondLife-Region
+ * X-SecondLife-Local-Position
+ * X-SecondLife-Local-Velocity
+ * X-SecondLife-Local-Rotation
+ * X-SecondLife-Owner-Name
+ * X-SecondLife-Owner-Key
+ *
+ * HTTPS support
+ *
+ * Configurable timeout?
+ * Configurable max response size?
+ * Configurable
+ *
+ * **************************************************/
+
+namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
+{
+ public class HttpRequestModule : IRegionModule, IHttpRequestModule
+ {
+ private object HttpListLock = new object();
+ private int httpTimeout = 30000;
+ private string m_name = "HttpScriptRequests";
+
+ private string m_proxyurl = "";
+ private string m_proxyexcepts = "";
+
+ //
+ private Dictionary m_pendingRequests;
+ private Scene m_scene;
+ // private Queue rpcQueue = new Queue();
+
+ public HttpRequestModule()
+ {
+ }
+
+ #region IHttpRequestModule Members
+
+ public UUID MakeHttpRequest(string url, string parameters, string body)
+ {
+ return UUID.Zero;
+ }
+
+ public UUID StartHttpRequest(uint localID, UUID itemID, string url, List parameters, Dictionary headers, string body)
+ {
+ UUID reqID = UUID.Random();
+ HttpRequestClass htc = new HttpRequestClass();
+
+ // Partial implementation: support for parameter flags needed
+ // see http://wiki.secondlife.com/wiki/LlHTTPRequest
+ //
+ // Parameters are expected in {key, value, ... , key, value}
+ if (parameters != null)
+ {
+ string[] parms = parameters.ToArray();
+ for (int i = 0; i < parms.Length; i += 2)
+ {
+ switch (Int32.Parse(parms[i]))
+ {
+ case (int)HttpRequestConstants.HTTP_METHOD:
+
+ htc.HttpMethod = parms[i + 1];
+ break;
+
+ case (int)HttpRequestConstants.HTTP_MIMETYPE:
+
+ htc.HttpMIMEType = parms[i + 1];
+ break;
+
+ case (int)HttpRequestConstants.HTTP_BODY_MAXLENGTH:
+
+ // TODO implement me
+ break;
+
+ case (int)HttpRequestConstants.HTTP_VERIFY_CERT:
+
+ // TODO implement me
+ break;
+ }
+ }
+ }
+
+ htc.LocalID = localID;
+ htc.ItemID = itemID;
+ htc.Url = url;
+ htc.ReqID = reqID;
+ htc.HttpTimeout = httpTimeout;
+ htc.OutboundBody = body;
+ htc.ResponseHeaders = headers;
+ htc.proxyurl = m_proxyurl;
+ htc.proxyexcepts = m_proxyexcepts;
+
+ lock (HttpListLock)
+ {
+ m_pendingRequests.Add(reqID, htc);
+ }
+
+ htc.Process();
+
+ return reqID;
+ }
+
+ public void StopHttpRequest(uint m_localID, UUID m_itemID)
+ {
+ if (m_pendingRequests != null)
+ {
+ lock (HttpListLock)
+ {
+ HttpRequestClass tmpReq;
+ if (m_pendingRequests.TryGetValue(m_itemID, out tmpReq))
+ {
+ tmpReq.Stop();
+ m_pendingRequests.Remove(m_itemID);
+ }
+ }
+ }
+ }
+
+ /*
+ * TODO
+ * Not sure how important ordering is is here - the next first
+ * one completed in the list is returned, based soley on its list
+ * position, not the order in which the request was started or
+ * finsihed. I thought about setting up a queue for this, but
+ * it will need some refactoring and this works 'enough' right now
+ */
+
+ public IServiceRequest GetNextCompletedRequest()
+ {
+ lock (HttpListLock)
+ {
+ foreach (UUID luid in m_pendingRequests.Keys)
+ {
+ HttpRequestClass tmpReq;
+
+ if (m_pendingRequests.TryGetValue(luid, out tmpReq))
+ {
+ if (tmpReq.Finished)
+ {
+ return tmpReq;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public void RemoveCompletedRequest(UUID id)
+ {
+ lock (HttpListLock)
+ {
+ HttpRequestClass tmpReq;
+ if (m_pendingRequests.TryGetValue(id, out tmpReq))
+ {
+ tmpReq.Stop();
+ tmpReq = null;
+ m_pendingRequests.Remove(id);
+ }
+ }
+ }
+
+ #endregion
+
+ #region IRegionModule Members
+
+ public void Initialise(Scene scene, IConfigSource config)
+ {
+ m_scene = scene;
+
+ m_scene.RegisterModuleInterface(this);
+
+ m_proxyurl = config.Configs["Startup"].GetString("HttpProxy");
+ m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions");
+
+ m_pendingRequests = new Dictionary();
+ }
+
+ public void PostInitialise()
+ {
+ }
+
+ public void Close()
+ {
+ }
+
+ public string Name
+ {
+ get { return m_name; }
+ }
+
+ public bool IsSharedModule
+ {
+ get { return true; }
+ }
+
+ #endregion
+ }
+
+ public class HttpRequestClass: IServiceRequest
+ {
+ // Constants for parameters
+ // public const int HTTP_BODY_MAXLENGTH = 2;
+ // public const int HTTP_METHOD = 0;
+ // public const int HTTP_MIMETYPE = 1;
+ // public const int HTTP_VERIFY_CERT = 3;
+ private bool _finished;
+ public bool Finished
+ {
+ get { return _finished; }
+ }
+ // public int HttpBodyMaxLen = 2048; // not implemented
+
+ // Parameter members and default values
+ public string HttpMethod = "GET";
+ public string HttpMIMEType = "text/plain;charset=utf-8";
+ public int HttpTimeout;
+ // public bool HttpVerifyCert = true; // not implemented
+ private Thread httpThread;
+
+ // Request info
+ private UUID _itemID;
+ public UUID ItemID
+ {
+ get { return _itemID; }
+ set { _itemID = value; }
+ }
+ private uint _localID;
+ public uint LocalID
+ {
+ get { return _localID; }
+ set { _localID = value; }
+ }
+ public DateTime Next;
+ public string proxyurl;
+ public string proxyexcepts;
+ public string OutboundBody;
+ private UUID _reqID;
+ public UUID ReqID
+ {
+ get { return _reqID; }
+ set { _reqID = value; }
+ }
+ public HttpWebRequest Request;
+ public string ResponseBody;
+ public List ResponseMetadata;
+ public Dictionary ResponseHeaders;
+ public int Status;
+ public string Url;
+
+ public void Process()
+ {
+ httpThread = new Thread(SendRequest);
+ httpThread.Name = "HttpRequestThread";
+ httpThread.Priority = ThreadPriority.BelowNormal;
+ httpThread.IsBackground = true;
+ _finished = false;
+ httpThread.Start();
+ ThreadTracker.Add(httpThread);
+ }
+
+ /*
+ * TODO: More work on the response codes. Right now
+ * returning 200 for success or 499 for exception
+ */
+
+ public void SendRequest()
+ {
+ HttpWebResponse response = null;
+ StringBuilder sb = new StringBuilder();
+ byte[] buf = new byte[8192];
+ string tempString = null;
+ int count = 0;
+
+ try
+ {
+ Request = (HttpWebRequest) WebRequest.Create(Url);
+ Request.Method = HttpMethod;
+ Request.ContentType = HttpMIMEType;
+
+ if (proxyurl != null && proxyurl.Length > 0)
+ {
+ if (proxyexcepts != null && proxyexcepts.Length > 0)
+ {
+ string[] elist = proxyexcepts.Split(';');
+ Request.Proxy = new WebProxy(proxyurl, true, elist);
+ }
+ else
+ {
+ Request.Proxy = new WebProxy(proxyurl, true);
+ }
+ }
+
+ foreach (KeyValuePair entry in ResponseHeaders)
+ Request.Headers[entry.Key] = entry.Value;
+
+ // Encode outbound data
+ if (OutboundBody.Length > 0)
+ {
+ byte[] data = Encoding.UTF8.GetBytes(OutboundBody);
+
+ Request.ContentLength = data.Length;
+ Stream bstream = Request.GetRequestStream();
+ bstream.Write(data, 0, data.Length);
+ bstream.Close();
+ }
+
+ Request.Timeout = HttpTimeout;
+ // execute the request
+ response = (HttpWebResponse) Request.GetResponse();
+
+ Stream resStream = response.GetResponseStream();
+
+ do
+ {
+ // fill the buffer with data
+ count = resStream.Read(buf, 0, buf.Length);
+
+ // make sure we read some data
+ if (count != 0)
+ {
+ // translate from bytes to ASCII text
+ tempString = Encoding.UTF8.GetString(buf, 0, count);
+
+ // continue building the string
+ sb.Append(tempString);
+ }
+ } while (count > 0); // any more data to read?
+
+ ResponseBody = sb.ToString();
+ }
+ catch (Exception e)
+ {
+ if (e is WebException && ((WebException)e).Status == WebExceptionStatus.ProtocolError)
+ {
+ HttpWebResponse webRsp = (HttpWebResponse)((WebException)e).Response;
+ Status = (int)webRsp.StatusCode;
+ ResponseBody = webRsp.StatusDescription;
+ }
+ else
+ {
+ Status = (int)OSHttpStatusCode.ClientErrorJoker;
+ ResponseBody = e.Message;
+ }
+
+ _finished = true;
+ return;
+ }
+ finally
+ {
+ if (response != null)
+ response.Close();
+ }
+
+ Status = (int)OSHttpStatusCode.SuccessOk;
+ _finished = true;
+ }
+
+ public void Stop()
+ {
+ try
+ {
+ httpThread.Abort();
+ }
+ catch (Exception)
+ {
+ }
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
new file mode 100644
index 0000000..afcaff1
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
@@ -0,0 +1,229 @@
+/*
+ * 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 OpenSim 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.Drawing;
+using System.IO;
+using System.Net;
+using OpenMetaverse;
+using OpenMetaverse.Imaging;
+using Nini.Config;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+
+namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
+{
+ public class LoadImageURLModule : IRegionModule, IDynamicTextureRender
+ {
+ private string m_name = "LoadImageURL";
+ private Scene m_scene;
+ private IDynamicTextureManager m_textureManager;
+
+ private string m_proxyurl = "";
+ private string m_proxyexcepts = "";
+
+ #region IDynamicTextureRender Members
+
+ public string GetName()
+ {
+ return m_name;
+ }
+
+ public string GetContentType()
+ {
+ return ("image");
+ }
+
+ public bool SupportsAsynchronous()
+ {
+ return true;
+ }
+
+ public byte[] ConvertUrl(string url, string extraParams)
+ {
+ return null;
+ }
+
+ public byte[] ConvertStream(Stream data, string extraParams)
+ {
+ return null;
+ }
+
+ public bool AsyncConvertUrl(UUID id, string url, string extraParams)
+ {
+ MakeHttpRequest(url, id);
+ return true;
+ }
+
+ public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
+ {
+ return false;
+ }
+
+ #endregion
+
+ #region IRegionModule Members
+
+ public void Initialise(Scene scene, IConfigSource config)
+ {
+ if (m_scene == null)
+ {
+ m_scene = scene;
+ }
+
+ m_proxyurl = config.Configs["Startup"].GetString("HttpProxy");
+ m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions");
+ }
+
+ public void PostInitialise()
+ {
+ m_textureManager = m_scene.RequestModuleInterface();
+ if (m_textureManager != null)
+ {
+ m_textureManager.RegisterRender(GetContentType(), this);
+ }
+ }
+
+ public void Close()
+ {
+ }
+
+ public string Name
+ {
+ get { return m_name; }
+ }
+
+ public bool IsSharedModule
+ {
+ get { return true; }
+ }
+
+ #endregion
+
+ private void MakeHttpRequest(string url, UUID requestID)
+ {
+ WebRequest request = HttpWebRequest.Create(url);
+
+ if (m_proxyurl != null && m_proxyurl.Length > 0)
+ {
+ if (m_proxyexcepts != null && m_proxyexcepts.Length > 0)
+ {
+ string[] elist = m_proxyexcepts.Split(';');
+ request.Proxy = new WebProxy(m_proxyurl, true, elist);
+ }
+ else
+ {
+ request.Proxy = new WebProxy(m_proxyurl, true);
+ }
+ }
+
+ RequestState state = new RequestState((HttpWebRequest) request, requestID);
+ // IAsyncResult result = request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state);
+ request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state);
+
+ TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
+ state.TimeOfRequest = (int) t.TotalSeconds;
+ }
+
+ private void HttpRequestReturn(IAsyncResult result)
+ {
+ RequestState state = (RequestState) result.AsyncState;
+ WebRequest request = (WebRequest) state.Request;
+ try
+ {
+ HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
+ if (response.StatusCode == HttpStatusCode.OK)
+ {
+ Bitmap image = new Bitmap(response.GetResponseStream());
+ Size newsize;
+
+ // TODO: make this a bit less hard coded
+ if ((image.Height < 64) && (image.Width < 64))
+ {
+ newsize = new Size(32, 32);
+ }
+ else if ((image.Height < 128) && (image.Width < 128))
+ {
+ newsize = new Size(64, 64);
+ }
+ else if ((image.Height < 256) && (image.Width < 256))
+ {
+ newsize = new Size(128, 128);
+ }
+ else if ((image.Height < 512 && image.Width < 512))
+ {
+ newsize = new Size(256, 256);
+ }
+ else if ((image.Height < 1024 && image.Width < 1024))
+ {
+ newsize = new Size(512, 512);
+ }
+ else
+ {
+ newsize = new Size(1024, 1024);
+ }
+
+ Bitmap resize = new Bitmap(image, newsize);
+ byte[] imageJ2000 = new byte[0];
+
+ try
+ {
+ imageJ2000 = OpenJPEG.EncodeFromImage(resize, true);
+ }
+ catch (Exception)
+ {
+ Console.WriteLine(
+ "[LOADIMAGEURLMODULE]: OpenJpeg Encode Failed. Empty byte data returned!");
+ }
+
+ m_textureManager.ReturnData(state.RequestID, imageJ2000);
+ }
+ }
+ catch (WebException)
+ {
+
+ }
+ }
+
+ #region Nested type: RequestState
+
+ public class RequestState
+ {
+ public HttpWebRequest Request = null;
+ public UUID RequestID = UUID.Zero;
+ public int TimeOfRequest = 0;
+
+ public RequestState(HttpWebRequest request, UUID requestID)
+ {
+ Request = request;
+ RequestID = requestID;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
new file mode 100644
index 0000000..0c709b5
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
@@ -0,0 +1,515 @@
+/*
+ * 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 OpenSim 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.Drawing;
+using System.Drawing.Imaging;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using OpenMetaverse;
+using OpenMetaverse.Imaging;
+using Nini.Config;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+
+//using Cairo;
+
+namespace OpenSim.Region.CoreModules.Scripting.VectorRender
+{
+ public class VectorRenderModule : IRegionModule, IDynamicTextureRender
+ {
+ private string m_name = "VectorRenderModule";
+ private Scene m_scene;
+ private IDynamicTextureManager m_textureManager;
+
+ public VectorRenderModule()
+ {
+ }
+
+ #region IDynamicTextureRender Members
+
+ public string GetContentType()
+ {
+ return ("vector");
+ }
+
+ public string GetName()
+ {
+ return m_name;
+ }
+
+ public bool SupportsAsynchronous()
+ {
+ return true;
+ }
+
+ public byte[] ConvertUrl(string url, string extraParams)
+ {
+ return null;
+ }
+
+ public byte[] ConvertStream(Stream data, string extraParams)
+ {
+ return null;
+ }
+
+ public bool AsyncConvertUrl(UUID id, string url, string extraParams)
+ {
+ return false;
+ }
+
+ public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
+ {
+ Draw(bodyData, id, extraParams);
+ return true;
+ }
+
+ #endregion
+
+ #region IRegionModule Members
+
+ public void Initialise(Scene scene, IConfigSource config)
+ {
+ if (m_scene == null)
+ {
+ m_scene = scene;
+ }
+ }
+
+ public void PostInitialise()
+ {
+ m_textureManager = m_scene.RequestModuleInterface();
+ if (m_textureManager != null)
+ {
+ m_textureManager.RegisterRender(GetContentType(), this);
+ }
+ }
+
+ public void Close()
+ {
+ }
+
+ public string Name
+ {
+ get { return m_name; }
+ }
+
+ public bool IsSharedModule
+ {
+ get { return true; }
+ }
+
+ #endregion
+
+ private void Draw(string data, UUID id, string extraParams)
+ {
+ // We need to cater for old scripts that didnt use extraParams neatly, they use either an integer size which represents both width and height, or setalpha
+ // we will now support multiple comma seperated params in the form width:256,height:512,alpha:255
+ int width = 256;
+ int height = 256;
+ int alpha = 255; // 0 is transparent
+
+ char[] paramDelimiter = { ',' };
+ char[] nvpDelimiter = { ':' };
+
+ extraParams = extraParams.Trim();
+ extraParams = extraParams.ToLower();
+
+ string[] nvps = extraParams.Split(paramDelimiter);
+
+ int temp = -1;
+ foreach (string pair in nvps)
+ {
+ string[] nvp = pair.Split(nvpDelimiter);
+ string name = "";
+ string value = "";
+
+ if (nvp[0] != null)
+ {
+ name = nvp[0].Trim();
+ }
+
+ if (nvp.Length == 2)
+ {
+ value = nvp[1].Trim();
+ }
+
+ switch (name)
+ {
+ case "width":
+ temp = parseIntParam(value);
+ if (temp != -1)
+ {
+ if (temp < 1)
+ {
+ width = 1;
+ }
+ else if (temp > 2048)
+ {
+ width = 2048;
+ }
+ else
+ {
+ width = temp;
+ }
+ }
+ break;
+ case "height":
+ temp = parseIntParam(value);
+ if (temp != -1)
+ {
+ if (temp < 1)
+ {
+ height = 1;
+ }
+ else if (temp > 2048)
+ {
+ height = 2048;
+ }
+ else
+ {
+ height = temp;
+ }
+ }
+ break;
+ case "alpha":
+ temp = parseIntParam(value);
+ if (temp != -1)
+ {
+ if (temp < 0)
+ {
+ alpha = 0;
+ }
+ else if (temp > 255)
+ {
+ alpha = 255;
+ }
+ else
+ {
+ alpha = temp;
+ }
+ }
+ break;
+ case "":
+ // blank string has been passed do nothing just use defaults
+ break;
+ default: // this is all for backwards compat, all a bit ugly hopfully can be removed in future
+ // could be either set alpha or just an int
+ if (name == "setalpha")
+ {
+ alpha = 0; // set the texture to have transparent background (maintains backwards compat)
+ }
+ else
+ {
+ // this function used to accept an int on its own that represented both
+ // width and height, this is to maintain backwards compat, could be removed
+ // but would break existing scripts
+ temp = parseIntParam(name);
+ if (temp != -1)
+ {
+ if (temp > 1024)
+ temp = 1024;
+
+ if (temp < 128)
+ temp = 128;
+
+ width = temp;
+ height = temp;
+ }
+ }
+ break;
+ }
+
+ }
+
+ Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
+
+ Graphics graph = Graphics.FromImage(bitmap);
+
+ // this is really just to save people filling the
+ // background white in their scripts, only do when fully opaque
+ if (alpha == 255)
+ {
+ graph.FillRectangle(new SolidBrush(Color.White), 0, 0, width, height);
+ }
+
+ for (int w = 0; w < bitmap.Width; w++)
+ {
+ for (int h = 0; h < bitmap.Height; h++)
+ {
+ bitmap.SetPixel(w, h, Color.FromArgb(alpha, bitmap.GetPixel(w, h)));
+ }
+ }
+
+
+ GDIDraw(data, graph);
+
+ byte[] imageJ2000 = new byte[0];
+
+ try
+ {
+ imageJ2000 = OpenJPEG.EncodeFromImage(bitmap, true);
+ }
+ catch (Exception)
+ {
+ Console.WriteLine(
+ "[VECTORRENDERMODULE]: OpenJpeg Encode Failed. Empty byte data returned!");
+ }
+ m_textureManager.ReturnData(id, imageJ2000);
+ }
+
+ private int parseIntParam(string strInt)
+ {
+ int parsed;
+ try
+ {
+ parsed = Convert.ToInt32(strInt);
+ }
+ catch (Exception)
+ {
+ //Ckrinke: Add a WriteLine to remove the warning about 'e' defined but not used
+ // Console.WriteLine("Problem with Draw. Please verify parameters." + e.ToString());
+ parsed = -1;
+ }
+
+ return parsed;
+
+ }
+
+
+/*
+ private void CairoDraw(string data, System.Drawing.Graphics graph)
+ {
+ using (Win32Surface draw = new Win32Surface(graph.GetHdc()))
+ {
+ Context contex = new Context(draw);
+
+ contex.Antialias = Antialias.None; //fastest method but low quality
+ contex.LineWidth = 7;
+ char[] lineDelimiter = { ';' };
+ char[] partsDelimiter = { ',' };
+ string[] lines = data.Split(lineDelimiter);
+
+ foreach (string line in lines)
+ {
+ string nextLine = line.Trim();
+
+ if (nextLine.StartsWith("MoveTO"))
+ {
+ float x = 0;
+ float y = 0;
+ GetParams(partsDelimiter, ref nextLine, ref x, ref y);
+ contex.MoveTo(x, y);
+ }
+ else if (nextLine.StartsWith("LineTo"))
+ {
+ float x = 0;
+ float y = 0;
+ GetParams(partsDelimiter, ref nextLine, ref x, ref y);
+ contex.LineTo(x, y);
+ contex.Stroke();
+ }
+ }
+ }
+ graph.ReleaseHdc();
+ }
+*/
+
+ private void GDIDraw(string data, Graphics graph)
+ {
+ Point startPoint = new Point(0, 0);
+ Point endPoint = new Point(0, 0);
+ Pen drawPen = new Pen(Color.Black, 7);
+ string fontName = "Arial";
+ float fontSize = 14;
+ Font myFont = new Font(fontName, fontSize);
+ SolidBrush myBrush = new SolidBrush(Color.Black);
+ char[] lineDelimiter = {';'};
+ char[] partsDelimiter = {','};
+ string[] lines = data.Split(lineDelimiter);
+
+ foreach (string line in lines)
+ {
+ string nextLine = line.Trim();
+ //replace with switch, or even better, do some proper parsing
+ if (nextLine.StartsWith("MoveTo"))
+ {
+ float x = 0;
+ float y = 0;
+ GetParams(partsDelimiter, ref nextLine, 6, ref x, ref y);
+ startPoint.X = (int) x;
+ startPoint.Y = (int) y;
+ }
+ else if (nextLine.StartsWith("LineTo"))
+ {
+ float x = 0;
+ float y = 0;
+ GetParams(partsDelimiter, ref nextLine, 6, ref x, ref y);
+ endPoint.X = (int) x;
+ endPoint.Y = (int) y;
+ graph.DrawLine(drawPen, startPoint, endPoint);
+ startPoint.X = endPoint.X;
+ startPoint.Y = endPoint.Y;
+ }
+ else if (nextLine.StartsWith("Text"))
+ {
+ nextLine = nextLine.Remove(0, 4);
+ nextLine = nextLine.Trim();
+ graph.DrawString(nextLine, myFont, myBrush, startPoint);
+ }
+ else if (nextLine.StartsWith("Image"))
+ {
+ float x = 0;
+ float y = 0;
+ GetParams(partsDelimiter, ref nextLine, 5, ref x, ref y);
+ endPoint.X = (int) x;
+ endPoint.Y = (int) y;
+ Image image = ImageHttpRequest(nextLine);
+ graph.DrawImage(image, (float) startPoint.X, (float) startPoint.Y, x, y);
+ startPoint.X += endPoint.X;
+ startPoint.Y += endPoint.Y;
+ }
+ else if (nextLine.StartsWith("Rectangle"))
+ {
+ float x = 0;
+ float y = 0;
+ GetParams(partsDelimiter, ref nextLine, 9, ref x, ref y);
+ endPoint.X = (int) x;
+ endPoint.Y = (int) y;
+ graph.DrawRectangle(drawPen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
+ startPoint.X += endPoint.X;
+ startPoint.Y += endPoint.Y;
+ }
+ else if (nextLine.StartsWith("FillRectangle"))
+ {
+ float x = 0;
+ float y = 0;
+ GetParams(partsDelimiter, ref nextLine, 13, ref x, ref y);
+ endPoint.X = (int) x;
+ endPoint.Y = (int) y;
+ graph.FillRectangle(myBrush, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
+ startPoint.X += endPoint.X;
+ startPoint.Y += endPoint.Y;
+ }
+ else if (nextLine.StartsWith("Ellipse"))
+ {
+ float x = 0;
+ float y = 0;
+ GetParams(partsDelimiter, ref nextLine, 7, ref x, ref y);
+ endPoint.X = (int) x;
+ endPoint.Y = (int) y;
+ graph.DrawEllipse(drawPen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
+ startPoint.X += endPoint.X;
+ startPoint.Y += endPoint.Y;
+ }
+ else if (nextLine.StartsWith("FontSize"))
+ {
+ nextLine = nextLine.Remove(0, 8);
+ nextLine = nextLine.Trim();
+ fontSize = Convert.ToSingle(nextLine, CultureInfo.InvariantCulture);
+ myFont = new Font(fontName, fontSize);
+ }
+ else if (nextLine.StartsWith("FontName"))
+ {
+ nextLine = nextLine.Remove(0, 8);
+ fontName = nextLine.Trim();
+ myFont = new Font(fontName, fontSize);
+ }
+ else if (nextLine.StartsWith("PenSize"))
+ {
+ nextLine = nextLine.Remove(0, 7);
+ nextLine = nextLine.Trim();
+ float size = Convert.ToSingle(nextLine, CultureInfo.InvariantCulture);
+ drawPen.Width = size;
+ }
+ else if (nextLine.StartsWith("PenColour"))
+ {
+ nextLine = nextLine.Remove(0, 9);
+ nextLine = nextLine.Trim();
+ int hex = 0;
+
+ Color newColour;
+ if (Int32.TryParse(nextLine, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out hex))
+ {
+ newColour = Color.FromArgb(hex);
+ }
+ else
+ {
+ // this doesn't fail, it just returns black if nothing is found
+ newColour = Color.FromName(nextLine);
+ }
+
+ myBrush.Color = newColour;
+ drawPen.Color = newColour;
+ }
+ }
+ }
+
+ private static void GetParams(char[] partsDelimiter, ref string line, int startLength, ref float x, ref float y)
+ {
+ line = line.Remove(0, startLength);
+ string[] parts = line.Split(partsDelimiter);
+ if (parts.Length == 2)
+ {
+ string xVal = parts[0].Trim();
+ string yVal = parts[1].Trim();
+ x = Convert.ToSingle(xVal, CultureInfo.InvariantCulture);
+ y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture);
+ }
+ else if (parts.Length > 2)
+ {
+ string xVal = parts[0].Trim();
+ string yVal = parts[1].Trim();
+ x = Convert.ToSingle(xVal, CultureInfo.InvariantCulture);
+ y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture);
+
+ line = "";
+ for (int i = 2; i < parts.Length; i++)
+ {
+ line = line + parts[i].Trim();
+ line = line + " ";
+ }
+ }
+ }
+
+ private Bitmap ImageHttpRequest(string url)
+ {
+ WebRequest request = HttpWebRequest.Create(url);
+//Ckrinke: Comment out for now as 'str' is unused. Bring it back into play later when it is used.
+//Ckrinke Stream str = null;
+ HttpWebResponse response = (HttpWebResponse) (request).GetResponse();
+ if (response.StatusCode == HttpStatusCode.OK)
+ {
+ Bitmap image = new Bitmap(response.GetResponseStream());
+ return image;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs
new file mode 100644
index 0000000..c363940
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs
@@ -0,0 +1,726 @@
+/*
+ * 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 OpenSim 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;
+using System.Collections.Generic;
+using OpenMetaverse;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+
+// using log4net;
+// using System.Reflection;
+
+
+/*****************************************************
+ *
+ * WorldCommModule
+ *
+ *
+ * Holding place for world comms - basically llListen
+ * function implementation.
+ *
+ * lLListen(integer channel, string name, key id, string msg)
+ * The name, id, and msg arguments specify the filtering
+ * criteria. You can pass the empty string
+ * (or NULL_KEY for id) for these to set a completely
+ * open filter; this causes the listen() event handler to be
+ * invoked for all chat on the channel. To listen only
+ * for chat spoken by a specific object or avatar,
+ * specify the name and/or id arguments. To listen
+ * only for a specific command, specify the
+ * (case-sensitive) msg argument. If msg is not empty,
+ * listener will only hear strings which are exactly equal
+ * to msg. You can also use all the arguments to establish
+ * the most restrictive filtering criteria.
+ *
+ * It might be useful for each listener to maintain a message
+ * digest, with a list of recent messages by UUID. This can
+ * be used to prevent in-world repeater loops. However, the
+ * linden functions do not have this capability, so for now
+ * thats the way it works.
+ * Instead it blocks messages originating from the same prim.
+ * (not Object!)
+ *
+ * For LSL compliance, note the following:
+ * (Tested again 1.21.1 on May 2, 2008)
+ * 1. 'id' has to be parsed into a UUID. None-UUID keys are
+ * to be replaced by the ZeroID key. (Well, TryParse does
+ * that for us.
+ * 2. Setting up an listen event from the same script, with the
+ * same filter settings (including step 1), returns the same
+ * handle as the original filter.
+ * 3. (TODO) handles should be script-local. Starting from 1.
+ * Might be actually easier to map the global handle into
+ * script-local handle in the ScriptEngine. Not sure if its
+ * worth the effort tho.
+ *
+ * **************************************************/
+
+namespace OpenSim.Region.CoreModules.Scripting.WorldComm
+{
+ public class WorldCommModule : IRegionModule, IWorldComm
+ {
+ // private static readonly ILog m_log =
+ // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private ListenerManager m_listenerManager;
+ private Queue m_pending;
+ private Queue m_pendingQ;
+ private Scene m_scene;
+ private int m_whisperdistance = 10;
+ private int m_saydistance = 30;
+ private int m_shoutdistance = 100;
+
+ #region IRegionModule Members
+
+ public void Initialise(Scene scene, IConfigSource config)
+ {
+ // wrap this in a try block so that defaults will work if
+ // the config file doesn't specify otherwise.
+ int maxlisteners = 1000;
+ int maxhandles = 64;
+ try
+ {
+ m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance);
+ m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance);
+ m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance);
+ maxlisteners = config.Configs["Chat"].GetInt("max_listens_per_region", maxlisteners);
+ maxhandles = config.Configs["Chat"].GetInt("max_listens_per_script", maxhandles);
+ }
+ catch (Exception)
+ {
+ }
+ if (maxlisteners < 1) maxlisteners = int.MaxValue;
+ if (maxhandles < 1) maxhandles = int.MaxValue;
+
+ m_scene = scene;
+ m_scene.RegisterModuleInterface(this);
+ m_listenerManager = new ListenerManager(maxlisteners, maxhandles);
+ m_scene.EventManager.OnChatFromClient += DeliverClientMessage;
+ m_scene.EventManager.OnChatBroadcast += DeliverClientMessage;
+ m_pendingQ = new Queue();
+ m_pending = Queue.Synchronized(m_pendingQ);
+ }
+
+ public void PostInitialise()
+ {
+ }
+
+ public void Close()
+ {
+ }
+
+ public string Name
+ {
+ get { return "WorldCommModule"; }
+ }
+
+ public bool IsSharedModule
+ {
+ get { return false; }
+ }
+
+ #endregion
+
+ #region IWorldComm Members
+
+ ///
+ /// Create a listen event callback with the specified filters.
+ /// The parameters localID,itemID are needed to uniquely identify
+ /// the script during 'peek' time. Parameter hostID is needed to
+ /// determine the position of the script.
+ ///
+ /// localID of the script engine
+ /// UUID of the script engine
+ /// UUID of the SceneObjectPart
+ /// channel to listen on
+ /// name to filter on
+ /// key to filter on (user given, could be totally faked)
+ /// msg to filter on
+ /// number of the scripts handle
+ public int Listen(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg)
+ {
+ return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg);
+ }
+
+ ///
+ /// Sets the listen event with handle as active (active = TRUE) or inactive (active = FALSE).
+ /// The handle used is returned from Listen()
+ ///
+ /// UUID of the script engine
+ /// handle returned by Listen()
+ /// temp. activate or deactivate the Listen()
+ public void ListenControl(UUID itemID, int handle, int active)
+ {
+ if (active == 1)
+ m_listenerManager.Activate(itemID, handle);
+ else if (active == 0)
+ m_listenerManager.Dectivate(itemID, handle);
+ }
+
+ ///
+ /// Removes the listen event callback with handle
+ ///
+ /// UUID of the script engine
+ /// handle returned by Listen()
+ public void ListenRemove(UUID itemID, int handle)
+ {
+ m_listenerManager.Remove(itemID, handle);
+ }
+
+ ///
+ /// Removes all listen event callbacks for the given itemID
+ /// (script engine)
+ ///
+ /// UUID of the script engine
+ public void DeleteListener(UUID itemID)
+ {
+ m_listenerManager.DeleteListener(itemID);
+ }
+
+
+ protected static Vector3 CenterOfRegion = new Vector3(128, 128, 20);
+
+ public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg)
+ {
+ Vector3 position;
+ SceneObjectPart source;
+ ScenePresence avatar;
+
+ if ((source = m_scene.GetSceneObjectPart(id)) != null)
+ position = source.AbsolutePosition;
+ else if ((avatar = m_scene.GetScenePresence(id)) != null)
+ position = avatar.AbsolutePosition;
+ else if (ChatTypeEnum.Region == type)
+ position = CenterOfRegion;
+ else
+ return;
+
+ DeliverMessage(type, channel, name, id, msg, position);
+ }
+
+ ///
+ /// This method scans over the objects which registered an interest in listen callbacks.
+ /// For everyone it finds, it checks if it fits the given filter. If it does, then
+ /// enqueue the message for delivery to the objects listen event handler.
+ /// The enqueued ListenerInfo no longer has filter values, but the actually trigged values.
+ /// Objects that do an llSay have their messages delivered here and for nearby avatars,
+ /// the OnChatFromClient event is used.
+ ///
+ /// type of delvery (whisper,say,shout or regionwide)
+ /// channel to sent on
+ /// name of sender (object or avatar)
+ /// key of sender (object or avatar)
+ /// msg to sent
+ public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg, Vector3 position)
+ {
+ // m_log.DebugFormat("[WorldComm] got[2] type {0}, channel {1}, name {2}, id {3}, msg {4}",
+ // type, channel, name, id, msg);
+
+ // Determine which listen event filters match the given set of arguments, this results
+ // in a limited set of listeners, each belonging a host. If the host is in range, add them
+ // to the pending queue.
+ foreach (ListenerInfo li in m_listenerManager.GetListeners(UUID.Zero, channel, name, id, msg))
+ {
+ // Dont process if this message is from yourself!
+ if (li.GetHostID().Equals(id))
+ continue;
+
+ SceneObjectPart sPart = m_scene.GetSceneObjectPart(li.GetHostID());
+ if (sPart == null)
+ continue;
+
+ double dis = Util.GetDistanceTo(sPart.AbsolutePosition, position);
+ switch (type)
+ {
+ case ChatTypeEnum.Whisper:
+ if (dis < m_whisperdistance)
+ {
+ lock (m_pending.SyncRoot)
+ {
+ m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
+ }
+ }
+ break;
+
+ case ChatTypeEnum.Say:
+ if (dis < m_saydistance)
+ {
+ lock (m_pending.SyncRoot)
+ {
+ m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
+ }
+ }
+ break;
+
+ case ChatTypeEnum.Shout:
+ if (dis < m_shoutdistance)
+ {
+ lock (m_pending.SyncRoot)
+ {
+ m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
+ }
+ }
+ break;
+
+ case ChatTypeEnum.Region:
+ lock (m_pending.SyncRoot)
+ {
+ m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
+ }
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Are there any listen events ready to be dispatched?
+ ///
+ /// boolean indication
+ public bool HasMessages()
+ {
+ return (m_pending.Count > 0);
+ }
+
+ ///
+ /// Pop the first availlable listen event from the queue
+ ///
+ /// ListenerInfo with filter filled in
+ public IWorldCommListenerInfo GetNextMessage()
+ {
+ ListenerInfo li = null;
+
+ lock (m_pending.SyncRoot)
+ {
+ li = (ListenerInfo) m_pending.Dequeue();
+ }
+
+ return li;
+ }
+
+ #endregion
+
+ /********************************************************************
+ *
+ * Listener Stuff
+ *
+ * *****************************************************************/
+
+ private void DeliverClientMessage(Object sender, OSChatMessage e)
+ {
+ if (null != e.Sender)
+ DeliverMessage(e.Type, e.Channel, e.Sender.Name, e.Sender.AgentId, e.Message, e.Position);
+ else
+ DeliverMessage(e.Type, e.Channel, e.From, UUID.Zero, e.Message, e.Position);
+ }
+
+ public Object[] GetSerializationData(UUID itemID)
+ {
+ return m_listenerManager.GetSerializationData(itemID);
+ }
+
+ public void CreateFromData(uint localID, UUID itemID, UUID hostID,
+ Object[] data)
+ {
+ m_listenerManager.AddFromData(localID, itemID, hostID, data);
+ }
+ }
+
+ public class ListenerManager
+ {
+ private Dictionary> m_listeners = new Dictionary>();
+ private int m_maxlisteners;
+ private int m_maxhandles;
+ private int m_curlisteners;
+
+ public ListenerManager(int maxlisteners, int maxhandles)
+ {
+ m_maxlisteners = maxlisteners;
+ m_maxhandles = maxhandles;
+ m_curlisteners = 0;
+ }
+
+ public int AddListener(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg)
+ {
+ // do we already have a match on this particular filter event?
+ List coll = GetListeners(itemID, channel, name, id, msg);
+
+ if (coll.Count > 0)
+ {
+ // special case, called with same filter settings, return same handle
+ // (2008-05-02, tested on 1.21.1 server, still holds)
+ return coll[0].GetHandle();
+ }
+
+ if (m_curlisteners < m_maxlisteners)
+ {
+ int newHandle = GetNewHandle(itemID);
+
+ if (newHandle > 0)
+ {
+ ListenerInfo li = new ListenerInfo(newHandle, localID, itemID, hostID, channel, name, id, msg);
+
+ lock (m_listeners)
+ {
+ List listeners;
+ if (!m_listeners.TryGetValue(channel,out listeners))
+ {
+ listeners = new List();
+ m_listeners.Add(channel, listeners);
+ }
+ listeners.Add(li);
+ m_curlisteners++;
+ }
+
+ return newHandle;
+ }
+ }
+ return -1;
+ }
+
+ public void Remove(UUID itemID, int handle)
+ {
+ lock (m_listeners)
+ {
+ foreach (KeyValuePair> lis in m_listeners)
+ {
+ foreach (ListenerInfo li in lis.Value)
+ {
+ if (li.GetItemID().Equals(itemID) && li.GetHandle().Equals(handle))
+ {
+ lis.Value.Remove(li);
+ if (lis.Value.Count == 0)
+ {
+ m_listeners.Remove(lis.Key);
+ m_curlisteners--;
+ }
+ // there should be only one, so we bail out early
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ public void DeleteListener(UUID itemID)
+ {
+ List emptyChannels = new List();
+ List removedListeners = new List();
+
+ lock (m_listeners)
+ {
+ foreach (KeyValuePair> lis in m_listeners)
+ {
+ foreach (ListenerInfo li in lis.Value)
+ {
+ if (li.GetItemID().Equals(itemID))
+ {
+ // store them first, else the enumerated bails on us
+ removedListeners.Add(li);
+ }
+ }
+ foreach (ListenerInfo li in removedListeners)
+ {
+ lis.Value.Remove(li);
+ m_curlisteners--;
+ }
+ removedListeners.Clear();
+ if (lis.Value.Count == 0)
+ {
+ // again, store first, remove later
+ emptyChannels.Add(lis.Key);
+ }
+ }
+ foreach (int channel in emptyChannels)
+ {
+ m_listeners.Remove(channel);
+ }
+ }
+ }
+
+ public void Activate(UUID itemID, int handle)
+ {
+ lock (m_listeners)
+ {
+ foreach (KeyValuePair> lis in m_listeners)
+ {
+ foreach (ListenerInfo li in lis.Value)
+ {
+ if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle)
+ {
+ li.Activate();
+ // only one, bail out
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ public void Dectivate(UUID itemID, int handle)
+ {
+ lock (m_listeners)
+ {
+ foreach (KeyValuePair> lis in m_listeners)
+ {
+ foreach (ListenerInfo li in lis.Value)
+ {
+ if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle)
+ {
+ li.Deactivate();
+ // only one, bail out
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // non-locked access, since its always called in the context of the lock
+ private int GetNewHandle(UUID itemID)
+ {
+ List handles = new List();
+
+ // build a list of used keys for this specific itemID...
+ foreach (KeyValuePair> lis in m_listeners)
+ {
+ foreach (ListenerInfo li in lis.Value)
+ {
+ if (li.GetItemID().Equals(itemID))
+ handles.Add(li.GetHandle());
+ }
+ }
+
+ // Note: 0 is NOT a valid handle for llListen() to return
+ for (int i = 1; i <= m_maxhandles; i++)
+ {
+ if (!handles.Contains(i))
+ return i;
+ }
+
+ return -1;
+ }
+
+ // Theres probably a more clever and efficient way to
+ // do this, maybe with regex.
+ // PM2008: Ha, one could even be smart and define a specialized Enumerator.
+ public List GetListeners(UUID itemID, int channel, string name, UUID id, string msg)
+ {
+ List collection = new List();
+
+ lock (m_listeners)
+ {
+ List listeners;
+ if (!m_listeners.TryGetValue(channel,out listeners))
+ {
+ return collection;
+ }
+
+ foreach (ListenerInfo li in listeners)
+ {
+ if (!li.IsActive())
+ {
+ continue;
+ }
+ if (!itemID.Equals(UUID.Zero) && !li.GetItemID().Equals(itemID))
+ {
+ continue;
+ }
+ if (li.GetName().Length > 0 && !li.GetName().Equals(name))
+ {
+ continue;
+ }
+ if (!li.GetID().Equals(UUID.Zero) && !li.GetID().Equals(id))
+ {
+ continue;
+ }
+ if (li.GetMessage().Length > 0 && !li.GetMessage().Equals(msg))
+ {
+ continue;
+ }
+ collection.Add(li);
+ }
+ }
+ return collection;
+ }
+
+ public Object[] GetSerializationData(UUID itemID)
+ {
+ List data = new List();
+
+ foreach (List list in m_listeners.Values)
+ {
+ foreach (ListenerInfo l in list)
+ {
+ if (l.GetItemID() == itemID)
+ data.AddRange(l.GetSerializationData());
+ }
+ }
+ return (Object[])data.ToArray();
+ }
+
+ public void AddFromData(uint localID, UUID itemID, UUID hostID,
+ Object[] data)
+ {
+ int idx = 0;
+ Object[] item = new Object[6];
+
+ while (idx < data.Length)
+ {
+ Array.Copy(data, idx, item, 0, 6);
+
+ ListenerInfo info =
+ ListenerInfo.FromData(localID, itemID, hostID, item);
+
+ if (!m_listeners.ContainsKey((int)item[2]))
+ m_listeners.Add((int)item[2], new List());
+ m_listeners[(int)item[2]].Add(info);
+
+ idx+=6;
+ }
+ }
+ }
+
+ public class ListenerInfo: IWorldCommListenerInfo
+ {
+ private bool m_active; // Listener is active or not
+ private int m_handle; // Assigned handle of this listener
+ private uint m_localID; // Local ID from script engine
+ private UUID m_itemID; // ID of the host script engine
+ private UUID m_hostID; // ID of the host/scene part
+ private int m_channel; // Channel
+ private UUID m_id; // ID to filter messages from
+ private string m_name; // Object name to filter messages from
+ private string m_message; // The message
+
+ public ListenerInfo(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, UUID id, string message)
+ {
+ Initialise(handle, localID, ItemID, hostID, channel, name, id, message);
+ }
+
+ public ListenerInfo(ListenerInfo li, string name, UUID id, string message)
+ {
+ Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message);
+ }
+
+ private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name,
+ UUID id, string message)
+ {
+ m_active = true;
+ m_handle = handle;
+ m_localID = localID;
+ m_itemID = ItemID;
+ m_hostID = hostID;
+ m_channel = channel;
+ m_name = name;
+ m_id = id;
+ m_message = message;
+ }
+
+ public Object[] GetSerializationData()
+ {
+ Object[] data = new Object[6];
+
+ data[0] = m_active;
+ data[1] = m_handle;
+ data[2] = m_channel;
+ data[3] = m_name;
+ data[4] = m_id;
+ data[5] = m_message;
+
+ return data;
+ }
+
+ public static ListenerInfo FromData(uint localID, UUID ItemID, UUID hostID, Object[] data)
+ {
+ ListenerInfo linfo = new ListenerInfo((int)data[1], localID,
+ ItemID, hostID, (int)data[2], (string)data[3],
+ (UUID)data[4], (string)data[5]);
+ linfo.m_active=(bool)data[0];
+
+ return linfo;
+ }
+
+ public UUID GetItemID()
+ {
+ return m_itemID;
+ }
+
+ public UUID GetHostID()
+ {
+ return m_hostID;
+ }
+
+ public int GetChannel()
+ {
+ return m_channel;
+ }
+
+ public uint GetLocalID()
+ {
+ return m_localID;
+ }
+
+ public int GetHandle()
+ {
+ return m_handle;
+ }
+
+ public string GetMessage()
+ {
+ return m_message;
+ }
+
+ public string GetName()
+ {
+ return m_name;
+ }
+
+ public bool IsActive()
+ {
+ return m_active;
+ }
+
+ public void Deactivate()
+ {
+ m_active = false;
+ }
+
+ public void Activate()
+ {
+ m_active = true;
+ }
+
+ public UUID GetID()
+ {
+ return m_id;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs b/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs
new file mode 100644
index 0000000..942c130
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs
@@ -0,0 +1,726 @@
+/*
+ * 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 OpenSim 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;
+using System.Collections.Generic;
+using System.Net;
+using System.Reflection;
+using System.Threading;
+using OpenMetaverse;
+using log4net;
+using Nini.Config;
+using Nwc.XmlRpc;
+using OpenSim.Framework;
+using OpenSim.Framework.Servers;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+
+/*****************************************************
+ *
+ * XMLRPCModule
+ *
+ * Module for accepting incoming communications from
+ * external XMLRPC client and calling a remote data
+ * procedure for a registered data channel/prim.
+ *
+ *
+ * 1. On module load, open a listener port
+ * 2. Attach an XMLRPC handler
+ * 3. When a request is received:
+ * 3.1 Parse into components: channel key, int, string
+ * 3.2 Look up registered channel listeners
+ * 3.3 Call the channel (prim) remote data method
+ * 3.4 Capture the response (llRemoteDataReply)
+ * 3.5 Return response to client caller
+ * 3.6 If no response from llRemoteDataReply within
+ * RemoteReplyScriptTimeout, generate script timeout fault
+ *
+ * Prims in script must:
+ * 1. Open a remote data channel
+ * 1.1 Generate a channel ID
+ * 1.2 Register primid,channelid pair with module
+ * 2. Implement the remote data procedure handler
+ *
+ * llOpenRemoteDataChannel
+ * llRemoteDataReply
+ * remote_data(integer type, key channel, key messageid, string sender, integer ival, string sval)
+ * llCloseRemoteDataChannel
+ *
+ * **************************************************/
+
+namespace OpenSim.Region.CoreModules.Scripting.XMLRPC
+{
+ public class XMLRPCModule : IRegionModule, IXMLRPC
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private string m_name = "XMLRPCModule";
+
+ //
+ private Dictionary m_openChannels;
+ private Dictionary m_pendingSRDResponses;
+ private int m_remoteDataPort = 0;
+
+ private Dictionary m_rpcPending;
+ private Dictionary m_rpcPendingResponses;
+ private List m_scenes = new List();
+ private int RemoteReplyScriptTimeout = 9000;
+ private int RemoteReplyScriptWait = 300;
+ private object XMLRPCListLock = new object();
+
+ #region IRegionModule Members
+
+ public void Initialise(Scene scene, IConfigSource config)
+ {
+ // We need to create these early because the scripts might be calling
+ // But since this gets called for every region, we need to make sure they
+ // get called only one time (or we lose any open channels)
+ if (null == m_openChannels)
+ {
+ m_openChannels = new Dictionary();
+ m_rpcPending = new Dictionary();
+ m_rpcPendingResponses = new Dictionary();
+ m_pendingSRDResponses = new Dictionary();
+
+ try
+ {
+ m_remoteDataPort = config.Configs["Network"].GetInt("remoteDataPort", m_remoteDataPort);
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ if (!m_scenes.Contains(scene))
+ {
+ m_scenes.Add(scene);
+
+ scene.RegisterModuleInterface(this);
+ }
+ }
+
+ public void PostInitialise()
+ {
+ if (IsEnabled())
+ {
+ // Start http server
+ // Attach xmlrpc handlers
+ m_log.Info("[REMOTE_DATA]: " +
+ "Starting XMLRPC Server on port " + m_remoteDataPort + " for llRemoteData commands.");
+ BaseHttpServer httpServer = new BaseHttpServer((uint) m_remoteDataPort);
+ httpServer.AddXmlRPCHandler("llRemoteData", XmlRpcRemoteData);
+ httpServer.Start();
+ }
+ }
+
+ public void Close()
+ {
+ }
+
+ public string Name
+ {
+ get { return m_name; }
+ }
+
+ public bool IsSharedModule
+ {
+ get { return true; }
+ }
+
+ #endregion
+
+ #region IXMLRPC Members
+
+ public bool IsEnabled()
+ {
+ return (m_remoteDataPort > 0);
+ }
+
+ /**********************************************
+ * OpenXMLRPCChannel
+ *
+ * Generate a UUID channel key and add it and
+ * the prim id to dictionary
+ *
+ * A custom channel key can be proposed.
+ * Otherwise, passing UUID.Zero will generate
+ * and return a random channel
+ *
+ * First check if there is a channel assigned for
+ * this itemID. If there is, then someone called
+ * llOpenRemoteDataChannel twice. Just return the
+ * original channel. Other option is to delete the
+ * current channel and assign a new one.
+ *
+ * ********************************************/
+
+ public UUID OpenXMLRPCChannel(uint localID, UUID itemID, UUID channelID)
+ {
+ UUID newChannel = UUID.Zero;
+
+ // This should no longer happen, but the check is reasonable anyway
+ if (null == m_openChannels)
+ {
+ m_log.Warn("[RemoteDataReply] Attempt to open channel before initialization is complete");
+ return newChannel;
+ }
+
+ //Is a dupe?
+ foreach (RPCChannelInfo ci in m_openChannels.Values)
+ {
+ if (ci.GetItemID().Equals(itemID))
+ {
+ // return the original channel ID for this item
+ newChannel = ci.GetChannelID();
+ break;
+ }
+ }
+
+ if (newChannel == UUID.Zero)
+ {
+ newChannel = (channelID == UUID.Zero) ? UUID.Random() : channelID;
+ RPCChannelInfo rpcChanInfo = new RPCChannelInfo(localID, itemID, newChannel);
+ lock (XMLRPCListLock)
+ {
+ m_openChannels.Add(newChannel, rpcChanInfo);
+ }
+ }
+
+ return newChannel;
+ }
+
+ // Delete channels based on itemID
+ // for when a script is deleted
+ public void DeleteChannels(UUID itemID)
+ {
+ if (m_openChannels != null)
+ {
+ ArrayList tmp = new ArrayList();
+
+ lock (XMLRPCListLock)
+ {
+ foreach (RPCChannelInfo li in m_openChannels.Values)
+ {
+ if (li.GetItemID().Equals(itemID))
+ {
+ tmp.Add(itemID);
+ }
+ }
+
+ IEnumerator tmpEnumerator = tmp.GetEnumerator();
+ while (tmpEnumerator.MoveNext())
+ m_openChannels.Remove((UUID) tmpEnumerator.Current);
+ }
+ }
+ }
+
+ /**********************************************
+ * Remote Data Reply
+ *
+ * Response to RPC message
+ *
+ *********************************************/
+
+ public void RemoteDataReply(string channel, string message_id, string sdata, int idata)
+ {
+ UUID message_key = new UUID(message_id);
+ UUID channel_key = new UUID(channel);
+
+ RPCRequestInfo rpcInfo = null;
+
+ if (message_key == UUID.Zero)
+ {
+ foreach (RPCRequestInfo oneRpcInfo in m_rpcPendingResponses.Values)
+ if (oneRpcInfo.GetChannelKey() == channel_key)
+ rpcInfo = oneRpcInfo;
+ }
+ else
+ {
+ m_rpcPendingResponses.TryGetValue(message_key, out rpcInfo);
+ }
+
+ if (rpcInfo != null)
+ {
+ rpcInfo.SetStrRetval(sdata);
+ rpcInfo.SetIntRetval(idata);
+ rpcInfo.SetProcessed(true);
+ m_rpcPendingResponses.Remove(message_key);
+ }
+ else
+ {
+ m_log.Warn("[RemoteDataReply]: Channel or message_id not found");
+ }
+ }
+
+ /**********************************************
+ * CloseXMLRPCChannel
+ *
+ * Remove channel from dictionary
+ *
+ *********************************************/
+
+ public void CloseXMLRPCChannel(UUID channelKey)
+ {
+ if (m_openChannels.ContainsKey(channelKey))
+ m_openChannels.Remove(channelKey);
+ }
+
+
+ public bool hasRequests()
+ {
+ lock (XMLRPCListLock)
+ {
+ if (m_rpcPending != null)
+ return (m_rpcPending.Count > 0);
+ else
+ return false;
+ }
+ }
+
+ public IXmlRpcRequestInfo GetNextCompletedRequest()
+ {
+ if (m_rpcPending != null)
+ {
+ lock (XMLRPCListLock)
+ {
+ foreach (UUID luid in m_rpcPending.Keys)
+ {
+ RPCRequestInfo tmpReq;
+
+ if (m_rpcPending.TryGetValue(luid, out tmpReq))
+ {
+ if (!tmpReq.IsProcessed()) return tmpReq;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public void RemoveCompletedRequest(UUID id)
+ {
+ lock (XMLRPCListLock)
+ {
+ RPCRequestInfo tmp;
+ if (m_rpcPending.TryGetValue(id, out tmp))
+ {
+ m_rpcPending.Remove(id);
+ m_rpcPendingResponses.Add(id, tmp);
+ }
+ else
+ {
+ Console.WriteLine("UNABLE TO REMOVE COMPLETED REQUEST");
+ }
+ }
+ }
+
+ public UUID SendRemoteData(uint localID, UUID itemID, string channel, string dest, int idata, string sdata)
+ {
+ SendRemoteDataRequest req = new SendRemoteDataRequest(
+ localID, itemID, channel, dest, idata, sdata
+ );
+ m_pendingSRDResponses.Add(req.GetReqID(), req);
+ req.Process();
+ return req.ReqID;
+ }
+
+ public IServiceRequest GetNextCompletedSRDRequest()
+ {
+ if (m_pendingSRDResponses != null)
+ {
+ lock (XMLRPCListLock)
+ {
+ foreach (UUID luid in m_pendingSRDResponses.Keys)
+ {
+ SendRemoteDataRequest tmpReq;
+
+ if (m_pendingSRDResponses.TryGetValue(luid, out tmpReq))
+ {
+ if (tmpReq.Finished)
+ return tmpReq;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public void RemoveCompletedSRDRequest(UUID id)
+ {
+ lock (XMLRPCListLock)
+ {
+ SendRemoteDataRequest tmpReq;
+ if (m_pendingSRDResponses.TryGetValue(id, out tmpReq))
+ {
+ m_pendingSRDResponses.Remove(id);
+ }
+ }
+ }
+
+ public void CancelSRDRequests(UUID itemID)
+ {
+ if (m_pendingSRDResponses != null)
+ {
+ lock (XMLRPCListLock)
+ {
+ foreach (SendRemoteDataRequest li in m_pendingSRDResponses.Values)
+ {
+ if (li.ItemID.Equals(itemID))
+ m_pendingSRDResponses.Remove(li.GetReqID());
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ public XmlRpcResponse XmlRpcRemoteData(XmlRpcRequest request)
+ {
+ XmlRpcResponse response = new XmlRpcResponse();
+
+ Hashtable requestData = (Hashtable) request.Params[0];
+ bool GoodXML = (requestData.Contains("Channel") && requestData.Contains("IntValue") &&
+ requestData.Contains("StringValue"));
+
+ if (GoodXML)
+ {
+ UUID channel = new UUID((string) requestData["Channel"]);
+ RPCChannelInfo rpcChanInfo;
+ if (m_openChannels.TryGetValue(channel, out rpcChanInfo))
+ {
+ string intVal = Convert.ToInt32(requestData["IntValue"]).ToString();
+ string strVal = (string) requestData["StringValue"];
+
+ RPCRequestInfo rpcInfo;
+
+ lock (XMLRPCListLock)
+ {
+ rpcInfo =
+ new RPCRequestInfo(rpcChanInfo.GetLocalID(), rpcChanInfo.GetItemID(), channel, strVal,
+ intVal);
+ m_rpcPending.Add(rpcInfo.GetMessageID(), rpcInfo);
+ }
+
+ int timeoutCtr = 0;
+
+ while (!rpcInfo.IsProcessed() && (timeoutCtr < RemoteReplyScriptTimeout))
+ {
+ Thread.Sleep(RemoteReplyScriptWait);
+ timeoutCtr += RemoteReplyScriptWait;
+ }
+ if (rpcInfo.IsProcessed())
+ {
+ Hashtable param = new Hashtable();
+ param["StringValue"] = rpcInfo.GetStrRetval();
+ param["IntValue"] = rpcInfo.GetIntRetval();
+
+ ArrayList parameters = new ArrayList();
+ parameters.Add(param);
+
+ response.Value = parameters;
+ rpcInfo = null;
+ }
+ else
+ {
+ response.SetFault(-1, "Script timeout");
+ rpcInfo = null;
+ }
+ }
+ else
+ {
+ response.SetFault(-1, "Invalid channel");
+ }
+ }
+
+ return response;
+ }
+ }
+
+ public class RPCRequestInfo: IXmlRpcRequestInfo
+ {
+ private UUID m_ChannelKey;
+ private string m_IntVal;
+ private UUID m_ItemID;
+ private uint m_localID;
+ private UUID m_MessageID;
+ private bool m_processed;
+ private int m_respInt;
+ private string m_respStr;
+ private string m_StrVal;
+
+ public RPCRequestInfo(uint localID, UUID itemID, UUID channelKey, string strVal, string intVal)
+ {
+ m_localID = localID;
+ m_StrVal = strVal;
+ m_IntVal = intVal;
+ m_ItemID = itemID;
+ m_ChannelKey = channelKey;
+ m_MessageID = UUID.Random();
+ m_processed = false;
+ m_respStr = String.Empty;
+ m_respInt = 0;
+ }
+
+ public bool IsProcessed()
+ {
+ return m_processed;
+ }
+
+ public UUID GetChannelKey()
+ {
+ return m_ChannelKey;
+ }
+
+ public void SetProcessed(bool processed)
+ {
+ m_processed = processed;
+ }
+
+ public void SetStrRetval(string resp)
+ {
+ m_respStr = resp;
+ }
+
+ public string GetStrRetval()
+ {
+ return m_respStr;
+ }
+
+ public void SetIntRetval(int resp)
+ {
+ m_respInt = resp;
+ }
+
+ public int GetIntRetval()
+ {
+ return m_respInt;
+ }
+
+ public uint GetLocalID()
+ {
+ return m_localID;
+ }
+
+ public UUID GetItemID()
+ {
+ return m_ItemID;
+ }
+
+ public string GetStrVal()
+ {
+ return m_StrVal;
+ }
+
+ public int GetIntValue()
+ {
+ return int.Parse(m_IntVal);
+ }
+
+ public UUID GetMessageID()
+ {
+ return m_MessageID;
+ }
+ }
+
+ public class RPCChannelInfo
+ {
+ private UUID m_ChannelKey;
+ private UUID m_itemID;
+ private uint m_localID;
+
+ public RPCChannelInfo(uint localID, UUID itemID, UUID channelID)
+ {
+ m_ChannelKey = channelID;
+ m_localID = localID;
+ m_itemID = itemID;
+ }
+
+ public UUID GetItemID()
+ {
+ return m_itemID;
+ }
+
+ public UUID GetChannelID()
+ {
+ return m_ChannelKey;
+ }
+
+ public uint GetLocalID()
+ {
+ return m_localID;
+ }
+ }
+
+ public class SendRemoteDataRequest: IServiceRequest
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ public string Channel;
+ public string DestURL;
+ private bool _finished;
+ public bool Finished
+ {
+ get { return _finished; }
+ set { _finished = value; }
+ }
+ private Thread httpThread;
+ public int Idata;
+ private UUID _itemID;
+ public UUID ItemID
+ {
+ get { return _itemID; }
+ set { _itemID = value; }
+ }
+ private uint _localID;
+ public uint LocalID
+ {
+ get { return _localID; }
+ set { _localID = value; }
+ }
+ private UUID _reqID;
+ public UUID ReqID
+ {
+ get { return _reqID; }
+ set { _reqID = value; }
+ }
+ public XmlRpcRequest Request;
+ public int ResponseIdata;
+ public string ResponseSdata;
+ public string Sdata;
+
+ public SendRemoteDataRequest(uint localID, UUID itemID, string channel, string dest, int idata, string sdata)
+ {
+ this.Channel = channel;
+ DestURL = dest;
+ this.Idata = idata;
+ this.Sdata = sdata;
+ ItemID = itemID;
+ LocalID = localID;
+
+ ReqID = UUID.Random();
+ }
+
+ public void Process()
+ {
+ httpThread = new Thread(SendRequest);
+ httpThread.Name = "HttpRequestThread";
+ httpThread.Priority = ThreadPriority.BelowNormal;
+ httpThread.IsBackground = true;
+ _finished = false;
+ httpThread.Start();
+ ThreadTracker.Add(httpThread);
+ }
+
+ /*
+ * TODO: More work on the response codes. Right now
+ * returning 200 for success or 499 for exception
+ */
+
+ public void SendRequest()
+ {
+ Hashtable param = new Hashtable();
+
+ // Check if channel is an UUID
+ // if not, use as method name
+ UUID parseUID;
+ string mName = "llRemoteData";
+ if ((Channel != null) && (Channel != ""))
+ if (!UUID.TryParse(Channel, out parseUID))
+ mName = Channel;
+ else
+ param["Channel"] = Channel;
+
+ param["StringValue"] = Sdata;
+ param["IntValue"] = Convert.ToString(Idata);
+
+ ArrayList parameters = new ArrayList();
+ parameters.Add(param);
+ XmlRpcRequest req = new XmlRpcRequest(mName, parameters);
+ try
+ {
+ XmlRpcResponse resp = req.Send(DestURL, 30000);
+ if (resp != null)
+ {
+ Hashtable respParms;
+ if (resp.Value.GetType().Equals(typeof(System.Collections.Hashtable)))
+ {
+ respParms = (Hashtable) resp.Value;
+ }
+ else
+ {
+ ArrayList respData = (ArrayList) resp.Value;
+ respParms = (Hashtable) respData[0];
+ }
+ if (respParms != null)
+ {
+ if (respParms.Contains("StringValue"))
+ {
+ Sdata = (string) respParms["StringValue"];
+ }
+ if (respParms.Contains("IntValue"))
+ {
+ Idata = Convert.ToInt32((string) respParms["IntValue"]);
+ }
+ if (respParms.Contains("faultString"))
+ {
+ Sdata = (string) respParms["faultString"];
+ }
+ if (respParms.Contains("faultCode"))
+ {
+ Idata = Convert.ToInt32(respParms["faultCode"]);
+ }
+ }
+ }
+ }
+ catch (Exception we)
+ {
+ Sdata = we.Message;
+ m_log.Warn("[SendRemoteDataRequest]: Request failed");
+ m_log.Warn(we.StackTrace);
+ }
+
+ _finished = true;
+ }
+
+ public void Stop()
+ {
+ try
+ {
+ httpThread.Abort();
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ public UUID GetReqID()
+ {
+ return ReqID;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveConstants.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveConstants.cs
new file mode 100644
index 0000000..179d1a2
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveConstants.cs
@@ -0,0 +1,128 @@
+/*
+ * 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 OpenSim 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.Collections.Generic;
+using OpenMetaverse;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Constants for the archiving module
+ ///
+ public class ArchiveConstants
+ {
+ ///
+ /// The location of the archive control file
+ ///
+ public static readonly string CONTROL_FILE_PATH = "archive.xml";
+
+ ///
+ /// Path for the assets held in an archive
+ ///
+ public static readonly string ASSETS_PATH = "assets/";
+
+ ///
+ /// Path for the assets metadata file
+ ///
+ //public static readonly string ASSETS_METADATA_PATH = "assets.xml";
+
+ ///
+ /// Path for the prims file
+ ///
+ public static readonly string OBJECTS_PATH = "objects/";
+
+ ///
+ /// Path for terrains. Technically these may be assets, but I think it's quite nice to split them out.
+ ///
+ public static readonly string TERRAINS_PATH = "terrains/";
+
+ ///
+ /// Path for region settings.
+ ///
+ public static readonly string SETTINGS_PATH = "settings/";
+
+ ///
+ /// The character the separates the uuid from extension information in an archived asset filename
+ ///
+ public static readonly string ASSET_EXTENSION_SEPARATOR = "_";
+
+ ///
+ /// Extensions used for asset types in the archive
+ ///
+ public static readonly IDictionary ASSET_TYPE_TO_EXTENSION = new Dictionary();
+ public static readonly IDictionary EXTENSION_TO_ASSET_TYPE = new Dictionary();
+
+ static ArchiveConstants()
+ {
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Animation] = ASSET_EXTENSION_SEPARATOR + "animation.bvh";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Bodypart] = ASSET_EXTENSION_SEPARATOR + "bodypart.txt";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.CallingCard] = ASSET_EXTENSION_SEPARATOR + "callingcard.txt";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Clothing] = ASSET_EXTENSION_SEPARATOR + "clothing.txt";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Folder] = ASSET_EXTENSION_SEPARATOR + "folder.txt"; // Not sure if we'll ever see this
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Gesture] = ASSET_EXTENSION_SEPARATOR + "gesture.txt";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.ImageJPEG] = ASSET_EXTENSION_SEPARATOR + "image.jpg";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.ImageTGA] = ASSET_EXTENSION_SEPARATOR + "image.tga";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Landmark] = ASSET_EXTENSION_SEPARATOR + "landmark.txt";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LostAndFoundFolder] = ASSET_EXTENSION_SEPARATOR + "lostandfoundfolder.txt"; // Not sure if we'll ever see this
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LSLBytecode] = ASSET_EXTENSION_SEPARATOR + "bytecode.lso";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LSLText] = ASSET_EXTENSION_SEPARATOR + "script.lsl";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Notecard] = ASSET_EXTENSION_SEPARATOR + "notecard.txt";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Object] = ASSET_EXTENSION_SEPARATOR + "object.xml";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.RootFolder] = ASSET_EXTENSION_SEPARATOR + "rootfolder.txt"; // Not sure if we'll ever see this
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Simstate] = ASSET_EXTENSION_SEPARATOR + "simstate.bin"; // Not sure if we'll ever see this
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SnapshotFolder] = ASSET_EXTENSION_SEPARATOR + "snapshotfolder.txt"; // Not sure if we'll ever see this
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Sound] = ASSET_EXTENSION_SEPARATOR + "sound.ogg";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV] = ASSET_EXTENSION_SEPARATOR + "sound.wav";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Texture] = ASSET_EXTENSION_SEPARATOR + "texture.jp2";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TextureTGA] = ASSET_EXTENSION_SEPARATOR + "texture.tga";
+ ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TrashFolder] = ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"; // Not sure if we'll ever see this
+
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "animation.bvh"] = (sbyte)AssetType.Animation;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bodypart.txt"] = (sbyte)AssetType.Bodypart;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "callingcard.txt"] = (sbyte)AssetType.CallingCard;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "clothing.txt"] = (sbyte)AssetType.Clothing;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "folder.txt"] = (sbyte)AssetType.Folder;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "gesture.txt"] = (sbyte)AssetType.Gesture;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.jpg"] = (sbyte)AssetType.ImageJPEG;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.tga"] = (sbyte)AssetType.ImageTGA;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "landmark.txt"] = (sbyte)AssetType.Landmark;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "lostandfoundfolder.txt"] = (sbyte)AssetType.LostAndFoundFolder;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bytecode.lso"] = (sbyte)AssetType.LSLBytecode;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "script.lsl"] = (sbyte)AssetType.LSLText;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "notecard.txt"] = (sbyte)AssetType.Notecard;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "object.xml"] = (sbyte)AssetType.Object;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "rootfolder.txt"] = (sbyte)AssetType.RootFolder;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "simstate.bin"] = (sbyte)AssetType.Simstate;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "snapshotfolder.txt"] = (sbyte)AssetType.SnapshotFolder;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.ogg"] = (sbyte)AssetType.Sound;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.wav"] = (sbyte)AssetType.SoundWAV;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.jp2"] = (sbyte)AssetType.Texture;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.tga"] = (sbyte)AssetType.TextureTGA;
+ EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = (sbyte)AssetType.TrashFolder;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
new file mode 100644
index 0000000..3218abc
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
@@ -0,0 +1,460 @@
+/*
+ * 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 OpenSim 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.IO;
+using System.IO.Compression;
+using System.Reflection;
+using System.Xml;
+using System.Net;
+using OpenMetaverse;
+using log4net;
+using OpenSim.Framework;
+using OpenSim.Framework.Communications.Cache;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.CoreModules.World.Terrain;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Handles an individual archive read request
+ ///
+ public class ArchiveReadRequest
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private static System.Text.ASCIIEncoding m_asciiEncoding = new System.Text.ASCIIEncoding();
+
+ private Scene m_scene;
+ private Stream m_loadStream;
+ private string m_errorMessage;
+
+ ///
+ /// Used to cache lookups for valid uuids.
+ ///
+ private IDictionary m_validUserUuids = new Dictionary();
+
+ public ArchiveReadRequest(Scene scene, string loadPath)
+ {
+ m_scene = scene;
+ m_loadStream = new GZipStream(GetStream(loadPath), CompressionMode.Decompress);
+ m_errorMessage = String.Empty;
+ }
+
+ public ArchiveReadRequest(Scene scene, Stream loadStream)
+ {
+ m_scene = scene;
+ m_loadStream = loadStream;
+ }
+
+ ///
+ /// Dearchive the region embodied in this request.
+ ///
+ public void DearchiveRegion()
+ {
+ // The same code can handle dearchiving 0.1 and 0.2 OpenSim Archive versions
+ DearchiveRegion0DotStar();
+ }
+
+ private void DearchiveRegion0DotStar()
+ {
+ int successfulAssetRestores = 0;
+ int failedAssetRestores = 0;
+ List serialisedSceneObjects = new List();
+
+ try
+ {
+ TarArchiveReader archive = new TarArchiveReader(m_loadStream);
+
+ //AssetsDearchiver dearchiver = new AssetsDearchiver(m_scene.AssetCache);
+
+ string filePath = "ERROR";
+
+ byte[] data;
+ TarArchiveReader.TarEntryType entryType;
+
+ while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
+ {
+ //m_log.DebugFormat(
+ // "[ARCHIVER]: Successfully read {0} ({1} bytes)}", filePath, data.Length);
+ if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
+ {
+ m_log.WarnFormat("[ARCHIVER]: Ignoring directory entry {0}",
+ filePath);
+ }
+ else if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
+ {
+ serialisedSceneObjects.Add(m_asciiEncoding.GetString(data));
+ }
+// else if (filePath.Equals(ArchiveConstants.ASSETS_METADATA_PATH))
+// {
+// string xml = m_asciiEncoding.GetString(data);
+// dearchiver.AddAssetMetadata(xml);
+// }
+ else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH))
+ {
+ if (LoadAsset(filePath, data))
+ successfulAssetRestores++;
+ else
+ failedAssetRestores++;
+ }
+ else if (filePath.StartsWith(ArchiveConstants.TERRAINS_PATH))
+ {
+ LoadTerrain(filePath, data);
+ }
+ else if (filePath.StartsWith(ArchiveConstants.SETTINGS_PATH))
+ {
+ LoadRegionSettings(filePath, data);
+ }
+ }
+
+ //m_log.Debug("[ARCHIVER]: Reached end of archive");
+
+ archive.Close();
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat(
+ "[ARCHIVER]: Error loading oar file. Exception was: {0}", e);
+ m_errorMessage += e.ToString();
+ m_scene.EventManager.TriggerOarFileLoaded(m_errorMessage);
+ return;
+ }
+
+ m_log.InfoFormat("[ARCHIVER]: Restored {0} assets", successfulAssetRestores);
+
+ if (failedAssetRestores > 0)
+ {
+ m_log.ErrorFormat("[ARCHIVER]: Failed to load {0} assets", failedAssetRestores);
+ m_errorMessage += String.Format("Failed to load {0} assets", failedAssetRestores);
+ }
+
+ m_log.Info("[ARCHIVER]: Clearing all existing scene objects");
+ m_scene.DeleteAllSceneObjects();
+
+ // Reload serialized prims
+ m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count);
+
+ IRegionSerialiserModule serialiser = m_scene.RequestModuleInterface();
+ ICollection sceneObjects = new List();
+
+ foreach (string serialisedSceneObject in serialisedSceneObjects)
+ {
+ SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject);
+
+ // For now, give all incoming scene objects new uuids. This will allow scenes to be cloned
+ // on the same region server and multiple examples a single object archive to be imported
+ // to the same scene (when this is possible).
+ sceneObject.ResetIDs();
+
+ // Try to retain the original creator/owner/lastowner if their uuid is present on this grid
+ // otherwise, use the master avatar uuid instead
+ UUID masterAvatarId = m_scene.RegionInfo.MasterAvatarAssignedUUID;
+
+ if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero)
+ masterAvatarId = m_scene.RegionInfo.EstateSettings.EstateOwner;
+
+ foreach (SceneObjectPart part in sceneObject.Children.Values)
+ {
+ if (!resolveUserUuid(part.CreatorID))
+ part.CreatorID = masterAvatarId;
+
+ if (!resolveUserUuid(part.OwnerID))
+ part.OwnerID = masterAvatarId;
+
+ if (!resolveUserUuid(part.LastOwnerID))
+ part.LastOwnerID = masterAvatarId;
+
+ // And zap any troublesome sit target information
+ part.SitTargetOrientation = new Quaternion(0, 0, 0, 1);
+ part.SitTargetPosition = new Vector3(0, 0, 0);
+
+ // Fix ownership/creator of inventory items
+ // Not doing so results in inventory items
+ // being no copy/no mod for everyone
+ TaskInventoryDictionary inv = part.TaskInventory;
+ foreach (KeyValuePair kvp in inv)
+ {
+ if (!resolveUserUuid(kvp.Value.OwnerID))
+ {
+ kvp.Value.OwnerID = masterAvatarId;
+ }
+ if (!resolveUserUuid(kvp.Value.CreatorID))
+ {
+ kvp.Value.CreatorID = masterAvatarId;
+ }
+ }
+ }
+
+ if (m_scene.AddRestoredSceneObject(sceneObject, true, false))
+ {
+ sceneObjects.Add(sceneObject);
+ }
+ }
+
+ m_log.InfoFormat("[ARCHIVER]: Restored {0} scene objects to the scene", sceneObjects.Count);
+
+ int ignoredObjects = serialisedSceneObjects.Count - sceneObjects.Count;
+
+ if (ignoredObjects > 0)
+ m_log.WarnFormat("[ARCHIVER]: Ignored {0} scene objects that already existed in the scene", ignoredObjects);
+
+ m_log.InfoFormat("[ARCHIVER]: Successfully loaded archive");
+
+ m_log.Debug("[ARCHIVER]: Starting scripts");
+
+ foreach (SceneObjectGroup sceneObject in sceneObjects)
+ {
+ sceneObject.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 0);
+ }
+
+ m_scene.EventManager.TriggerOarFileLoaded(m_errorMessage);
+ }
+
+ ///
+ /// Look up the given user id to check whether it's one that is valid for this grid.
+ ///
+ ///
+ ///
+ private bool resolveUserUuid(UUID uuid)
+ {
+ if (!m_validUserUuids.ContainsKey(uuid))
+ {
+ CachedUserInfo profile = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(uuid);
+ if (profile != null && profile.UserProfile != null)
+ m_validUserUuids.Add(uuid, true);
+ else
+ m_validUserUuids.Add(uuid, false);
+ }
+
+ if (m_validUserUuids[uuid])
+ return true;
+ else
+ return false;
+ }
+
+ ///
+ /// Load an asset
+ ///
+ ///
+ ///
+ /// true if asset was successfully loaded, false otherwise
+ private bool LoadAsset(string assetPath, byte[] data)
+ {
+ // Right now we're nastily obtaining the UUID from the filename
+ string filename = assetPath.Remove(0, ArchiveConstants.ASSETS_PATH.Length);
+ int i = filename.LastIndexOf(ArchiveConstants.ASSET_EXTENSION_SEPARATOR);
+
+ if (i == -1)
+ {
+ m_log.ErrorFormat(
+ "[ARCHIVER]: Could not find extension information in asset path {0} since it's missing the separator {1}. Skipping",
+ assetPath, ArchiveConstants.ASSET_EXTENSION_SEPARATOR);
+
+ return false;
+ }
+
+ string extension = filename.Substring(i);
+ string uuid = filename.Remove(filename.Length - extension.Length);
+
+ if (ArchiveConstants.EXTENSION_TO_ASSET_TYPE.ContainsKey(extension))
+ {
+ sbyte assetType = ArchiveConstants.EXTENSION_TO_ASSET_TYPE[extension];
+
+ //m_log.DebugFormat("[ARCHIVER]: Importing asset {0}, type {1}", uuid, assetType);
+
+ AssetBase asset = new AssetBase(new UUID(uuid), String.Empty);
+ asset.Metadata.Type = assetType;
+ asset.Data = data;
+
+ m_scene.AssetCache.AddAsset(asset);
+
+ /**
+ * Create layers on decode for image assets. This is likely to significantly increase the time to load archives so
+ * it might be best done when dearchive takes place on a separate thread
+ if (asset.Type=AssetType.Texture)
+ {
+ IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface();
+ if (cacheLayerDecode != null)
+ cacheLayerDecode.syncdecode(asset.FullID, asset.Data);
+ }
+ */
+
+ return true;
+ }
+ else
+ {
+ m_log.ErrorFormat(
+ "[ARCHIVER]: Tried to dearchive data with path {0} with an unknown type extension {1}",
+ assetPath, extension);
+
+ return false;
+ }
+ }
+
+ ///
+ /// Load region settings data
+ ///
+ ///
+ ///
+ ///
+ /// true if settings were loaded successfully, false otherwise
+ ///
+ private bool LoadRegionSettings(string settingsPath, byte[] data)
+ {
+ RegionSettings loadedRegionSettings;
+
+ try
+ {
+ loadedRegionSettings = RegionSettingsSerializer.Deserialize(data);
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat(
+ "[ARCHIVER]: Could not parse region settings file {0}. Ignoring. Exception was {1}",
+ settingsPath, e);
+ return false;
+ }
+
+ RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings;
+
+ currentRegionSettings.AgentLimit = loadedRegionSettings.AgentLimit;
+ currentRegionSettings.AllowDamage = loadedRegionSettings.AllowDamage;
+ currentRegionSettings.AllowLandJoinDivide = loadedRegionSettings.AllowLandJoinDivide;
+ currentRegionSettings.AllowLandResell = loadedRegionSettings.AllowLandResell;
+ currentRegionSettings.BlockFly = loadedRegionSettings.BlockFly;
+ currentRegionSettings.BlockShowInSearch = loadedRegionSettings.BlockShowInSearch;
+ currentRegionSettings.BlockTerraform = loadedRegionSettings.BlockTerraform;
+ currentRegionSettings.DisableCollisions = loadedRegionSettings.DisableCollisions;
+ currentRegionSettings.DisablePhysics = loadedRegionSettings.DisablePhysics;
+ currentRegionSettings.DisableScripts = loadedRegionSettings.DisableScripts;
+ currentRegionSettings.Elevation1NE = loadedRegionSettings.Elevation1NE;
+ currentRegionSettings.Elevation1NW = loadedRegionSettings.Elevation1NW;
+ currentRegionSettings.Elevation1SE = loadedRegionSettings.Elevation1SE;
+ currentRegionSettings.Elevation1SW = loadedRegionSettings.Elevation1SW;
+ currentRegionSettings.Elevation2NE = loadedRegionSettings.Elevation2NE;
+ currentRegionSettings.Elevation2NW = loadedRegionSettings.Elevation2NW;
+ currentRegionSettings.Elevation2SE = loadedRegionSettings.Elevation2SE;
+ currentRegionSettings.Elevation2SW = loadedRegionSettings.Elevation2SW;
+ currentRegionSettings.FixedSun = loadedRegionSettings.FixedSun;
+ currentRegionSettings.ObjectBonus = loadedRegionSettings.ObjectBonus;
+ currentRegionSettings.RestrictPushing = loadedRegionSettings.RestrictPushing;
+ currentRegionSettings.TerrainLowerLimit = loadedRegionSettings.TerrainLowerLimit;
+ currentRegionSettings.TerrainRaiseLimit = loadedRegionSettings.TerrainRaiseLimit;
+ currentRegionSettings.TerrainTexture1 = loadedRegionSettings.TerrainTexture1;
+ currentRegionSettings.TerrainTexture2 = loadedRegionSettings.TerrainTexture2;
+ currentRegionSettings.TerrainTexture3 = loadedRegionSettings.TerrainTexture3;
+ currentRegionSettings.TerrainTexture4 = loadedRegionSettings.TerrainTexture4;
+ currentRegionSettings.UseEstateSun = loadedRegionSettings.UseEstateSun;
+ currentRegionSettings.WaterHeight = loadedRegionSettings.WaterHeight;
+
+ IEstateModule estateModule = m_scene.RequestModuleInterface();
+ estateModule.sendRegionHandshakeToAll();
+
+ return true;
+ }
+
+ ///
+ /// Load terrain data
+ ///
+ ///
+ ///
+ ///
+ /// true if terrain was resolved successfully, false otherwise.
+ ///
+ private bool LoadTerrain(string terrainPath, byte[] data)
+ {
+ ITerrainModule terrainModule = m_scene.RequestModuleInterface();
+
+ MemoryStream ms = new MemoryStream(data);
+ terrainModule.LoadFromStream(terrainPath, ms);
+ ms.Close();
+
+ m_log.DebugFormat("[ARCHIVER]: Restored terrain {0}", terrainPath);
+
+ return true;
+ }
+
+ ///
+ /// Resolve path to a working FileStream
+ ///
+ private Stream GetStream(string path)
+ {
+ try
+ {
+ if (File.Exists(path))
+ {
+ return new FileStream(path, FileMode.Open);
+ }
+ else
+ {
+ Uri uri = new Uri(path); // throw exception if not valid URI
+ if (uri.Scheme == "file")
+ {
+ return new FileStream(uri.AbsolutePath, FileMode.Open);
+ }
+ else
+ {
+ if (uri.Scheme != "http")
+ throw new Exception(String.Format("Unsupported URI scheme ({0})", path));
+
+ // OK, now we know we have an HTTP URI to work with
+
+ return URIFetch(uri);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw new Exception(String.Format("Unable to create file input stream for {0}: {1}", path, e));
+ }
+ }
+
+ private static Stream URIFetch(Uri uri)
+ {
+ HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri);
+
+ // request.Credentials = credentials;
+
+ request.ContentLength = 0;
+
+ WebResponse response = request.GetResponse();
+ Stream file = response.GetResponseStream();
+
+ if (response.ContentType != "application/x-oar")
+ throw new Exception(String.Format("{0} does not identify an OAR file", uri.ToString()));
+
+ if (response.ContentLength == 0)
+ throw new Exception(String.Format("{0} returned an empty file", uri.ToString()));
+
+ // return new BufferedStream(file, (int) response.ContentLength);
+ return new BufferedStream(file, 1000000);
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs
new file mode 100644
index 0000000..d3c2cd1
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs
@@ -0,0 +1,161 @@
+/*
+ * 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 OpenSim 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.IO;
+using System.Reflection;
+using System.Xml;
+using OpenMetaverse;
+using log4net;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.CoreModules.World.Serialiser;
+using OpenSim.Region.CoreModules.World.Terrain;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Method called when all the necessary assets for an archive request have been received.
+ ///
+ public delegate void AssetsRequestCallback(IDictionary assetsFound, ICollection assetsNotFoundUuids);
+
+ ///
+ /// Execute the write of an archive once we have received all the necessary data
+ ///
+ public class ArchiveWriteRequestExecution
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ protected ITerrainModule m_terrainModule;
+ protected IRegionSerialiserModule m_serialiser;
+ protected List m_sceneObjects;
+ protected Scene m_scene;
+ protected Stream m_saveStream;
+
+ public ArchiveWriteRequestExecution(
+ List sceneObjects,
+ ITerrainModule terrainModule,
+ IRegionSerialiserModule serialiser,
+ Scene scene,
+ Stream saveStream)
+ {
+ m_sceneObjects = sceneObjects;
+ m_terrainModule = terrainModule;
+ m_serialiser = serialiser;
+ m_scene = scene;
+ m_saveStream = saveStream;
+ }
+
+ protected internal void ReceivedAllAssets(
+ IDictionary assetsFound, ICollection assetsNotFoundUuids)
+ {
+ foreach (UUID uuid in assetsNotFoundUuids)
+ {
+ m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid);
+ }
+
+ m_log.InfoFormat(
+ "[ARCHIVER]: Received {0} of {1} assets requested",
+ assetsFound.Count, assetsFound.Count + assetsNotFoundUuids.Count);
+
+ m_log.InfoFormat("[ARCHIVER]: Creating archive file. This may take some time.");
+
+ TarArchiveWriter archive = new TarArchiveWriter();
+
+ // Write out control file
+ archive.AddFile(ArchiveConstants.CONTROL_FILE_PATH, Create0p2ControlFile());
+
+ // Write out region settings
+ string settingsPath
+ = String.Format("{0}{1}.xml", ArchiveConstants.SETTINGS_PATH, m_scene.RegionInfo.RegionName);
+ archive.AddFile(settingsPath, RegionSettingsSerializer.Serialize(m_scene.RegionInfo.RegionSettings));
+
+ // Write out terrain
+ string terrainPath
+ = String.Format("{0}{1}.r32", ArchiveConstants.TERRAINS_PATH, m_scene.RegionInfo.RegionName);
+
+ MemoryStream ms = new MemoryStream();
+ m_terrainModule.SaveToStream(terrainPath, ms);
+ archive.AddFile(terrainPath, ms.ToArray());
+ ms.Close();
+
+ // Write out scene object metadata
+ foreach (SceneObjectGroup sceneObject in m_sceneObjects)
+ {
+ //m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType());
+
+ Vector3 position = sceneObject.AbsolutePosition;
+
+ string serializedObject = m_serialiser.SaveGroupToXml2(sceneObject);
+ string filename
+ = string.Format(
+ "{0}{1}_{2:000}-{3:000}-{4:000}__{5}.xml",
+ ArchiveConstants.OBJECTS_PATH, sceneObject.Name,
+ Math.Round(position.X), Math.Round(position.Y), Math.Round(position.Z),
+ sceneObject.UUID);
+
+ archive.AddFile(filename, serializedObject);
+ }
+
+ // Write out assets
+ AssetsArchiver assetsArchiver = new AssetsArchiver(assetsFound);
+ assetsArchiver.Archive(archive);
+
+ archive.WriteTar(m_saveStream);
+
+ m_log.InfoFormat("[ARCHIVER]: Wrote out OpenSimulator archive for {0}", m_scene.RegionInfo.RegionName);
+
+ m_scene.EventManager.TriggerOarFileSaved(String.Empty);
+ }
+
+ ///
+ /// Create the control file for a 0.2 version archive
+ ///
+ ///
+ public static string Create0p2ControlFile()
+ {
+ StringWriter sw = new StringWriter();
+ XmlTextWriter xtw = new XmlTextWriter(sw);
+ xtw.Formatting = Formatting.Indented;
+ xtw.WriteStartDocument();
+ xtw.WriteStartElement("archive");
+ xtw.WriteAttributeString("major_version", "0");
+ xtw.WriteAttributeString("minor_version", "2");
+ xtw.WriteEndElement();
+
+ xtw.Flush();
+ xtw.Close();
+
+ String s = sw.ToString();
+ sw.Close();
+
+ return s;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs
new file mode 100644
index 0000000..ee0ec69
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs
@@ -0,0 +1,333 @@
+/*
+ * 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 OpenSim 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 OpenSim.Framework;
+using OpenSim.Framework.Communications.Cache;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.CoreModules.World.Serialiser;
+using OpenSim.Region.CoreModules.World.Terrain;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using System.Threading;
+using OpenMetaverse;
+using log4net;
+using Nini.Config;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Prepare to write out an archive.
+ ///
+ public class ArchiveWriteRequestPreparation
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ protected Scene m_scene;
+ protected Stream m_saveStream;
+
+ ///
+ /// Used as a temporary store of an asset which represents an object. This can be a null if no appropriate
+ /// asset was found by the asset service.
+ ///
+ protected AssetBase m_requestedObjectAsset;
+
+ ///
+ /// Signal whether we are currently waiting for the asset service to deliver an asset.
+ ///
+ protected bool m_waitingForObjectAsset;
+
+ ///
+ /// Constructor
+ ///
+ public ArchiveWriteRequestPreparation(Scene scene, string savePath)
+ {
+ m_scene = scene;
+ m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress);
+ }
+
+ ///
+ /// Constructor.
+ ///
+ ///
+ /// The stream to which to save data.
+ public ArchiveWriteRequestPreparation(Scene scene, Stream saveStream)
+ {
+ m_scene = scene;
+ m_saveStream = saveStream;
+ }
+
+ ///
+ /// The callback made when we request the asset for an object from the asset service.
+ ///
+ public void AssetRequestCallback(UUID assetID, AssetBase asset)
+ {
+ lock (this)
+ {
+ m_requestedObjectAsset = asset;
+ m_waitingForObjectAsset = false;
+ Monitor.Pulse(this);
+ }
+ }
+
+ ///
+ /// Get an asset synchronously, potentially using an asynchronous callback. If the
+ /// asynchronous callback is used, we will wait for it to complete.
+ ///
+ ///
+ ///
+ protected AssetBase GetAsset(UUID uuid)
+ {
+ m_waitingForObjectAsset = true;
+ m_scene.AssetCache.GetAsset(uuid, AssetRequestCallback, true);
+
+ // The asset cache callback can either
+ //
+ // 1. Complete on the same thread (if the asset is already in the cache) or
+ // 2. Come in via a different thread (if we need to go fetch it).
+ //
+ // The code below handles both these alternatives.
+ lock (this)
+ {
+ if (m_waitingForObjectAsset)
+ {
+ Monitor.Wait(this);
+ m_waitingForObjectAsset = false;
+ }
+ }
+
+ return m_requestedObjectAsset;
+ }
+
+ ///
+ /// Record the asset uuids embedded within the given script.
+ ///
+ ///
+ /// Dictionary in which to record the references
+ protected void GetScriptAssetUuids(UUID scriptUuid, IDictionary assetUuids)
+ {
+ AssetBase scriptAsset = GetAsset(scriptUuid);
+
+ if (null != scriptAsset)
+ {
+ string script = Utils.BytesToString(scriptAsset.Data);
+ //m_log.DebugFormat("[ARCHIVER]: Script {0}", script);
+ MatchCollection uuidMatches = Util.UUIDPattern.Matches(script);
+ //m_log.DebugFormat("[ARCHIVER]: Found {0} matches in script", uuidMatches.Count);
+
+ foreach (Match uuidMatch in uuidMatches)
+ {
+ UUID uuid = new UUID(uuidMatch.Value);
+ //m_log.DebugFormat("[ARCHIVER]: Recording {0} in script", uuid);
+ assetUuids[uuid] = 1;
+ }
+ }
+ }
+
+ ///
+ /// Record the uuids referenced by the given wearable asset
+ ///
+ ///
+ /// Dictionary in which to record the references
+ protected void GetWearableAssetUuids(UUID wearableAssetUuid, IDictionary assetUuids)
+ {
+ AssetBase assetBase = GetAsset(wearableAssetUuid);
+ //m_log.Debug(new System.Text.ASCIIEncoding().GetString(bodypartAsset.Data));
+ AssetWearable wearableAsset = new AssetBodypart(wearableAssetUuid, assetBase.Data);
+ wearableAsset.Decode();
+
+ //m_log.DebugFormat(
+ // "[ARCHIVER]: Wearable asset {0} references {1} assets", wearableAssetUuid, wearableAsset.Textures.Count);
+
+ foreach (UUID uuid in wearableAsset.Textures.Values)
+ {
+ //m_log.DebugFormat("[ARCHIVER]: Got bodypart uuid {0}", uuid);
+ assetUuids[uuid] = 1;
+ }
+ }
+
+ ///
+ /// Get all the asset uuids associated with a given object. This includes both those directly associated with
+ /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained
+ /// within this object).
+ ///
+ ///
+ ///
+ protected void GetSceneObjectAssetUuids(UUID sceneObjectUuid, IDictionary assetUuids)
+ {
+ AssetBase objectAsset = GetAsset(sceneObjectUuid);
+
+ if (null != objectAsset)
+ {
+ string xml = Utils.BytesToString(objectAsset.Data);
+ SceneObjectGroup sog = new SceneObjectGroup(xml, true);
+ GetSceneObjectAssetUuids(sog, assetUuids);
+ }
+ }
+
+ ///
+ /// Get all the asset uuids associated with a given object. This includes both those directly associated with
+ /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained
+ /// within this object).
+ ///
+ ///
+ ///
+ protected void GetSceneObjectAssetUuids(SceneObjectGroup sceneObject, IDictionary assetUuids)
+ {
+ m_log.DebugFormat(
+ "[ARCHIVER]: Getting assets for object {0}, {1}", sceneObject.Name, sceneObject.UUID);
+
+ foreach (SceneObjectPart part in sceneObject.GetParts())
+ {
+ //m_log.DebugFormat(
+ // "[ARCHIVER]: Getting part {0}, {1} for object {2}", part.Name, part.UUID, sceneObject.UUID);
+
+ try
+ {
+ Primitive.TextureEntry textureEntry = part.Shape.Textures;
+
+ // Get the prim's default texture. This will be used for faces which don't have their own texture
+ assetUuids[textureEntry.DefaultTexture.TextureID] = 1;
+
+ // XXX: Not a great way to iterate through face textures, but there's no
+ // other method available to tell how many faces there actually are
+ //int i = 0;
+ foreach (Primitive.TextureEntryFace texture in textureEntry.FaceTextures)
+ {
+ if (texture != null)
+ {
+ //m_log.DebugFormat("[ARCHIVER]: Got face {0}", i++);
+ assetUuids[texture.TextureID] = 1;
+ }
+ }
+
+ // If the prim is a sculpt then preserve this information too
+ if (part.Shape.SculptTexture != UUID.Zero)
+ assetUuids[part.Shape.SculptTexture] = 1;
+
+ // Now analyze this prim's inventory items to preserve all the uuids that they reference
+ foreach (TaskInventoryItem tii in part.TaskInventory.Values)
+ {
+ //m_log.DebugFormat("[ARCHIVER]: Analysing item asset type {0}", tii.Type);
+
+ if (!assetUuids.ContainsKey(tii.AssetID))
+ {
+ assetUuids[tii.AssetID] = 1;
+
+ if ((int)AssetType.Bodypart == tii.Type || ((int)AssetType.Clothing == tii.Type))
+ {
+ GetWearableAssetUuids(tii.AssetID, assetUuids);
+ }
+ else if ((int)AssetType.LSLText == tii.Type)
+ {
+ GetScriptAssetUuids(tii.AssetID, assetUuids);
+ }
+ else if ((int)AssetType.Object == tii.Type)
+ {
+ GetSceneObjectAssetUuids(tii.AssetID, assetUuids);
+ }
+ //else
+ //{
+ //m_log.DebugFormat("[ARCHIVER]: Recording asset {0} in object {1}", tii.AssetID, part.UUID);
+ //}
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[ARCHIVER]: Failed to get part - {0}", e);
+ m_log.DebugFormat("[ARCHIVER]: Texture entry length for prim was {0} (min is 46)", part.Shape.TextureEntry.Length);
+ }
+ }
+ }
+
+ ///
+ /// Archive the region requested.
+ ///
+ /// if there was an io problem with creating the file
+ public void ArchiveRegion()
+ {
+ Dictionary assetUuids = new Dictionary();
+
+ List entities = m_scene.GetEntities();
+ List sceneObjects = new List();
+
+ // Filter entities so that we only have scene objects.
+ // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods
+ // end up having to do this
+ foreach (EntityBase entity in entities)
+ {
+ if (entity is SceneObjectGroup)
+ {
+ SceneObjectGroup sceneObject = (SceneObjectGroup)entity;
+
+ if (!sceneObject.IsDeleted && !sceneObject.IsAttachment)
+ sceneObjects.Add((SceneObjectGroup)entity);
+ }
+ }
+
+ foreach (SceneObjectGroup sceneObject in sceneObjects)
+ {
+ GetSceneObjectAssetUuids(sceneObject, assetUuids);
+ }
+
+ m_log.DebugFormat(
+ "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets",
+ sceneObjects.Count, assetUuids.Count);
+
+ // Make sure that we also request terrain texture assets
+ RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings;
+
+ if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1)
+ assetUuids[regionSettings.TerrainTexture1] = 1;
+
+ if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2)
+ assetUuids[regionSettings.TerrainTexture2] = 1;
+
+ if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3)
+ assetUuids[regionSettings.TerrainTexture3] = 1;
+
+ if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4)
+ assetUuids[regionSettings.TerrainTexture4] = 1;
+
+ // Asynchronously request all the assets required to perform this archive operation
+ ArchiveWriteRequestExecution awre
+ = new ArchiveWriteRequestExecution(
+ sceneObjects,
+ m_scene.RequestModuleInterface(),
+ m_scene.RequestModuleInterface(),
+ m_scene,
+ m_saveStream);
+
+ new AssetsRequest(assetUuids.Keys, m_scene.AssetCache, awre.ReceivedAllAssets).Execute();
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
new file mode 100644
index 0000000..c1f5b18
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
@@ -0,0 +1,95 @@
+/*
+ * 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 OpenSim 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.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Threading;
+using OpenMetaverse;
+using log4net;
+using Nini.Config;
+using OpenSim.Framework.Communications.Cache;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.CoreModules.World.Serialiser;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// This module loads and saves OpenSimulator archives
+ ///
+ public class ArchiverModule : IRegionModule, IRegionArchiverModule
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private Scene m_scene;
+
+ public string Name { get { return "Archiver Module"; } }
+
+ public bool IsSharedModule { get { return false; } }
+
+ public void Initialise(Scene scene, IConfigSource source)
+ {
+ m_scene = scene;
+ m_scene.RegisterModuleInterface(this);
+ }
+
+ public void PostInitialise()
+ {
+ }
+
+ public void Close()
+ {
+ }
+
+ public void ArchiveRegion(string savePath)
+ {
+ m_log.InfoFormat(
+ "[ARCHIVER]: Writing archive for region {0} to {1}", m_scene.RegionInfo.RegionName, savePath);
+
+ new ArchiveWriteRequestPreparation(m_scene, savePath).ArchiveRegion();
+ }
+
+ public void ArchiveRegion(Stream saveStream)
+ {
+ new ArchiveWriteRequestPreparation(m_scene, saveStream).ArchiveRegion();
+ }
+
+ public void DearchiveRegion(string loadPath)
+ {
+ m_log.InfoFormat(
+ "[ARCHIVER]: Loading archive to region {0} from {1}", m_scene.RegionInfo.RegionName, loadPath);
+
+ new ArchiveReadRequest(m_scene, loadPath).DearchiveRegion();
+ }
+
+ public void DearchiveRegion(Stream loadStream)
+ {
+ new ArchiveReadRequest(m_scene, loadStream).DearchiveRegion();
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsArchiver.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsArchiver.cs
new file mode 100644
index 0000000..76d27ce
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsArchiver.cs
@@ -0,0 +1,143 @@
+/*
+ * 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 OpenSim 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.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Xml;
+using OpenMetaverse;
+using log4net;
+using OpenSim.Framework;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Archives assets
+ ///
+ public class AssetsArchiver
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ ///
+ /// Archive assets
+ ///
+ protected IDictionary m_assets;
+
+ public AssetsArchiver(IDictionary assets)
+ {
+ m_assets = assets;
+ }
+
+ ///
+ /// Archive the assets given to this archiver to the given archive.
+ ///
+ ///
+ public void Archive(TarArchiveWriter archive)
+ {
+ //WriteMetadata(archive);
+ WriteData(archive);
+ }
+
+ ///
+ /// Write an assets metadata file to the given archive
+ ///
+ ///
+ protected void WriteMetadata(TarArchiveWriter archive)
+ {
+ StringWriter sw = new StringWriter();
+ XmlTextWriter xtw = new XmlTextWriter(sw);
+
+ xtw.Formatting = Formatting.Indented;
+ xtw.WriteStartDocument();
+
+ xtw.WriteStartElement("assets");
+
+ foreach (UUID uuid in m_assets.Keys)
+ {
+ AssetBase asset = m_assets[uuid];
+
+ if (asset != null)
+ {
+ xtw.WriteStartElement("asset");
+
+ string extension = string.Empty;
+
+ if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(asset.Metadata.Type))
+ {
+ extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[asset.Metadata.Type];
+ }
+
+ xtw.WriteElementString("filename", uuid.ToString() + extension);
+
+ xtw.WriteElementString("name", asset.Metadata.Name);
+ xtw.WriteElementString("description", asset.Metadata.Description);
+ xtw.WriteElementString("asset-type", asset.Metadata.Type.ToString());
+
+ xtw.WriteEndElement();
+ }
+ }
+
+ xtw.WriteEndElement();
+
+ xtw.WriteEndDocument();
+
+ archive.AddFile("assets.xml", sw.ToString());
+ }
+
+ ///
+ /// Write asset data files to the given archive
+ ///
+ ///
+ protected void WriteData(TarArchiveWriter archive)
+ {
+ // It appears that gtar, at least, doesn't need the intermediate directory entries in the tar
+ //archive.AddDir("assets");
+
+ foreach (UUID uuid in m_assets.Keys)
+ {
+ AssetBase asset = m_assets[uuid];
+
+ string extension = string.Empty;
+
+ if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(asset.Metadata.Type))
+ {
+ extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[asset.Metadata.Type];
+ }
+ else
+ {
+ m_log.ErrorFormat(
+ "[ARCHIVER]: Unrecognized asset type {0} with uuid {1}. This asset will be saved but not reloaded",
+ asset.Metadata.Type, asset.Metadata.ID);
+ }
+
+ archive.AddFile(
+ ArchiveConstants.ASSETS_PATH + uuid.ToString() + extension,
+ asset.Data);
+ }
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsDearchiver.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsDearchiver.cs
new file mode 100644
index 0000000..f9909d9
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsDearchiver.cs
@@ -0,0 +1,184 @@
+/*
+ * 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 OpenSim 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.IO;
+using System.Reflection;
+using System.Xml;
+using OpenMetaverse;
+using log4net;
+using OpenSim.Framework;
+using OpenSim.Framework.Communications.Cache;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Dearchives assets
+ ///
+ public class AssetsDearchiver
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ protected static System.Text.ASCIIEncoding m_asciiEncoding = new System.Text.ASCIIEncoding();
+
+ ///
+ /// Store for asset data we received before we get the metadata
+ ///
+ protected Dictionary m_assetDataAwaitingMetadata = new Dictionary();
+
+ ///
+ /// Asset metadata. Is null if asset metadata isn't yet available.
+ ///
+ protected Dictionary m_metadata;
+
+ ///
+ /// Cache to which dearchived assets will be added
+ ///
+ protected IAssetCache m_cache;
+
+ public AssetsDearchiver(IAssetCache cache)
+ {
+ m_cache = cache;
+ }
+
+ ///
+ /// Add asset data to the dearchiver
+ ///
+ ///
+ ///
+ public void AddAssetData(string assetFilename, byte[] data)
+ {
+ if (null == m_metadata)
+ {
+ m_assetDataAwaitingMetadata[assetFilename] = data;
+ }
+ else
+ {
+ ResolveAssetData(assetFilename, data);
+ }
+ }
+
+ ///
+ /// Add asset metadata xml
+ ///
+ ///
+ public void AddAssetMetadata(string xml)
+ {
+ m_metadata = new Dictionary();
+
+ StringReader sr = new StringReader(xml);
+ XmlTextReader reader = new XmlTextReader(sr);
+
+ reader.ReadStartElement("assets");
+ reader.Read();
+
+ while (reader.Name.Equals("asset"))
+ {
+ reader.Read();
+
+ AssetMetadata metadata = new AssetMetadata();
+
+ string filename = reader.ReadElementString("filename");
+ m_log.DebugFormat("[DEARCHIVER]: Reading node {0}", filename);
+
+ metadata.Name = reader.ReadElementString("name");
+ metadata.Description = reader.ReadElementString("description");
+ metadata.AssetType = Convert.ToSByte(reader.ReadElementString("asset-type"));
+
+ m_metadata[filename] = metadata;
+
+ // Read asset end tag
+ reader.ReadEndElement();
+
+ reader.Read();
+ }
+
+ m_log.DebugFormat("[DEARCHIVER]: Resolved {0} items of asset metadata", m_metadata.Count);
+
+ ResolvePendingAssetData();
+ }
+
+ ///
+ /// Resolve asset data that we collected before receiving the metadata
+ ///
+ protected void ResolvePendingAssetData()
+ {
+ foreach (string filename in m_assetDataAwaitingMetadata.Keys)
+ {
+ ResolveAssetData(filename, m_assetDataAwaitingMetadata[filename]);
+ }
+ }
+
+ ///
+ /// Resolve a new piece of asset data against stored metadata
+ ///
+ ///
+ ///
+ protected void ResolveAssetData(string assetPath, byte[] data)
+ {
+ // Right now we're nastily obtaining the UUID from the filename
+ string filename = assetPath.Remove(0, ArchiveConstants.ASSETS_PATH.Length);
+
+ if (m_metadata.ContainsKey(filename))
+ {
+ AssetMetadata metadata = m_metadata[filename];
+
+ if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(metadata.AssetType))
+ {
+ string extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[metadata.AssetType];
+ filename = filename.Remove(filename.Length - extension.Length);
+ }
+
+ m_log.DebugFormat("[ARCHIVER]: Importing asset {0}", filename);
+
+ AssetBase asset = new AssetBase(new UUID(filename), metadata.Name);
+ asset.Metadata.Description = metadata.Description;
+ asset.Metadata.Type = metadata.AssetType;
+ asset.Data = data;
+
+ m_cache.AddAsset(asset);
+ }
+ else
+ {
+ m_log.ErrorFormat(
+ "[DEARCHIVER]: Tried to dearchive data with filename {0} without any corresponding metadata",
+ assetPath);
+ }
+ }
+
+ ///
+ /// Metadata for an asset
+ ///
+ protected struct AssetMetadata
+ {
+ public string Name;
+ public string Description;
+ public sbyte AssetType;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
new file mode 100644
index 0000000..8971b6e
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
@@ -0,0 +1,138 @@
+/*
+ * 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 OpenSim 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.Reflection;
+using System.Threading;
+using OpenMetaverse;
+using log4net;
+using OpenSim.Framework;
+using OpenSim.Framework.Communications.Cache;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Encapsulate the asynchronous requests for the assets required for an archive operation
+ ///
+ class AssetsRequest
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ ///
+ /// uuids to request
+ ///
+ protected ICollection m_uuids;
+
+ ///
+ /// Callback used when all the assets requested have been received.
+ ///
+ protected AssetsRequestCallback m_assetsRequestCallback;
+
+ ///
+ /// Assets retrieved in this request
+ ///
+ protected Dictionary m_assets = new Dictionary();
+
+ ///
+ /// Maintain a list of assets that could not be found. This will be passed back to the requester.
+ ///
+ protected List m_notFoundAssetUuids = new List();
+
+ ///
+ /// Record the number of asset replies required so we know when we've finished
+ ///
+ private int m_repliesRequired;
+
+ ///
+ /// Asset cache used to request the assets
+ ///
+ protected IAssetCache m_assetCache;
+
+ protected internal AssetsRequest(ICollection uuids, IAssetCache assetCache, AssetsRequestCallback assetsRequestCallback)
+ {
+ m_uuids = uuids;
+ m_assetsRequestCallback = assetsRequestCallback;
+ m_assetCache = assetCache;
+ m_repliesRequired = uuids.Count;
+ }
+
+ protected internal void Execute()
+ {
+ // We can stop here if there are no assets to fetch
+ if (m_repliesRequired == 0)
+ m_assetsRequestCallback(m_assets, m_notFoundAssetUuids);
+
+ foreach (UUID uuid in m_uuids)
+ {
+ m_assetCache.GetAsset(uuid, AssetRequestCallback, true);
+ }
+ }
+
+ ///
+ /// Called back by the asset cache when it has the asset
+ ///
+ ///
+ ///
+ public void AssetRequestCallback(UUID assetID, AssetBase asset)
+ {
+ if (asset != null)
+ m_assets[assetID] = asset;
+ else
+ m_notFoundAssetUuids.Add(assetID);
+
+ //m_log.DebugFormat(
+ // "[ARCHIVER]: Received {0} assets and notification of {1} missing assets", m_assets.Count, m_notFoundAssetUuids.Count);
+
+ if (m_assets.Count + m_notFoundAssetUuids.Count == m_repliesRequired)
+ {
+ // We want to stop using the asset cache thread asap as we now need to do the actual work of producing the archive
+ Thread newThread = new Thread(PerformAssetsRequestCallback);
+ newThread.Name = "OpenSimulator archiving thread post assets receipt";
+ newThread.Start();
+ }
+ }
+
+ ///
+ /// Perform the callback on the original requester of the assets
+ ///
+ protected void PerformAssetsRequestCallback()
+ {
+ try
+ {
+ m_assetsRequestCallback(m_assets, m_notFoundAssetUuids);
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat(
+ "[ARCHIVER]: Terminating archive creation since asset requster callback failed with {0}", e);
+ }
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/RegionSettingsSerializer.cs b/OpenSim/Region/CoreModules/World/Archiver/RegionSettingsSerializer.cs
new file mode 100644
index 0000000..2580316
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/RegionSettingsSerializer.cs
@@ -0,0 +1,258 @@
+/*
+ * 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 OpenSim 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.IO;
+using System.Text;
+using System.Xml;
+using OpenMetaverse;
+using OpenSim.Framework;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Serialize and deserialize region settings for an archive file format.
+ ///
+ /// We didn't simply use automatic .NET serializagion for OpenSim.Framework.RegionSettings since this is really
+ /// a file format rather than an object serialization.
+ /// TODO: However, we could still have used separate non-framework classes here to read and write the xml
+ /// automatically rather than laboriously doing it by hand using XmlTextReader and Writer. Should switch to this
+ /// in the future.
+ public class RegionSettingsSerializer
+ {
+ protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding();
+
+ ///
+ /// Deserialize region settings
+ ///
+ ///
+ ///
+ ///
+ public static RegionSettings Deserialize(byte[] serializedSettings)
+ {
+ return Deserialize(m_asciiEncoding.GetString(serializedSettings, 0, serializedSettings.Length));
+ }
+
+ ///
+ /// Deserialize region settings
+ ///
+ ///
+ ///
+ ///
+ public static RegionSettings Deserialize(string serializedSettings)
+ {
+ RegionSettings settings = new RegionSettings();
+
+ StringReader sr = new StringReader(serializedSettings);
+ XmlTextReader xtr = new XmlTextReader(sr);
+
+ xtr.ReadStartElement("RegionSettings");
+
+ xtr.ReadStartElement("General");
+
+ while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement)
+ {
+ switch (xtr.Name)
+ {
+ case "AllowDamage":
+ settings.AllowDamage = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "AllowLandResell":
+ settings.AllowLandResell = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "AllowLandJoinDivide":
+ settings.AllowLandJoinDivide = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "BlockFly":
+ settings.BlockFly = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "BlockLandShowInSearch":
+ settings.BlockShowInSearch = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "BlockTerraform":
+ settings.BlockTerraform = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "DisableCollisions":
+ settings.DisableCollisions = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "DisablePhysics":
+ settings.DisablePhysics = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "DisableScripts":
+ settings.DisableScripts = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "MaturityRating":
+ settings.Maturity = int.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "RestrictPushing":
+ settings.RestrictPushing = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "AgentLimit":
+ settings.AgentLimit = int.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "ObjectBonus":
+ settings.ObjectBonus = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ }
+ }
+
+ xtr.ReadEndElement();
+ xtr.ReadStartElement("GroundTextures");
+
+ while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement)
+ {
+ switch (xtr.Name)
+ {
+ case "Texture1":
+ settings.TerrainTexture1 = UUID.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "Texture2":
+ settings.TerrainTexture2 = UUID.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "Texture3":
+ settings.TerrainTexture3 = UUID.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "Texture4":
+ settings.TerrainTexture4 = UUID.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "ElevationLowSW":
+ settings.Elevation1SW = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "ElevationLowNW":
+ settings.Elevation1NW = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "ElevationLowSE":
+ settings.Elevation1SE = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "ElevationLowNE":
+ settings.Elevation1NE = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "ElevationHighSW":
+ settings.Elevation1SW = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "ElevationHighNW":
+ settings.Elevation2NW = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "ElevationHighSE":
+ settings.Elevation2SE = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "ElevationHighNE":
+ settings.Elevation2NE = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ }
+ }
+
+ xtr.ReadEndElement();
+ xtr.ReadStartElement("Terrain");
+
+ while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement)
+ {
+ switch (xtr.Name)
+ {
+ case "WaterHeight":
+ settings.WaterHeight = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "TerrainRaiseLimit":
+ settings.TerrainRaiseLimit = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "TerrainLowerLimit":
+ settings.TerrainLowerLimit = double.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "UseEstateSun":
+ settings.UseEstateSun = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ case "FixedSun":
+ settings.FixedSun = bool.Parse(xtr.ReadElementContentAsString());
+ break;
+ }
+ }
+
+ xtr.Close();
+ sr.Close();
+
+ return settings;
+ }
+
+ public static string Serialize(RegionSettings settings)
+ {
+ StringWriter sw = new StringWriter();
+ XmlTextWriter xtw = new XmlTextWriter(sw);
+ xtw.Formatting = Formatting.Indented;
+ xtw.WriteStartDocument();
+
+ xtw.WriteStartElement("RegionSettings");
+
+ xtw.WriteStartElement("General");
+ xtw.WriteElementString("AllowDamage", settings.AllowDamage.ToString());
+ xtw.WriteElementString("AllowLandResell", settings.AllowLandResell.ToString());
+ xtw.WriteElementString("AllowLandJoinDivide", settings.AllowLandJoinDivide.ToString());
+ xtw.WriteElementString("BlockFly", settings.BlockFly.ToString());
+ xtw.WriteElementString("BlockLandShowInSearch", settings.BlockShowInSearch.ToString());
+ xtw.WriteElementString("BlockTerraform", settings.BlockTerraform.ToString());
+ xtw.WriteElementString("DisableCollisions", settings.DisableCollisions.ToString());
+ xtw.WriteElementString("DisablePhysics", settings.DisablePhysics.ToString());
+ xtw.WriteElementString("DisableScripts", settings.DisableScripts.ToString());
+ xtw.WriteElementString("MaturityRating", settings.Maturity.ToString());
+ xtw.WriteElementString("RestrictPushing", settings.RestrictPushing.ToString());
+ xtw.WriteElementString("AgentLimit", settings.AgentLimit.ToString());
+ xtw.WriteElementString("ObjectBonus", settings.ObjectBonus.ToString());
+ xtw.WriteEndElement();
+
+ xtw.WriteStartElement("GroundTextures");
+ xtw.WriteElementString("Texture1", settings.TerrainTexture1.ToString());
+ xtw.WriteElementString("Texture2", settings.TerrainTexture2.ToString());
+ xtw.WriteElementString("Texture3", settings.TerrainTexture3.ToString());
+ xtw.WriteElementString("Texture4", settings.TerrainTexture4.ToString());
+ xtw.WriteElementString("ElevationLowSW", settings.Elevation1SW.ToString());
+ xtw.WriteElementString("ElevationLowNW", settings.Elevation1NW.ToString());
+ xtw.WriteElementString("ElevationLowSE", settings.Elevation1SE.ToString());
+ xtw.WriteElementString("ElevationLowNE", settings.Elevation1NE.ToString());
+ xtw.WriteElementString("ElevationHighSW", settings.Elevation2SW.ToString());
+ xtw.WriteElementString("ElevationHighNW", settings.Elevation2NW.ToString());
+ xtw.WriteElementString("ElevationHighSE", settings.Elevation2SE.ToString());
+ xtw.WriteElementString("ElevationHighNE", settings.Elevation2NE.ToString());
+ xtw.WriteEndElement();
+
+ xtw.WriteStartElement("Terrain");
+ xtw.WriteElementString("WaterHeight", settings.WaterHeight.ToString());
+ xtw.WriteElementString("TerrainRaiseLimit", settings.TerrainRaiseLimit.ToString());
+ xtw.WriteElementString("TerrainLowerLimit", settings.TerrainLowerLimit.ToString());
+ xtw.WriteElementString("UseEstateSun", settings.UseEstateSun.ToString());
+ xtw.WriteElementString("FixedSun", settings.FixedSun.ToString());
+ // XXX: Need to expose interface to get sun phase information from sun module
+ // xtw.WriteStartElement("SunPhase",
+ xtw.WriteEndElement();
+
+ xtw.WriteEndElement();
+
+ xtw.Close();
+ sw.Close();
+
+ return sw.ToString();
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/TarArchiveReader.cs b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveReader.cs
new file mode 100644
index 0000000..506d770
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveReader.cs
@@ -0,0 +1,195 @@
+/*
+ * 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 OpenSim 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.IO;
+using System.Reflection;
+using System.Text;
+using log4net;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Temporary code to do the bare minimum required to read a tar archive for our purposes
+ ///
+ public class TarArchiveReader
+ {
+ //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ public enum TarEntryType
+ {
+ TYPE_UNKNOWN = 0,
+ TYPE_NORMAL_FILE = 1,
+ TYPE_HARD_LINK = 2,
+ TYPE_SYMBOLIC_LINK = 3,
+ TYPE_CHAR_SPECIAL = 4,
+ TYPE_BLOCK_SPECIAL = 5,
+ TYPE_DIRECTORY = 6,
+ TYPE_FIFO = 7,
+ TYPE_CONTIGUOUS_FILE = 8,
+ }
+
+ protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding();
+
+ ///
+ /// Binary reader for the underlying stream
+ ///
+ protected BinaryReader m_br;
+
+ ///
+ /// Used to trim off null chars
+ ///
+ protected char[] m_nullCharArray = new char[] { '\0' };
+
+ ///
+ /// Generate a tar reader which reads from the given stream.
+ ///
+ ///
+ public TarArchiveReader(Stream s)
+ {
+ m_br = new BinaryReader(s);
+ }
+
+ ///
+ /// Read the next entry in the tar file.
+ ///
+ ///
+ /// the data for the entry. Returns null if there are no more entries
+ public byte[] ReadEntry(out string filePath, out TarEntryType entryType)
+ {
+ filePath = String.Empty;
+ entryType = TarEntryType.TYPE_UNKNOWN;
+ TarHeader header = ReadHeader();
+
+ if (null == header)
+ return null;
+
+ entryType = header.EntryType;
+ filePath = header.FilePath;
+ byte[] data = m_br.ReadBytes(header.FileSize);
+
+ //m_log.DebugFormat("[TAR ARCHIVE READER]: filePath {0}, fileSize {1}", filePath, header.FileSize);
+
+ // Read the rest of the empty padding in the 512 byte block
+ if (header.FileSize % 512 != 0)
+ {
+ int paddingLeft = 512 - (header.FileSize % 512);
+
+ //m_log.DebugFormat("[TAR ARCHIVE READER]: Reading {0} padding bytes", paddingLeft);
+
+ m_br.ReadBytes(paddingLeft);
+ }
+
+ return data;
+ }
+
+ ///
+ /// Read the next 512 byte chunk of data as a tar header.
+ ///
+ /// A tar header struct. null if we have reached the end of the archive.
+ protected TarHeader ReadHeader()
+ {
+ byte[] header = m_br.ReadBytes(512);
+
+ // If we've reached the end of the archive we'll be in null block territory, which means
+ // the next byte will be 0
+ if (header[0] == 0)
+ return null;
+
+ TarHeader tarHeader = new TarHeader();
+
+ tarHeader.FilePath = m_asciiEncoding.GetString(header, 0, 100);
+ tarHeader.FilePath = tarHeader.FilePath.Trim(m_nullCharArray);
+ tarHeader.FileSize = ConvertOctalBytesToDecimal(header, 124, 11);
+
+ switch (header[156])
+ {
+ case 0:
+ tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE;
+ break;
+ case (byte)'0':
+ tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE;
+ break;
+ case (byte)'1':
+ tarHeader.EntryType = TarEntryType.TYPE_HARD_LINK;
+ break;
+ case (byte)'2':
+ tarHeader.EntryType = TarEntryType.TYPE_SYMBOLIC_LINK;
+ break;
+ case (byte)'3':
+ tarHeader.EntryType = TarEntryType.TYPE_CHAR_SPECIAL;
+ break;
+ case (byte)'4':
+ tarHeader.EntryType = TarEntryType.TYPE_BLOCK_SPECIAL;
+ break;
+ case (byte)'5':
+ tarHeader.EntryType = TarEntryType.TYPE_DIRECTORY;
+ break;
+ case (byte)'6':
+ tarHeader.EntryType = TarEntryType.TYPE_FIFO;
+ break;
+ case (byte)'7':
+ tarHeader.EntryType = TarEntryType.TYPE_CONTIGUOUS_FILE;
+ break;
+ }
+
+ return tarHeader;
+ }
+
+ public void Close()
+ {
+ m_br.Close();
+ }
+
+ ///
+ /// Convert octal bytes to a decimal representation
+ ///
+ ///
+ ///
+ public static int ConvertOctalBytesToDecimal(byte[] bytes, int startIndex, int count)
+ {
+ string oString = m_asciiEncoding.GetString(bytes, startIndex, count);
+
+ int d = 0;
+
+ foreach (char c in oString)
+ {
+ d <<= 3;
+ d |= c - '0';
+ }
+
+ return d;
+ }
+ }
+
+ public class TarHeader
+ {
+ public string FilePath;
+ public int FileSize;
+ public TarArchiveReader.TarEntryType EntryType;
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/TarArchiveWriter.cs b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveWriter.cs
new file mode 100644
index 0000000..437939e
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveWriter.cs
@@ -0,0 +1,202 @@
+/*
+ * 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 OpenSim 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.IO;
+using System.Text;
+using System.Reflection;
+using log4net;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Temporary code to produce a tar archive in tar v7 format
+ ///
+ public class TarArchiveWriter
+ {
+ //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ protected Dictionary m_files = new Dictionary();
+
+ protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding();
+
+ ///
+ /// Add a directory to the tar archive. We can only handle one path level right now!
+ ///
+ ///
+ public void AddDir(string dirName)
+ {
+ // Directories are signalled by a final /
+ if (!dirName.EndsWith("/"))
+ dirName += "/";
+
+ AddFile(dirName, new byte[0]);
+ }
+
+ ///
+ /// Add a file to the tar archive
+ ///
+ ///
+ ///
+ public void AddFile(string filePath, string data)
+ {
+ AddFile(filePath, m_asciiEncoding.GetBytes(data));
+ }
+
+ ///
+ /// Add a file to the tar archive
+ ///
+ ///
+ ///
+ public void AddFile(string filePath, byte[] data)
+ {
+ m_files[filePath] = data;
+ }
+
+ ///
+ /// Write the raw tar archive data to a stream. The stream will be closed on completion.
+ ///
+ /// Stream to which to write the data
+ ///
+ public void WriteTar(Stream s)
+ {
+ BinaryWriter bw = new BinaryWriter(s);
+
+ foreach (string filePath in m_files.Keys)
+ {
+ byte[] header = new byte[512];
+ byte[] data = m_files[filePath];
+
+ // file path field (100)
+ byte[] nameBytes = m_asciiEncoding.GetBytes(filePath);
+ int nameSize = (nameBytes.Length >= 100) ? 100 : nameBytes.Length;
+ Array.Copy(nameBytes, header, nameSize);
+
+ // file mode (8)
+ byte[] modeBytes = m_asciiEncoding.GetBytes("0000777");
+ Array.Copy(modeBytes, 0, header, 100, 7);
+
+ // owner user id (8)
+ byte[] ownerIdBytes = m_asciiEncoding.GetBytes("0000764");
+ Array.Copy(ownerIdBytes, 0, header, 108, 7);
+
+ // group user id (8)
+ byte[] groupIdBytes = m_asciiEncoding.GetBytes("0000764");
+ Array.Copy(groupIdBytes, 0, header, 116, 7);
+
+ // file size in bytes (12)
+ int fileSize = data.Length;
+ //m_log.DebugFormat("[TAR ARCHIVE WRITER]: File size of {0} is {1}", filePath, fileSize);
+
+ byte[] fileSizeBytes = ConvertDecimalToPaddedOctalBytes(fileSize, 11);
+
+ Array.Copy(fileSizeBytes, 0, header, 124, 11);
+
+ // last modification time (12)
+ byte[] lastModTimeBytes = m_asciiEncoding.GetBytes("11017037332");
+ Array.Copy(lastModTimeBytes, 0, header, 136, 11);
+
+ // link indicator (1)
+ //header[156] = m_asciiEncoding.GetBytes("0")[0];
+ if (filePath.EndsWith("/"))
+ {
+ header[156] = m_asciiEncoding.GetBytes("5")[0];
+ }
+ else
+ {
+ header[156] = 0;
+ }
+
+ Array.Copy(m_asciiEncoding.GetBytes("0000000"), 0, header, 329, 7);
+ Array.Copy(m_asciiEncoding.GetBytes("0000000"), 0, header, 337, 7);
+
+ // check sum for header block (8) [calculated last]
+ Array.Copy(m_asciiEncoding.GetBytes(" "), 0, header, 148, 8);
+
+ int checksum = 0;
+ foreach (byte b in header)
+ {
+ checksum += b;
+ }
+
+ //m_log.DebugFormat("[TAR ARCHIVE WRITER]: Decimal header checksum is {0}", checksum);
+
+ byte[] checkSumBytes = ConvertDecimalToPaddedOctalBytes(checksum, 6);
+
+ Array.Copy(checkSumBytes, 0, header, 148, 6);
+
+ header[154] = 0;
+
+ // Write out header
+ bw.Write(header);
+
+ // Write out data
+ bw.Write(data);
+
+ if (data.Length % 512 != 0)
+ {
+ int paddingRequired = 512 - (data.Length % 512);
+
+ //m_log.DebugFormat("[TAR ARCHIVE WRITER]: Padding data with {0} bytes", paddingRequired);
+
+ byte[] padding = new byte[paddingRequired];
+ bw.Write(padding);
+ }
+ }
+
+ //m_log.Debug("[TAR ARCHIVE WRITER]: Writing final consecutive 0 blocks");
+
+ // Write two consecutive 0 blocks to end the archive
+ byte[] finalZeroPadding = new byte[1024];
+ bw.Write(finalZeroPadding);
+
+ bw.Flush();
+ bw.Close();
+ }
+
+ public static byte[] ConvertDecimalToPaddedOctalBytes(int d, int padding)
+ {
+ string oString = "";
+
+ while (d > 0)
+ {
+ oString = Convert.ToString((byte)'0' + d & 7) + oString;
+ d >>= 3;
+ }
+
+ while (oString.Length < padding)
+ {
+ oString = "0" + oString;
+ }
+
+ byte[] oBytes = m_asciiEncoding.GetBytes(oString);
+
+ return oBytes;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
new file mode 100644
index 0000000..a14e0f6
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
@@ -0,0 +1,188 @@
+/*
+ * 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 OpenSim 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.IO;
+using System.Threading;
+using NUnit.Framework;
+using NUnit.Framework.SyntaxHelpers;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.CoreModules.World.Archiver;
+using OpenSim.Region.CoreModules.World.Serialiser;
+using OpenSim.Region.CoreModules.World.Terrain;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Tests.Common.Setup;
+
+namespace OpenSim.Region.CoreModules.World.Archiver.Tests
+{
+ [TestFixture]
+ public class ArchiverTests
+ {
+ private EventWaitHandle m_waitHandle = new AutoResetEvent(false);
+
+ private void SaveCompleted(string errorMessage)
+ {
+ m_waitHandle.Set();
+ }
+
+ ///
+ /// Test saving a V0.2 OpenSim Region Archive.
+ ///
+ [Test]
+ public void TestSaveOarV0p2()
+ {
+ log4net.Config.XmlConfigurator.Configure();
+
+ ArchiverModule archiverModule = new ArchiverModule();
+ SerialiserModule serialiserModule = new SerialiserModule();
+ TerrainModule terrainModule = new TerrainModule();
+
+ Scene scene = SceneSetupHelpers.SetupScene();
+ SceneSetupHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule);
+
+ SceneObjectPart part1;
+
+ // Create and add prim 1
+ {
+ string partName = "My Little Pony";
+ UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000015");
+ PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere();
+ Vector3 groupPosition = new Vector3(10, 20, 30);
+ Quaternion rotationOffset = new Quaternion(20, 30, 40, 50);
+ Vector3 offsetPosition = new Vector3(5, 10, 15);
+
+ part1
+ = new SceneObjectPart(
+ ownerId, shape, groupPosition, rotationOffset, offsetPosition);
+ part1.Name = partName;
+
+ scene.AddNewSceneObject(new SceneObjectGroup(part1), false);
+ }
+
+ SceneObjectPart part2;
+
+ // Create and add prim 2
+ {
+ string partName = "Action Man";
+ UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000016");
+ PrimitiveBaseShape shape = PrimitiveBaseShape.CreateCylinder();
+ Vector3 groupPosition = new Vector3(90, 80, 70);
+ Quaternion rotationOffset = new Quaternion(60, 70, 80, 90);
+ Vector3 offsetPosition = new Vector3(20, 25, 30);
+
+ part2
+ = new SceneObjectPart(
+ ownerId, shape, groupPosition, rotationOffset, offsetPosition);
+ part2.Name = partName;
+
+ scene.AddNewSceneObject(new SceneObjectGroup(part2), false);
+ }
+
+ MemoryStream archiveWriteStream = new MemoryStream();
+
+ scene.EventManager.OnOarFileSaved += SaveCompleted;
+ archiverModule.ArchiveRegion(archiveWriteStream);
+ m_waitHandle.WaitOne(60000, true);
+
+ byte[] archive = archiveWriteStream.ToArray();
+ MemoryStream archiveReadStream = new MemoryStream(archive);
+ TarArchiveReader tar = new TarArchiveReader(archiveReadStream);
+
+ bool gotControlFile = false;
+ bool gotObject1File = false;
+ bool gotObject2File = false;
+ string expectedObject1FileName = string.Format(
+ "{0}_{1:000}-{2:000}-{3:000}__{4}.xml",
+ part1.Name,
+ Math.Round(part1.GroupPosition.X), Math.Round(part1.GroupPosition.Y), Math.Round(part1.GroupPosition.Z),
+ part1.UUID);
+ string expectedObject2FileName = string.Format(
+ "{0}_{1:000}-{2:000}-{3:000}__{4}.xml",
+ part2.Name,
+ Math.Round(part2.GroupPosition.X), Math.Round(part2.GroupPosition.Y), Math.Round(part2.GroupPosition.Z),
+ part2.UUID);
+
+ string filePath;
+ TarArchiveReader.TarEntryType tarEntryType;
+
+ while (tar.ReadEntry(out filePath, out tarEntryType) != null)
+ {
+ if (ArchiveConstants.CONTROL_FILE_PATH == filePath)
+ {
+ gotControlFile = true;
+ }
+ else if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
+ {
+ string fileName = filePath.Remove(0, ArchiveConstants.OBJECTS_PATH.Length);
+
+ if (fileName.StartsWith(part1.Name))
+ {
+ Assert.That(fileName, Is.EqualTo(expectedObject1FileName));
+ gotObject1File = true;
+ }
+ else if (fileName.StartsWith(part2.Name))
+ {
+ Assert.That(fileName, Is.EqualTo(expectedObject2FileName));
+ gotObject2File = true;
+ }
+ }
+ }
+
+ Assert.That(gotControlFile, Is.True, "No control file in archive");
+ Assert.That(gotObject1File, Is.True, "No object1 file in archive");
+ Assert.That(gotObject2File, Is.True, "No object2 file in archive");
+
+ // TODO: Test presence of more files and contents of files.
+ }
+
+ ///
+ /// Test loading a V0.2 OpenSim Region Archive. Does not yet do what it says on the tin.
+ ///
+ [Test]
+ public void TestLoadOarV0p2()
+ {
+ MemoryStream archiveWriteStream = new MemoryStream();
+ TarArchiveWriter tar = new TarArchiveWriter();
+
+ tar.AddFile(ArchiveConstants.CONTROL_FILE_PATH, ArchiveWriteRequestExecution.Create0p2ControlFile());
+ tar.WriteTar(archiveWriteStream);
+
+ MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray());
+
+ ArchiverModule archiverModule = new ArchiverModule();
+
+ Scene scene = SceneSetupHelpers.SetupScene();
+ SceneSetupHelpers.SetupSceneModules(scene, archiverModule);
+
+ archiverModule.DearchiveRegion(archiveReadStream);
+
+ // TODO: Okay, so nothing is tested yet apart from the fact that it doesn't blow up
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
new file mode 100644
index 0000000..8b15308
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
@@ -0,0 +1,1012 @@
+/*
+ * 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 OpenSim 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.Threading;
+using System.Collections.Generic;
+using System.Reflection;
+using OpenMetaverse;
+using log4net;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+
+namespace OpenSim.Region.CoreModules.World.Estate
+{
+ public class EstateManagementModule : IEstateModule
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private delegate void LookupUUIDS(List uuidLst);
+
+ private Scene m_scene;
+
+ private EstateTerrainXferHandler TerrainUploader = null;
+
+ #region Packet Data Responders
+
+ private void sendDetailedEstateData(IClientAPI remote_client, UUID invoice)
+ {
+ uint sun = 0;
+
+ if (!m_scene.RegionInfo.EstateSettings.UseGlobalTime)
+ sun=(uint)(m_scene.RegionInfo.EstateSettings.SunPosition*1024.0) + 0x1800;
+ UUID estateOwner;
+ if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero)
+ estateOwner = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ else
+ estateOwner = m_scene.RegionInfo.MasterAvatarAssignedUUID;
+
+ if (m_scene.Permissions.IsGod(remote_client.AgentId))
+ estateOwner = remote_client.AgentId;
+
+ remote_client.SendDetailedEstateData(invoice,
+ m_scene.RegionInfo.EstateSettings.EstateName,
+ m_scene.RegionInfo.EstateSettings.EstateID,
+ m_scene.RegionInfo.EstateSettings.ParentEstateID,
+ GetEstateFlags(),
+ sun,
+ m_scene.RegionInfo.RegionSettings.Covenant,
+ m_scene.RegionInfo.EstateSettings.AbuseEmail,
+ estateOwner);
+
+ remote_client.SendEstateManagersList(invoice,
+ m_scene.RegionInfo.EstateSettings.EstateManagers,
+ m_scene.RegionInfo.EstateSettings.EstateID);
+
+ remote_client.SendBannedUserList(invoice,
+ m_scene.RegionInfo.EstateSettings.EstateBans,
+ m_scene.RegionInfo.EstateSettings.EstateID);
+ }
+
+ private void estateSetRegionInfoHandler(bool blockTerraform, bool noFly, bool allowDamage, bool blockLandResell, int maxAgents, float objectBonusFactor,
+ int matureLevel, bool restrictPushObject, bool allowParcelChanges)
+ {
+ if (blockTerraform)
+ m_scene.RegionInfo.RegionSettings.BlockTerraform = true;
+ else
+ m_scene.RegionInfo.RegionSettings.BlockTerraform = false;
+
+ if (noFly)
+ m_scene.RegionInfo.RegionSettings.BlockFly = true;
+ else
+ m_scene.RegionInfo.RegionSettings.BlockFly = false;
+
+ if (allowDamage)
+ m_scene.RegionInfo.RegionSettings.AllowDamage = true;
+ else
+ m_scene.RegionInfo.RegionSettings.AllowDamage = false;
+
+ if (blockLandResell)
+ m_scene.RegionInfo.RegionSettings.AllowLandResell = false;
+ else
+ m_scene.RegionInfo.RegionSettings.AllowLandResell = true;
+
+ m_scene.RegionInfo.RegionSettings.AgentLimit = (byte) maxAgents;
+
+ m_scene.RegionInfo.RegionSettings.ObjectBonus = objectBonusFactor;
+
+ if (matureLevel <= 13)
+ m_scene.RegionInfo.RegionSettings.Maturity = 0;
+ else
+ m_scene.RegionInfo.RegionSettings.Maturity = 1;
+
+ if (restrictPushObject)
+ m_scene.RegionInfo.RegionSettings.RestrictPushing = true;
+ else
+ m_scene.RegionInfo.RegionSettings.RestrictPushing = false;
+
+ if (allowParcelChanges)
+ m_scene.RegionInfo.RegionSettings.AllowLandJoinDivide = true;
+ else
+ m_scene.RegionInfo.RegionSettings.AllowLandJoinDivide = false;
+
+ m_scene.RegionInfo.RegionSettings.Save();
+
+ sendRegionInfoPacketToAll();
+ }
+
+ public void setEstateTerrainBaseTexture(IClientAPI remoteClient, int corner, UUID texture)
+ {
+ if (texture == UUID.Zero)
+ return;
+
+ switch (corner)
+ {
+ case 0:
+ m_scene.RegionInfo.RegionSettings.TerrainTexture1 = texture;
+ break;
+ case 1:
+ m_scene.RegionInfo.RegionSettings.TerrainTexture2 = texture;
+ break;
+ case 2:
+ m_scene.RegionInfo.RegionSettings.TerrainTexture3 = texture;
+ break;
+ case 3:
+ m_scene.RegionInfo.RegionSettings.TerrainTexture4 = texture;
+ break;
+ }
+ m_scene.RegionInfo.RegionSettings.Save();
+ }
+
+ public void setEstateTerrainTextureHeights(IClientAPI client, int corner, float lowValue, float highValue)
+ {
+ switch (corner)
+ {
+ case 0:
+ m_scene.RegionInfo.RegionSettings.Elevation1SW = lowValue;
+ m_scene.RegionInfo.RegionSettings.Elevation2SW = highValue;
+ break;
+ case 1:
+ m_scene.RegionInfo.RegionSettings.Elevation1NW = lowValue;
+ m_scene.RegionInfo.RegionSettings.Elevation2NW = highValue;
+ break;
+ case 2:
+ m_scene.RegionInfo.RegionSettings.Elevation1SE = lowValue;
+ m_scene.RegionInfo.RegionSettings.Elevation2SE = highValue;
+ break;
+ case 3:
+ m_scene.RegionInfo.RegionSettings.Elevation1NE = lowValue;
+ m_scene.RegionInfo.RegionSettings.Elevation2NE = highValue;
+ break;
+ }
+ m_scene.RegionInfo.RegionSettings.Save();
+ }
+
+ private void handleCommitEstateTerrainTextureRequest(IClientAPI remoteClient)
+ {
+ sendRegionHandshakeToAll();
+ }
+
+ public void setRegionTerrainSettings(float WaterHeight,
+ float TerrainRaiseLimit, float TerrainLowerLimit,
+ bool UseEstateSun, bool UseFixedSun, float SunHour,
+ bool UseGlobal, bool EstateFixedSun, float EstateSunHour)
+ {
+ // Water Height
+ m_scene.RegionInfo.RegionSettings.WaterHeight = WaterHeight;
+
+ // Terraforming limits
+ m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit = TerrainRaiseLimit;
+ m_scene.RegionInfo.RegionSettings.TerrainLowerLimit = TerrainLowerLimit;
+
+ // Time of day / fixed sun
+ m_scene.RegionInfo.RegionSettings.UseEstateSun = UseEstateSun;
+ m_scene.RegionInfo.RegionSettings.FixedSun = UseFixedSun;
+ m_scene.RegionInfo.RegionSettings.SunPosition = SunHour;
+
+ m_scene.EventManager.TriggerEstateToolsTimeUpdate(m_scene.RegionInfo.RegionHandle, UseFixedSun, UseEstateSun, SunHour);
+
+ //m_log.Debug("[ESTATE]: UFS: " + UseFixedSun.ToString());
+ //m_log.Debug("[ESTATE]: SunHour: " + SunHour.ToString());
+
+ sendRegionInfoPacketToAll();
+ m_scene.RegionInfo.RegionSettings.Save();
+ }
+
+ private void handleEstateRestartSimRequest(IClientAPI remoteClient, int timeInSeconds)
+ {
+ m_scene.Restart(timeInSeconds);
+ }
+
+ private void handleChangeEstateCovenantRequest(IClientAPI remoteClient, UUID estateCovenantID)
+ {
+ m_scene.RegionInfo.RegionSettings.Covenant = estateCovenantID;
+ m_scene.RegionInfo.RegionSettings.Save();
+ }
+
+ private void handleEstateAccessDeltaRequest(IClientAPI remote_client, UUID invoice, int estateAccessType, UUID user)
+ {
+ // EstateAccessDelta handles Estate Managers, Sim Access, Sim Banlist, allowed Groups.. etc.
+
+ if (user == m_scene.RegionInfo.EstateSettings.EstateOwner)
+ return; // never process EO
+ if (user == m_scene.RegionInfo.MasterAvatarAssignedUUID)
+ return; // never process owner
+
+ switch (estateAccessType)
+ {
+ case 64:
+ if (m_scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, false) || m_scene.Permissions.BypassPermissions())
+ {
+ EstateBan[] banlistcheck = m_scene.RegionInfo.EstateSettings.EstateBans;
+
+ bool alreadyInList = false;
+
+ for (int i = 0; i < banlistcheck.Length; i++)
+ {
+ if (user == banlistcheck[i].bannedUUID)
+ {
+ alreadyInList = true;
+ break;
+ }
+
+ }
+ if (!alreadyInList)
+ {
+
+ EstateBan item = new EstateBan();
+
+ item.bannedUUID = user;
+ item.estateID = m_scene.RegionInfo.EstateSettings.EstateID;
+ item.bannedIP = "0.0.0.0";
+ item.bannedIPHostMask = "0.0.0.0";
+
+ m_scene.RegionInfo.EstateSettings.AddBan(item);
+ m_scene.RegionInfo.EstateSettings.Save();
+
+ ScenePresence s = m_scene.GetScenePresence(user);
+ if (s != null)
+ {
+ if (!s.IsChildAgent)
+ {
+ s.ControllingClient.SendTeleportLocationStart();
+ m_scene.TeleportClientHome(user, s.ControllingClient);
+ }
+ }
+
+ }
+ else
+ {
+ remote_client.SendAlertMessage("User is already on the region ban list");
+ }
+ //m_scene.RegionInfo.regionBanlist.Add(Manager(user);
+ remote_client.SendBannedUserList(invoice, m_scene.RegionInfo.EstateSettings.EstateBans, m_scene.RegionInfo.EstateSettings.EstateID);
+ }
+ else
+ {
+ remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
+ }
+ break;
+ case 128:
+ if (m_scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, false) || m_scene.Permissions.BypassPermissions())
+ {
+ EstateBan[] banlistcheck = m_scene.RegionInfo.EstateSettings.EstateBans;
+
+ bool alreadyInList = false;
+ EstateBan listitem = null;
+
+ for (int i = 0; i < banlistcheck.Length; i++)
+ {
+ if (user == banlistcheck[i].bannedUUID)
+ {
+ alreadyInList = true;
+ listitem = banlistcheck[i];
+ break;
+ }
+
+ }
+ if (alreadyInList && listitem != null)
+ {
+ m_scene.RegionInfo.EstateSettings.RemoveBan(listitem.bannedUUID);
+ m_scene.RegionInfo.EstateSettings.Save();
+ }
+ else
+ {
+ remote_client.SendAlertMessage("User is not on the region ban list");
+ }
+ //m_scene.RegionInfo.regionBanlist.Add(Manager(user);
+ remote_client.SendBannedUserList(invoice, m_scene.RegionInfo.EstateSettings.EstateBans, m_scene.RegionInfo.EstateSettings.EstateID);
+ }
+ else
+ {
+ remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
+ }
+ break;
+ case 256:
+
+ if (m_scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true) || m_scene.Permissions.BypassPermissions())
+ {
+ m_scene.RegionInfo.EstateSettings.AddEstateManager(user);
+ m_scene.RegionInfo.EstateSettings.Save();
+ remote_client.SendEstateManagersList(invoice, m_scene.RegionInfo.EstateSettings.EstateManagers, m_scene.RegionInfo.EstateSettings.EstateID);
+ }
+ else
+ {
+ remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
+ }
+
+ break;
+ case 512:
+ if (m_scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true) || m_scene.Permissions.BypassPermissions())
+ {
+ m_scene.RegionInfo.EstateSettings.RemoveEstateManager(user);
+ m_scene.RegionInfo.EstateSettings.Save();
+
+ remote_client.SendEstateManagersList(invoice, m_scene.RegionInfo.EstateSettings.EstateManagers, m_scene.RegionInfo.EstateSettings.EstateID);
+ }
+ else
+ {
+ remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
+ }
+ break;
+
+ default:
+
+ m_log.ErrorFormat("EstateOwnerMessage: Unknown EstateAccessType requested in estateAccessDelta: {0}", estateAccessType.ToString());
+ break;
+ }
+ }
+
+ private void SendSimulatorBlueBoxMessage(
+ IClientAPI remote_client, UUID invoice, UUID senderID, UUID sessionID, string senderName, string message)
+ {
+ IDialogModule dm = m_scene.RequestModuleInterface();
+
+ if (dm != null)
+ dm.SendNotificationToUsersInRegion(senderID, senderName, message);
+ }
+
+ private void SendEstateBlueBoxMessage(
+ IClientAPI remote_client, UUID invoice, UUID senderID, UUID sessionID, string senderName, string message)
+ {
+ IDialogModule dm = m_scene.RequestModuleInterface();
+
+ if (dm != null)
+ dm.SendNotificationToUsersInEstate(senderID, senderName, message);
+ }
+
+ private void handleEstateDebugRegionRequest(IClientAPI remote_client, UUID invoice, UUID senderID, bool scripted, bool collisionEvents, bool physics)
+ {
+ if (physics)
+ m_scene.RegionInfo.RegionSettings.DisablePhysics = true;
+ else
+ m_scene.RegionInfo.RegionSettings.DisablePhysics = false;
+
+ if (scripted)
+ m_scene.RegionInfo.RegionSettings.DisableScripts = true;
+ else
+ m_scene.RegionInfo.RegionSettings.DisableScripts = false;
+
+ if (collisionEvents)
+ m_scene.RegionInfo.RegionSettings.DisableCollisions = true;
+ else
+ m_scene.RegionInfo.RegionSettings.DisableCollisions = false;
+
+
+ m_scene.RegionInfo.RegionSettings.Save();
+
+ m_scene.SetSceneCoreDebug(scripted, collisionEvents, physics);
+ }
+
+ private void handleEstateTeleportOneUserHomeRequest(IClientAPI remover_client, UUID invoice, UUID senderID, UUID prey)
+ {
+ if (prey != UUID.Zero)
+ {
+ ScenePresence s = m_scene.GetScenePresence(prey);
+ if (s != null)
+ {
+ s.ControllingClient.SendTeleportLocationStart();
+ m_scene.TeleportClientHome(prey, s.ControllingClient);
+ }
+ }
+ }
+
+ private void handleEstateTeleportAllUsersHomeRequest(IClientAPI remover_client, UUID invoice, UUID senderID)
+ {
+ // Get a fresh list that will not change as people get teleported away
+ List prescences = m_scene.GetScenePresences();
+ foreach (ScenePresence p in prescences)
+ {
+ if (p.UUID != senderID)
+ {
+ // make sure they are still there, we could be working down a long list
+ ScenePresence s = m_scene.GetScenePresence(p.UUID);
+ if (s != null)
+ {
+ // Also make sure they are actually in the region
+ if (!s.IsChildAgent)
+ {
+ s.ControllingClient.SendTeleportLocationStart();
+ m_scene.TeleportClientHome(s.UUID, s.ControllingClient);
+ }
+ }
+ }
+ }
+ }
+ private void AbortTerrainXferHandler(IClientAPI remoteClient, ulong XferID)
+ {
+ if (TerrainUploader != null)
+ {
+ lock (TerrainUploader)
+ {
+ if (XferID == TerrainUploader.XferID)
+ {
+ remoteClient.OnXferReceive -= TerrainUploader.XferReceive;
+ remoteClient.OnAbortXfer -= AbortTerrainXferHandler;
+ TerrainUploader.TerrainUploadDone -= HandleTerrainApplication;
+
+ TerrainUploader = null;
+ remoteClient.SendAlertMessage("Terrain Upload aborted by the client");
+ }
+ }
+ }
+
+ }
+ private void HandleTerrainApplication(string filename, byte[] terrainData, IClientAPI remoteClient)
+ {
+ lock (TerrainUploader)
+ {
+ remoteClient.OnXferReceive -= TerrainUploader.XferReceive;
+ remoteClient.OnAbortXfer -= AbortTerrainXferHandler;
+ TerrainUploader.TerrainUploadDone -= HandleTerrainApplication;
+
+ TerrainUploader = null;
+ }
+ remoteClient.SendAlertMessage("Terrain Upload Complete. Loading....");
+ OpenSim.Region.CoreModules.World.Terrain.ITerrainModule terr = m_scene.RequestModuleInterface();
+
+ if (terr != null)
+ {
+ m_log.Warn("[CLIENT]: Got Request to Send Terrain in region " + m_scene.RegionInfo.RegionName);
+ if (System.IO.File.Exists(Util.dataDir() + "/terrain.raw"))
+ {
+ System.IO.File.Delete(Util.dataDir() + "/terrain.raw");
+ }
+ try
+ {
+ System.IO.FileStream input = new System.IO.FileStream(Util.dataDir() + "/terrain.raw", System.IO.FileMode.CreateNew);
+ input.Write(terrainData, 0, terrainData.Length);
+ input.Close();
+ }
+ catch (System.IO.IOException e)
+ {
+ m_log.ErrorFormat("[TERRAIN]: Error Saving a terrain file uploaded via the estate tools. It gave us the following error: {0}", e.ToString());
+ remoteClient.SendAlertMessage("There was an IO Exception loading your terrain. Please check free space");
+
+ return;
+ }
+ catch (System.Security.SecurityException e)
+ {
+ m_log.ErrorFormat("[TERRAIN]: Error Saving a terrain file uploaded via the estate tools. It gave us the following error: {0}", e.ToString());
+ remoteClient.SendAlertMessage("There was a security Exception loading your terrain. Please check the security on the simulator drive");
+
+ return;
+ }
+ catch (System.UnauthorizedAccessException e)
+ {
+ m_log.ErrorFormat("[TERRAIN]: Error Saving a terrain file uploaded via the estate tools. It gave us the following error: {0}", e.ToString());
+ remoteClient.SendAlertMessage("There was a security Exception loading your terrain. Please check the security on the simulator drive");
+
+ return;
+ }
+
+
+
+
+ try
+ {
+ terr.LoadFromFile(Util.dataDir() + "/terrain.raw");
+ remoteClient.SendAlertMessage("Your terrain was loaded. Give it a minute or two to apply");
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[TERRAIN]: Error loading a terrain file uploaded via the estate tools. It gave us the following error: {0}", e.ToString());
+ remoteClient.SendAlertMessage("There was a general error loading your terrain. Please fix the terrain file and try again");
+ }
+
+ }
+ else
+ {
+ remoteClient.SendAlertMessage("Unable to apply terrain. Cannot get an instance of the terrain module");
+ }
+
+
+
+ }
+
+ private void handleUploadTerrain(IClientAPI remote_client, string clientFileName)
+ {
+
+ if (TerrainUploader == null)
+ {
+
+ TerrainUploader = new EstateTerrainXferHandler(remote_client, clientFileName);
+ lock (TerrainUploader)
+ {
+ remote_client.OnXferReceive += TerrainUploader.XferReceive;
+ remote_client.OnAbortXfer += AbortTerrainXferHandler;
+ TerrainUploader.TerrainUploadDone += HandleTerrainApplication;
+ }
+ TerrainUploader.RequestStartXfer(remote_client);
+
+ }
+ else
+ {
+ remote_client.SendAlertMessage("Another Terrain Upload is in progress. Please wait your turn!");
+ }
+
+ }
+ private void handleTerrainRequest(IClientAPI remote_client, string clientFileName)
+ {
+ // Save terrain here
+ OpenSim.Region.CoreModules.World.Terrain.ITerrainModule terr = m_scene.RequestModuleInterface();
+
+ if (terr != null)
+ {
+ m_log.Warn("[CLIENT]: Got Request to Send Terrain in region " + m_scene.RegionInfo.RegionName);
+ if (System.IO.File.Exists(Util.dataDir() + "/terrain.raw"))
+ {
+ System.IO.File.Delete(Util.dataDir() + "/terrain.raw");
+ }
+ terr.SaveToFile(Util.dataDir() + "/terrain.raw");
+
+ System.IO.FileStream input = new System.IO.FileStream(Util.dataDir() + "/terrain.raw", System.IO.FileMode.Open);
+ byte[] bdata = new byte[input.Length];
+ input.Read(bdata, 0, (int)input.Length);
+ remote_client.SendAlertMessage("Terrain file written, starting download...");
+ m_scene.XferManager.AddNewFile("terrain.raw", bdata);
+ // Tell client about it
+ m_log.Warn("[CLIENT]: Sending Terrain to " + remote_client.Name);
+ remote_client.SendInitiateDownload("terrain.raw", clientFileName);
+ }
+ }
+
+ private void HandleRegionInfoRequest(IClientAPI remote_client)
+ {
+ RegionInfoForEstateMenuArgs args = new RegionInfoForEstateMenuArgs();
+ args.billableFactor = m_scene.RegionInfo.EstateSettings.BillableFactor;
+ args.estateID = m_scene.RegionInfo.EstateSettings.EstateID;
+ args.maxAgents = (byte)m_scene.RegionInfo.RegionSettings.AgentLimit;
+ args.objectBonusFactor = (float)m_scene.RegionInfo.RegionSettings.ObjectBonus;
+ args.parentEstateID = m_scene.RegionInfo.EstateSettings.ParentEstateID;
+ args.pricePerMeter = m_scene.RegionInfo.EstateSettings.PricePerMeter;
+ args.redirectGridX = m_scene.RegionInfo.EstateSettings.RedirectGridX;
+ args.redirectGridY = m_scene.RegionInfo.EstateSettings.RedirectGridY;
+ args.regionFlags = GetRegionFlags();
+ byte mature = 13;
+ if (m_scene.RegionInfo.RegionSettings.Maturity == 1)
+ mature = 21;
+ args.simAccess = mature;
+
+ args.sunHour = (float)m_scene.RegionInfo.RegionSettings.SunPosition;
+ args.terrainLowerLimit = (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit;
+ args.terrainRaiseLimit = (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit;
+ args.useEstateSun = m_scene.RegionInfo.RegionSettings.UseEstateSun;
+ args.waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
+ args.simName = m_scene.RegionInfo.RegionName;
+
+ remote_client.SendRegionInfoToEstateMenu(args);
+ }
+
+ private void HandleEstateCovenantRequest(IClientAPI remote_client)
+ {
+ remote_client.SendEstateCovenantInformation(m_scene.RegionInfo.RegionSettings.Covenant);
+ }
+
+ private void HandleLandStatRequest(int parcelID, uint reportType, uint requestFlags, string filter, IClientAPI remoteClient)
+ {
+ Dictionary SceneData = new Dictionary();
+ List uuidNameLookupList = new List();
+
+ if (reportType == 1)
+ {
+ SceneData = m_scene.PhysicsScene.GetTopColliders();
+ }
+ else if (reportType == 0)
+ {
+ SceneData = m_scene.m_sceneGraph.GetTopScripts();
+ }
+
+ List SceneReport = new List();
+ lock (SceneData)
+ {
+ foreach (uint obj in SceneData.Keys)
+ {
+ SceneObjectPart prt = m_scene.GetSceneObjectPart(obj);
+ if (prt != null)
+ {
+ if (prt.ParentGroup != null)
+ {
+ SceneObjectGroup sog = prt.ParentGroup;
+ if (sog != null)
+ {
+ LandStatReportItem lsri = new LandStatReportItem();
+ lsri.LocationX = sog.AbsolutePosition.X;
+ lsri.LocationY = sog.AbsolutePosition.Y;
+ lsri.LocationZ = sog.AbsolutePosition.Z;
+ lsri.Score = SceneData[obj];
+ lsri.TaskID = sog.UUID;
+ lsri.TaskLocalID = sog.LocalId;
+ lsri.TaskName = sog.GetPartName(obj);
+ if (m_scene.CommsManager.UUIDNameCachedTest(sog.OwnerID))
+ {
+ lsri.OwnerName = m_scene.CommsManager.UUIDNameRequestString(sog.OwnerID);
+ }
+ else
+ {
+ lsri.OwnerName = "waiting";
+ lock (uuidNameLookupList)
+ uuidNameLookupList.Add(sog.OwnerID);
+ }
+
+ if (filter.Length != 0)
+ {
+ if ((lsri.OwnerName.Contains(filter) || lsri.TaskName.Contains(filter)))
+ {
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ SceneReport.Add(lsri);
+ }
+ }
+ }
+
+ }
+ }
+ remoteClient.SendLandStatReply(reportType, requestFlags, (uint)SceneReport.Count,SceneReport.ToArray());
+
+ if (uuidNameLookupList.Count > 0)
+ LookupUUID(uuidNameLookupList);
+ }
+
+ private void LookupUUIDSCompleted(IAsyncResult iar)
+ {
+ LookupUUIDS icon = (LookupUUIDS)iar.AsyncState;
+ icon.EndInvoke(iar);
+ }
+ private void LookupUUID(List uuidLst)
+ {
+ LookupUUIDS d = LookupUUIDsAsync;
+
+ d.BeginInvoke(uuidLst,
+ LookupUUIDSCompleted,
+ d);
+ }
+ private void LookupUUIDsAsync(List uuidLst)
+ {
+ UUID[] uuidarr = new UUID[0];
+
+ lock (uuidLst)
+ {
+ uuidarr = uuidLst.ToArray();
+ }
+
+ for (int i = 0; i < uuidarr.Length; i++)
+ {
+ // string lookupname = m_scene.CommsManager.UUIDNameRequestString(uuidarr[i]);
+ m_scene.CommsManager.UUIDNameRequestString(uuidarr[i]);
+ // we drop it. It gets cached though... so we're ready for the next request.
+ }
+ }
+ #endregion
+
+ #region Outgoing Packets
+
+ public void sendRegionInfoPacketToAll()
+ {
+ List avatars = m_scene.GetAvatars();
+
+ for (int i = 0; i < avatars.Count; i++)
+ {
+ HandleRegionInfoRequest(avatars[i].ControllingClient); ;
+ }
+ }
+
+ public void sendRegionHandshake(IClientAPI remoteClient)
+ {
+ RegionHandshakeArgs args = new RegionHandshakeArgs();
+
+ args.isEstateManager = m_scene.RegionInfo.EstateSettings.IsEstateManager(remoteClient.AgentId);
+ if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero && m_scene.RegionInfo.EstateSettings.EstateOwner == remoteClient.AgentId)
+ args.isEstateManager = true;
+
+ args.billableFactor = m_scene.RegionInfo.EstateSettings.BillableFactor;
+ args.terrainStartHeight0 = (float)m_scene.RegionInfo.RegionSettings.Elevation1SW;
+ args.terrainHeightRange0 = (float)m_scene.RegionInfo.RegionSettings.Elevation2SW;
+ args.terrainStartHeight1 = (float)m_scene.RegionInfo.RegionSettings.Elevation1NW;
+ args.terrainHeightRange1 = (float)m_scene.RegionInfo.RegionSettings.Elevation2NW;
+ args.terrainStartHeight2 = (float)m_scene.RegionInfo.RegionSettings.Elevation1SE;
+ args.terrainHeightRange2 = (float)m_scene.RegionInfo.RegionSettings.Elevation2SE;
+ args.terrainStartHeight3 = (float)m_scene.RegionInfo.RegionSettings.Elevation1NE;
+ args.terrainHeightRange3 = (float)m_scene.RegionInfo.RegionSettings.Elevation2NE;
+ byte mature = 13;
+ if (m_scene.RegionInfo.RegionSettings.Maturity == 1)
+ mature = 21;
+ args.simAccess = mature;
+ args.waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
+
+ args.regionFlags = GetRegionFlags();
+ args.regionName = m_scene.RegionInfo.RegionName;
+ if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero)
+ args.SimOwner = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ else
+ args.SimOwner = m_scene.RegionInfo.MasterAvatarAssignedUUID;
+
+ // Fudge estate owner
+ //if (m_scene.Permissions.IsGod(remoteClient.AgentId))
+ // args.SimOwner = remoteClient.AgentId;
+
+ args.terrainBase0 = UUID.Zero;
+ args.terrainBase1 = UUID.Zero;
+ args.terrainBase2 = UUID.Zero;
+ args.terrainBase3 = UUID.Zero;
+ args.terrainDetail0 = m_scene.RegionInfo.RegionSettings.TerrainTexture1;
+ args.terrainDetail1 = m_scene.RegionInfo.RegionSettings.TerrainTexture2;
+ args.terrainDetail2 = m_scene.RegionInfo.RegionSettings.TerrainTexture3;
+ args.terrainDetail3 = m_scene.RegionInfo.RegionSettings.TerrainTexture4;
+
+ remoteClient.SendRegionHandshake(m_scene.RegionInfo,args);
+ }
+
+ public void sendRegionHandshakeToAll()
+ {
+ m_scene.Broadcast(sendRegionHandshake);
+ }
+
+ public void handleEstateChangeInfo(IClientAPI remoteClient, UUID invoice, UUID senderID, UInt32 parms1, UInt32 parms2)
+ {
+ if (parms2 == 0)
+ {
+ m_scene.RegionInfo.EstateSettings.UseGlobalTime = true;
+ m_scene.RegionInfo.EstateSettings.SunPosition = 0.0;
+ }
+ else
+ {
+ m_scene.RegionInfo.EstateSettings.UseGlobalTime = false;
+ m_scene.RegionInfo.EstateSettings.SunPosition = (double)(parms2 - 0x1800)/1024.0;
+ }
+
+ if ((parms1 & 0x00000010) != 0)
+ m_scene.RegionInfo.EstateSettings.FixedSun = true;
+ else
+ m_scene.RegionInfo.EstateSettings.FixedSun = false;
+
+ if ((parms1 & 0x00008000) != 0)
+ m_scene.RegionInfo.EstateSettings.PublicAccess = true;
+ else
+ m_scene.RegionInfo.EstateSettings.PublicAccess = false;
+
+ if ((parms1 & 0x10000000) != 0)
+ m_scene.RegionInfo.EstateSettings.AllowVoice = true;
+ else
+ m_scene.RegionInfo.EstateSettings.AllowVoice = false;
+
+ if ((parms1 & 0x00100000) != 0)
+ m_scene.RegionInfo.EstateSettings.AllowDirectTeleport = true;
+ else
+ m_scene.RegionInfo.EstateSettings.AllowDirectTeleport = false;
+
+ if ((parms1 & 0x00800000) != 0)
+ m_scene.RegionInfo.EstateSettings.DenyAnonymous = true;
+ else
+ m_scene.RegionInfo.EstateSettings.DenyAnonymous = false;
+
+ if ((parms1 & 0x01000000) != 0)
+ m_scene.RegionInfo.EstateSettings.DenyIdentified = true;
+ else
+ m_scene.RegionInfo.EstateSettings.DenyIdentified = false;
+
+ if ((parms1 & 0x02000000) != 0)
+ m_scene.RegionInfo.EstateSettings.DenyTransacted = true;
+ else
+ m_scene.RegionInfo.EstateSettings.DenyTransacted = false;
+
+ if ((parms1 & 0x40000000) != 0)
+ m_scene.RegionInfo.EstateSettings.DenyMinors = true;
+ else
+ m_scene.RegionInfo.EstateSettings.DenyMinors = false;
+
+ m_scene.RegionInfo.EstateSettings.Save();
+
+ float sun = (float)m_scene.RegionInfo.RegionSettings.SunPosition;
+ if (m_scene.RegionInfo.RegionSettings.UseEstateSun)
+ {
+ sun = (float)m_scene.RegionInfo.EstateSettings.SunPosition;
+ if (m_scene.RegionInfo.EstateSettings.UseGlobalTime)
+ sun = m_scene.EventManager.GetSunLindenHour();
+ }
+
+ m_scene.EventManager.TriggerEstateToolsTimeUpdate(
+ m_scene.RegionInfo.RegionHandle,
+ m_scene.RegionInfo.EstateSettings.FixedSun ||
+ m_scene.RegionInfo.RegionSettings.FixedSun,
+ m_scene.RegionInfo.RegionSettings.UseEstateSun, sun);
+
+ sendDetailedEstateData(remoteClient, invoice);
+ }
+
+ #endregion
+
+ #region IRegionModule Members
+
+ public void Initialise(Scene scene, IConfigSource source)
+ {
+ m_scene = scene;
+ m_scene.RegisterModuleInterface(this);
+ m_scene.EventManager.OnNewClient += EventManager_OnNewClient;
+ m_scene.EventManager.OnRequestChangeWaterHeight += changeWaterHeight;
+ }
+
+
+ public void PostInitialise()
+ {
+ }
+
+ public void Close()
+ {
+ }
+
+ public string Name
+ {
+ get { return "EstateManagementModule"; }
+ }
+
+ public bool IsSharedModule
+ {
+ get { return false; }
+ }
+
+ #endregion
+
+ #region Other Functions
+
+ public void changeWaterHeight(float height)
+ {
+ setRegionTerrainSettings(height,
+ (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit,
+ (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit,
+ m_scene.RegionInfo.RegionSettings.UseEstateSun,
+ m_scene.RegionInfo.RegionSettings.FixedSun,
+ (float)m_scene.RegionInfo.RegionSettings.SunPosition,
+ m_scene.RegionInfo.EstateSettings.UseGlobalTime,
+ m_scene.RegionInfo.EstateSettings.FixedSun,
+ (float)m_scene.RegionInfo.EstateSettings.SunPosition);
+
+ sendRegionInfoPacketToAll();
+ }
+
+ #endregion
+
+ private void EventManager_OnNewClient(IClientAPI client)
+ {
+ client.OnDetailedEstateDataRequest += sendDetailedEstateData;
+ client.OnSetEstateFlagsRequest += estateSetRegionInfoHandler;
+// client.OnSetEstateTerrainBaseTexture += setEstateTerrainBaseTexture;
+ client.OnSetEstateTerrainDetailTexture += setEstateTerrainBaseTexture;
+ client.OnSetEstateTerrainTextureHeights += setEstateTerrainTextureHeights;
+ client.OnCommitEstateTerrainTextureRequest += handleCommitEstateTerrainTextureRequest;
+ client.OnSetRegionTerrainSettings += setRegionTerrainSettings;
+ client.OnEstateRestartSimRequest += handleEstateRestartSimRequest;
+ client.OnEstateChangeCovenantRequest += handleChangeEstateCovenantRequest;
+ client.OnEstateChangeInfo += handleEstateChangeInfo;
+ client.OnUpdateEstateAccessDeltaRequest += handleEstateAccessDeltaRequest;
+ client.OnSimulatorBlueBoxMessageRequest += SendSimulatorBlueBoxMessage;
+ client.OnEstateBlueBoxMessageRequest += SendEstateBlueBoxMessage;
+ client.OnEstateDebugRegionRequest += handleEstateDebugRegionRequest;
+ client.OnEstateTeleportOneUserHomeRequest += handleEstateTeleportOneUserHomeRequest;
+ client.OnEstateTeleportAllUsersHomeRequest += handleEstateTeleportAllUsersHomeRequest;
+ client.OnRequestTerrain += handleTerrainRequest;
+ client.OnUploadTerrain += handleUploadTerrain;
+
+ client.OnRegionInfoRequest += HandleRegionInfoRequest;
+ client.OnEstateCovenantRequest += HandleEstateCovenantRequest;
+ client.OnLandStatRequest += HandleLandStatRequest;
+ sendRegionHandshake(client);
+ }
+
+ public uint GetRegionFlags()
+ {
+ RegionFlags flags = RegionFlags.None;
+
+ // Fully implemented
+ //
+ if (m_scene.RegionInfo.RegionSettings.AllowDamage)
+ flags |= RegionFlags.AllowDamage;
+ if (m_scene.RegionInfo.RegionSettings.BlockTerraform)
+ flags |= RegionFlags.BlockTerraform;
+ if (!m_scene.RegionInfo.RegionSettings.AllowLandResell)
+ flags |= RegionFlags.BlockLandResell;
+ if (m_scene.RegionInfo.RegionSettings.DisableCollisions)
+ flags |= RegionFlags.SkipCollisions;
+ if (m_scene.RegionInfo.RegionSettings.DisableScripts)
+ flags |= RegionFlags.SkipScripts;
+ if (m_scene.RegionInfo.RegionSettings.DisablePhysics)
+ flags |= RegionFlags.SkipPhysics;
+ if (m_scene.RegionInfo.RegionSettings.BlockFly)
+ flags |= RegionFlags.NoFly;
+ if (m_scene.RegionInfo.RegionSettings.RestrictPushing)
+ flags |= RegionFlags.RestrictPushObject;
+ if (m_scene.RegionInfo.RegionSettings.AllowLandJoinDivide)
+ flags |= RegionFlags.AllowParcelChanges;
+ if (m_scene.RegionInfo.RegionSettings.BlockShowInSearch)
+ flags |= (RegionFlags)(1 << 29);
+
+ if (m_scene.RegionInfo.RegionSettings.FixedSun)
+ flags |= RegionFlags.SunFixed;
+ if (m_scene.RegionInfo.RegionSettings.Sandbox)
+ flags |= RegionFlags.Sandbox;
+
+ // Fudge these to always on, so the menu options activate
+ //
+ flags |= RegionFlags.AllowLandmark;
+ flags |= RegionFlags.AllowSetHome;
+
+ // TODO: SkipUpdateInterestList
+
+ // Omitted
+ //
+ // Omitted: NullLayer (what is that?)
+ // Omitted: SkipAgentAction (what does it do?)
+
+ return (uint)flags;
+ }
+
+ public uint GetEstateFlags()
+ {
+ RegionFlags flags = RegionFlags.None;
+
+ if (m_scene.RegionInfo.EstateSettings.FixedSun)
+ flags |= RegionFlags.SunFixed;
+ if (m_scene.RegionInfo.EstateSettings.PublicAccess)
+ flags |= (RegionFlags.PublicAllowed |
+ RegionFlags.ExternallyVisible);
+ if (m_scene.RegionInfo.EstateSettings.AllowVoice)
+ flags |= RegionFlags.AllowVoice;
+ if (m_scene.RegionInfo.EstateSettings.AllowDirectTeleport)
+ flags |= RegionFlags.AllowDirectTeleport;
+ if (m_scene.RegionInfo.EstateSettings.DenyAnonymous)
+ flags |= RegionFlags.DenyAnonymous;
+ if (m_scene.RegionInfo.EstateSettings.DenyIdentified)
+ flags |= RegionFlags.DenyIdentified;
+ if (m_scene.RegionInfo.EstateSettings.DenyTransacted)
+ flags |= RegionFlags.DenyTransacted;
+ if (m_scene.RegionInfo.EstateSettings.AbuseEmailToEstateOwner)
+ flags |= RegionFlags.AbuseEmailToEstateOwner;
+ if (m_scene.RegionInfo.EstateSettings.BlockDwell)
+ flags |= RegionFlags.BlockDwell;
+ if (m_scene.RegionInfo.EstateSettings.EstateSkipScripts)
+ flags |= RegionFlags.EstateSkipScripts;
+ if (m_scene.RegionInfo.EstateSettings.ResetHomeOnTeleport)
+ flags |= RegionFlags.ResetHomeOnTeleport;
+ if (m_scene.RegionInfo.EstateSettings.TaxFree)
+ flags |= RegionFlags.TaxFree;
+ if (m_scene.RegionInfo.EstateSettings.DenyMinors)
+ flags |= (RegionFlags)(1 << 30);
+
+ return (uint)flags;
+ }
+
+ public bool IsManager(UUID avatarID)
+ {
+ if (avatarID == m_scene.RegionInfo.MasterAvatarAssignedUUID)
+ return true;
+ if (avatarID == m_scene.RegionInfo.EstateSettings.EstateOwner)
+ return true;
+
+ List ems = new List(m_scene.RegionInfo.EstateSettings.EstateManagers);
+ if (ems.Contains(avatarID))
+ return true;
+
+ return false;
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateTerrainXferHandler.cs b/OpenSim/Region/CoreModules/World/Estate/EstateTerrainXferHandler.cs
new file mode 100644
index 0000000..94a4072
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Estate/EstateTerrainXferHandler.cs
@@ -0,0 +1,127 @@
+/*
+ * 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 OpenSim 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.IO;
+using System.Reflection;
+using log4net;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Framework.Communications.Cache;
+using OpenSim.Region.Framework.Scenes;
+
+
+namespace OpenSim.Region.CoreModules.World.Estate
+{
+
+ public class EstateTerrainXferHandler
+ {
+ //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private AssetBase m_asset;
+
+ public delegate void TerrainUploadComplete(string name, byte[] filedata, IClientAPI remoteClient);
+
+ public event TerrainUploadComplete TerrainUploadDone;
+
+ //private string m_description = String.Empty;
+ //private string m_name = String.Empty;
+ //private UUID TransactionID = UUID.Zero;
+ private sbyte type = 0;
+
+ public ulong mXferID;
+ private TerrainUploadComplete handlerTerrainUploadDone;
+
+ public EstateTerrainXferHandler(IClientAPI pRemoteClient, string pClientFilename)
+ {
+
+ m_asset = new AssetBase();
+ m_asset.Metadata.FullID = UUID.Zero;
+ m_asset.Metadata.Type = type;
+ m_asset.Data = new byte[0];
+ m_asset.Metadata.Name = pClientFilename;
+ m_asset.Metadata.Description = "empty";
+ m_asset.Metadata.Local = true;
+ m_asset.Metadata.Temporary = true;
+
+ }
+
+ public ulong XferID
+ {
+ get { return mXferID; }
+ }
+
+ public void RequestStartXfer(IClientAPI pRemoteClient)
+ {
+ mXferID = Util.GetNextXferID();
+ pRemoteClient.SendXferRequest(mXferID, m_asset.Metadata.Type, m_asset.Metadata.FullID, 0, Utils.StringToBytes(m_asset.Metadata.Name));
+ }
+
+ ///
+ /// Process transfer data received from the client.
+ ///
+ ///
+ ///
+ ///
+ public void XferReceive(IClientAPI remoteClient, ulong xferID, uint packetID, byte[] data)
+ {
+ if (mXferID == xferID)
+ {
+ if (m_asset.Data.Length > 1)
+ {
+ byte[] destinationArray = new byte[m_asset.Data.Length + data.Length];
+ Array.Copy(m_asset.Data, 0, destinationArray, 0, m_asset.Data.Length);
+ Array.Copy(data, 0, destinationArray, m_asset.Data.Length, data.Length);
+ m_asset.Data = destinationArray;
+ }
+ else
+ {
+ byte[] buffer2 = new byte[data.Length - 4];
+ Array.Copy(data, 4, buffer2, 0, data.Length - 4);
+ m_asset.Data = buffer2;
+ }
+
+ remoteClient.SendConfirmXfer(xferID, packetID);
+
+ if ((packetID & 0x80000000) != 0)
+ {
+ SendCompleteMessage(remoteClient);
+
+ }
+ }
+ }
+
+ public void SendCompleteMessage(IClientAPI remoteClient)
+ {
+ handlerTerrainUploadDone = TerrainUploadDone;
+ if (handlerTerrainUploadDone != null)
+ {
+ handlerTerrainUploadDone(m_asset.Metadata.Name, m_asset.Data, remoteClient);
+ }
+ }
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Land/LandChannel.cs b/OpenSim/Region/CoreModules/World/Land/LandChannel.cs
new file mode 100644
index 0000000..41163a0
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Land/LandChannel.cs
@@ -0,0 +1,188 @@
+/*
+ * 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 OpenSim 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 OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+
+namespace OpenSim.Region.CoreModules.World.Land
+{
+ public class LandChannel : ILandChannel
+ {
+ #region Constants
+
+ //Land types set with flags in ParcelOverlay.
+ //Only one of these can be used.
+ public const float BAN_LINE_SAFETY_HIEGHT = 100;
+ public const byte LAND_FLAG_PROPERTY_BORDER_SOUTH = 128; //Equals 10000000
+ public const byte LAND_FLAG_PROPERTY_BORDER_WEST = 64; //Equals 01000000
+
+ //RequestResults (I think these are right, they seem to work):
+ public const int LAND_RESULT_MULTIPLE = 1; // The request they made contained more than a single peice of land
+ public const int LAND_RESULT_SINGLE = 0; // The request they made contained only a single piece of land
+
+ //ParcelSelectObjects
+ public const int LAND_SELECT_OBJECTS_GROUP = 4;
+ public const int LAND_SELECT_OBJECTS_OTHER = 8;
+ public const int LAND_SELECT_OBJECTS_OWNER = 2;
+ public const byte LAND_TYPE_IS_BEING_AUCTIONED = 5; //Equals 00000101
+ public const byte LAND_TYPE_IS_FOR_SALE = 4; //Equals 00000100
+ public const byte LAND_TYPE_OWNED_BY_GROUP = 2; //Equals 00000010
+ public const byte LAND_TYPE_OWNED_BY_OTHER = 1; //Equals 00000001
+ public const byte LAND_TYPE_OWNED_BY_REQUESTER = 3; //Equals 00000011
+ public const byte LAND_TYPE_PUBLIC = 0; //Equals 00000000
+
+ //These are other constants. Yay!
+ public const int START_LAND_LOCAL_ID = 1;
+
+ #endregion
+
+ private readonly Scene m_scene;
+ private readonly LandManagementModule m_landManagementModule;
+
+ public LandChannel(Scene scene, LandManagementModule landManagementMod)
+ {
+ m_scene = scene;
+ m_landManagementModule = landManagementMod;
+ }
+
+ #region ILandChannel Members
+
+
+ ///
+ /// Get the land object at the specified point
+ ///
+ /// Value between 0 - 256 on the x axis of the point
+ /// Value between 0 - 256 on the y axis of the point
+ /// Land object at the point supplied
+ public ILandObject GetLandObject(float x_float, float y_float)
+ {
+ if (m_landManagementModule != null)
+ {
+ return m_landManagementModule.GetLandObject(x_float, y_float);
+ }
+ ILandObject obj = new LandObject(UUID.Zero, false, m_scene);
+ obj.landData.Name = "NO LAND";
+ return obj;
+ }
+
+ public ILandObject GetLandObject(int x, int y)
+ {
+ if (m_landManagementModule != null)
+ {
+ return m_landManagementModule.GetLandObject(x, y);
+ }
+ ILandObject obj = new LandObject(UUID.Zero, false, m_scene);
+ obj.landData.Name = "NO LAND";
+ return obj;
+ }
+
+ public List AllParcels()
+ {
+ if (m_landManagementModule != null)
+ {
+ return m_landManagementModule.AllParcels();
+ }
+
+ return new List();
+ }
+
+ public List ParcelsNearPoint(Vector3 position)
+ {
+ if (m_landManagementModule != null)
+ {
+ return m_landManagementModule.ParcelsNearPoint(position);
+ }
+
+ return new List();
+ }
+
+ public bool IsLandPrimCountTainted()
+ {
+ if (m_landManagementModule != null)
+ {
+ return m_landManagementModule.IsLandPrimCountTainted();
+ }
+
+ return false;
+ }
+
+ public bool IsForcefulBansAllowed()
+ {
+ if (m_landManagementModule != null)
+ {
+ return m_landManagementModule.AllowedForcefulBans;
+ }
+
+ return false;
+ }
+
+ public void UpdateLandObject(int localID, LandData data)
+ {
+ if (m_landManagementModule != null)
+ {
+ m_landManagementModule.UpdateLandObject(localID, data);
+ }
+ }
+ public void ReturnObjectsInParcel(int localID, uint returnType, UUID[] agentIDs, UUID[] taskIDs, IClientAPI remoteClient)
+ {
+ if (m_landManagementModule != null)
+ {
+ m_landManagementModule.ReturnObjectsInParcel(localID, returnType, agentIDs, taskIDs, remoteClient);
+ }
+ }
+
+ public void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel)
+ {
+ if (m_landManagementModule != null)
+ {
+ m_landManagementModule.setParcelObjectMaxOverride(overrideDel);
+ }
+ }
+
+ public void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel)
+ {
+ if (m_landManagementModule != null)
+ {
+ m_landManagementModule.setSimulatorObjectMaxOverride(overrideDel);
+ }
+ }
+
+ public void SetParcelOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime)
+ {
+ if (m_landManagementModule != null)
+ {
+ m_landManagementModule.setParcelOtherCleanTime(remoteClient, localID, otherCleanTime);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
new file mode 100644
index 0000000..6ae6576
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
@@ -0,0 +1,1347 @@
+/*
+ * 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 OpenSim 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;
+using System.Collections.Generic;
+using System.Reflection;
+using OpenMetaverse;
+using log4net;
+using Nini.Config;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Framework;
+using OpenSim.Framework.Servers;
+using OpenSim.Framework.Communications.Capabilities;
+using OpenSim.Region.Physics.Manager;
+using Caps = OpenSim.Framework.Communications.Capabilities.Caps;
+
+namespace OpenSim.Region.CoreModules.World.Land
+{
+ // used for caching
+ internal class ExtendedLandData {
+ public LandData landData;
+ public ulong regionHandle;
+ public uint x, y;
+ }
+
+ public class LandManagementModule : IRegionModule
+ {
+ private static readonly ILog m_log =
+ LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private static readonly string remoteParcelRequestPath = "0009/";
+
+ private LandChannel landChannel;
+ private Scene m_scene;
+
+ private readonly int[,] m_landIDList = new int[64, 64];
+ private readonly Dictionary m_landList = new Dictionary