Merge branch 'master' of /home/opensim/var/repo/opensim
commit
21f4ee8a3d
|
@ -350,7 +350,5 @@ namespace OpenSim.Capabilities.Handlers
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.IO;
|
||||||
|
using System.Web;
|
||||||
|
using log4net;
|
||||||
|
using Nini.Config;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
using OpenMetaverse.Imaging;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Framework.Capabilities;
|
||||||
|
using OpenSim.Framework.Servers;
|
||||||
|
using OpenSim.Framework.Servers.HttpServer;
|
||||||
|
using OpenSim.Region.Framework.Interfaces;
|
||||||
|
using OpenSim.Services.Interfaces;
|
||||||
|
using Caps = OpenSim.Framework.Capabilities.Caps;
|
||||||
|
|
||||||
|
namespace OpenSim.Capabilities.Handlers
|
||||||
|
{
|
||||||
|
public class UploadBakedTextureHandler
|
||||||
|
{
|
||||||
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
private Caps m_HostCapsObj;
|
||||||
|
private IAssetService m_assetService;
|
||||||
|
private bool m_persistBakedTextures;
|
||||||
|
|
||||||
|
public UploadBakedTextureHandler(Caps caps, IAssetService assetService, bool persistBakedTextures)
|
||||||
|
{
|
||||||
|
m_HostCapsObj = caps;
|
||||||
|
m_assetService = assetService;
|
||||||
|
m_persistBakedTextures = persistBakedTextures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle a request from the client for a Uri to upload a baked texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="path"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="httpRequest"></param>
|
||||||
|
/// <param name="httpResponse"></param>
|
||||||
|
/// <returns>The upload response if the request is successful, null otherwise.</returns>
|
||||||
|
public string UploadBakedTexture(
|
||||||
|
string request, string path, string param, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// m_log.Debug("[CAPS]: UploadBakedTexture Request in region: " + m_regionName);
|
||||||
|
|
||||||
|
string capsBase = "/CAPS/" + m_HostCapsObj.CapsObjectPath;
|
||||||
|
string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000");
|
||||||
|
|
||||||
|
BakedTextureUploader uploader =
|
||||||
|
new BakedTextureUploader(capsBase + uploaderPath, m_HostCapsObj.HttpListener);
|
||||||
|
uploader.OnUpLoad += BakedTextureUploaded;
|
||||||
|
|
||||||
|
m_HostCapsObj.HttpListener.AddStreamHandler(
|
||||||
|
new BinaryStreamHandler("POST", capsBase + uploaderPath,
|
||||||
|
uploader.uploaderCaps));
|
||||||
|
|
||||||
|
string protocol = "http://";
|
||||||
|
|
||||||
|
if (m_HostCapsObj.SSLCaps)
|
||||||
|
protocol = "https://";
|
||||||
|
|
||||||
|
string uploaderURL = protocol + m_HostCapsObj.HostName + ":" +
|
||||||
|
m_HostCapsObj.Port.ToString() + capsBase + uploaderPath;
|
||||||
|
|
||||||
|
LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse();
|
||||||
|
uploadResponse.uploader = uploaderURL;
|
||||||
|
uploadResponse.state = "upload";
|
||||||
|
|
||||||
|
return LLSDHelpers.SerialiseLLSDReply(uploadResponse);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.Error("[UPLOAD BAKED TEXTURE HANDLER]: " + e.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a baked texture has been successfully uploaded by a client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assetID"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
private void BakedTextureUploaded(UUID assetID, byte[] data)
|
||||||
|
{
|
||||||
|
// m_log.WarnFormat("[CAPS]: Received baked texture {0}", assetID.ToString());
|
||||||
|
|
||||||
|
AssetBase asset;
|
||||||
|
asset = new AssetBase(assetID, "Baked Texture", (sbyte)AssetType.Texture, m_HostCapsObj.AgentID.ToString());
|
||||||
|
asset.Data = data;
|
||||||
|
asset.Temporary = true;
|
||||||
|
asset.Local = !m_persistBakedTextures; // Local assets aren't persisted, non-local are
|
||||||
|
m_assetService.Store(asset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BakedTextureUploader
|
||||||
|
{
|
||||||
|
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
public event Action<UUID, byte[]> OnUpLoad;
|
||||||
|
|
||||||
|
private string uploaderPath = String.Empty;
|
||||||
|
private UUID newAssetID;
|
||||||
|
private IHttpServer httpListener;
|
||||||
|
|
||||||
|
public BakedTextureUploader(string path, IHttpServer httpServer)
|
||||||
|
{
|
||||||
|
newAssetID = UUID.Random();
|
||||||
|
uploaderPath = path;
|
||||||
|
httpListener = httpServer;
|
||||||
|
// m_log.InfoFormat("[CAPS] baked texture upload starting for {0}",newAssetID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle raw uploaded baked texture data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="path"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string uploaderCaps(byte[] data, string path, string param)
|
||||||
|
{
|
||||||
|
Action<UUID, byte[]> handlerUpLoad = OnUpLoad;
|
||||||
|
|
||||||
|
// Don't do this asynchronously, otherwise it's possible for the client to send set appearance information
|
||||||
|
// on another thread which might send out avatar updates before the asset has been put into the asset
|
||||||
|
// service.
|
||||||
|
if (handlerUpLoad != null)
|
||||||
|
handlerUpLoad(newAssetID, data);
|
||||||
|
|
||||||
|
string res = String.Empty;
|
||||||
|
LLSDAssetUploadComplete uploadComplete = new LLSDAssetUploadComplete();
|
||||||
|
uploadComplete.new_asset = newAssetID.ToString();
|
||||||
|
uploadComplete.new_inventory_item = UUID.Zero;
|
||||||
|
uploadComplete.state = "complete";
|
||||||
|
|
||||||
|
res = LLSDHelpers.SerialiseLLSDReply(uploadComplete);
|
||||||
|
|
||||||
|
httpListener.RemoveStreamHandler("POST", uploaderPath);
|
||||||
|
|
||||||
|
// m_log.DebugFormat("[BAKED TEXTURE UPLOADER]: baked texture upload completed for {0}", newAssetID);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,8 +56,6 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
string assetName, string description, UUID assetID, UUID inventoryItem, UUID parentFolder,
|
string assetName, string description, UUID assetID, UUID inventoryItem, UUID parentFolder,
|
||||||
byte[] data, string inventoryType, string assetType);
|
byte[] data, string inventoryType, string assetType);
|
||||||
|
|
||||||
public delegate void UploadedBakedTexture(UUID assetID, byte[] data);
|
|
||||||
|
|
||||||
public delegate UUID UpdateItem(UUID itemID, byte[] data);
|
public delegate UUID UpdateItem(UUID itemID, byte[] data);
|
||||||
|
|
||||||
public delegate void UpdateTaskScript(UUID itemID, UUID primID, bool isScriptRunning, byte[] data, ref ArrayList errors);
|
public delegate void UpdateTaskScript(UUID itemID, UUID primID, bool isScriptRunning, byte[] data, ref ArrayList errors);
|
||||||
|
@ -97,7 +95,6 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
private static readonly string m_notecardTaskUpdatePath = "0005/";
|
private static readonly string m_notecardTaskUpdatePath = "0005/";
|
||||||
// private static readonly string m_fetchInventoryPath = "0006/";
|
// private static readonly string m_fetchInventoryPath = "0006/";
|
||||||
// private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule.
|
// private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule.
|
||||||
private static readonly string m_uploadBakedTexturePath = "0010/";// This is in the LandManagementModule.
|
|
||||||
|
|
||||||
|
|
||||||
// These are callbacks which will be setup by the scene so that we can update scene data when we
|
// These are callbacks which will be setup by the scene so that we can update scene data when we
|
||||||
|
@ -164,8 +161,6 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
IRequestHandler req = new RestStreamHandler("POST", capsBase + m_notecardTaskUpdatePath, ScriptTaskInventory);
|
IRequestHandler req = new RestStreamHandler("POST", capsBase + m_notecardTaskUpdatePath, ScriptTaskInventory);
|
||||||
m_HostCapsObj.RegisterHandler("UpdateScriptTaskInventory", req);
|
m_HostCapsObj.RegisterHandler("UpdateScriptTaskInventory", req);
|
||||||
m_HostCapsObj.RegisterHandler("UpdateScriptTask", req);
|
m_HostCapsObj.RegisterHandler("UpdateScriptTask", req);
|
||||||
m_HostCapsObj.RegisterHandler("UploadBakedTexture", new RestStreamHandler("POST", capsBase + m_uploadBakedTexturePath, UploadBakedTexture));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -330,74 +325,6 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle a request from the client for a Uri to upload a baked texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="path"></param>
|
|
||||||
/// <param name="param"></param>
|
|
||||||
/// <param name="httpRequest"></param>
|
|
||||||
/// <param name="httpResponse"></param>
|
|
||||||
/// <returns>The upload response if the request is successful, null otherwise.</returns>
|
|
||||||
public string UploadBakedTexture(string request, string path,
|
|
||||||
string param, OSHttpRequest httpRequest,
|
|
||||||
OSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// m_log.Debug("[CAPS]: UploadBakedTexture Request in region: " + m_regionName);
|
|
||||||
|
|
||||||
string capsBase = "/CAPS/" + m_HostCapsObj.CapsObjectPath;
|
|
||||||
string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000");
|
|
||||||
|
|
||||||
BakedTextureUploader uploader =
|
|
||||||
new BakedTextureUploader(capsBase + uploaderPath, m_HostCapsObj.HttpListener);
|
|
||||||
uploader.OnUpLoad += BakedTextureUploaded;
|
|
||||||
|
|
||||||
m_HostCapsObj.HttpListener.AddStreamHandler(
|
|
||||||
new BinaryStreamHandler("POST", capsBase + uploaderPath,
|
|
||||||
uploader.uploaderCaps));
|
|
||||||
|
|
||||||
string protocol = "http://";
|
|
||||||
|
|
||||||
if (m_HostCapsObj.SSLCaps)
|
|
||||||
protocol = "https://";
|
|
||||||
|
|
||||||
string uploaderURL = protocol + m_HostCapsObj.HostName + ":" +
|
|
||||||
m_HostCapsObj.Port.ToString() + capsBase + uploaderPath;
|
|
||||||
|
|
||||||
LLSDAssetUploadResponse uploadResponse =
|
|
||||||
new LLSDAssetUploadResponse();
|
|
||||||
uploadResponse.uploader = uploaderURL;
|
|
||||||
uploadResponse.state = "upload";
|
|
||||||
|
|
||||||
return LLSDHelpers.SerialiseLLSDReply(uploadResponse);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
m_log.Error("[CAPS]: " + e.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when a baked texture has been successfully uploaded by a client.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="assetID"></param>
|
|
||||||
/// <param name="data"></param>
|
|
||||||
public void BakedTextureUploaded(UUID assetID, byte[] data)
|
|
||||||
{
|
|
||||||
// m_log.WarnFormat("[CAPS]: Received baked texture {0}", assetID.ToString());
|
|
||||||
|
|
||||||
AssetBase asset;
|
|
||||||
asset = new AssetBase(assetID, "Baked Texture", (sbyte)AssetType.Texture, m_HostCapsObj.AgentID.ToString());
|
|
||||||
asset.Data = data;
|
|
||||||
asset.Temporary = true;
|
|
||||||
asset.Local = !m_persistBakedTextures; // Local assets aren't persisted, non-local are
|
|
||||||
m_assetService.Store(asset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when new asset data for an agent inventory item update has been uploaded.
|
/// Called when new asset data for an agent inventory item update has been uploaded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1067,6 +994,7 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
// XXX Maybe this should be some meaningful error packet
|
// XXX Maybe this should be some meaningful error packet
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
///Left this in and commented in case there are unforseen issues
|
///Left this in and commented in case there are unforseen issues
|
||||||
//private void SaveAssetToFile(string filename, byte[] data)
|
//private void SaveAssetToFile(string filename, byte[] data)
|
||||||
//{
|
//{
|
||||||
|
@ -1090,53 +1018,4 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
fs.Close();
|
fs.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BakedTextureUploader
|
|
||||||
{
|
|
||||||
public event UploadedBakedTexture OnUpLoad;
|
|
||||||
private UploadedBakedTexture handlerUpLoad = null;
|
|
||||||
|
|
||||||
private string uploaderPath = String.Empty;
|
|
||||||
private UUID newAssetID;
|
|
||||||
private IHttpServer httpListener;
|
|
||||||
|
|
||||||
public BakedTextureUploader(string path, IHttpServer httpServer)
|
|
||||||
{
|
|
||||||
newAssetID = UUID.Random();
|
|
||||||
uploaderPath = path;
|
|
||||||
httpListener = httpServer;
|
|
||||||
// m_log.InfoFormat("[CAPS] baked texture upload starting for {0}",newAssetID);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle raw uploaded baked texture data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data"></param>
|
|
||||||
/// <param name="path"></param>
|
|
||||||
/// <param name="param"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public string uploaderCaps(byte[] data, string path, string param)
|
|
||||||
{
|
|
||||||
handlerUpLoad = OnUpLoad;
|
|
||||||
if (handlerUpLoad != null)
|
|
||||||
{
|
|
||||||
Util.FireAndForget(delegate(object o) { handlerUpLoad(newAssetID, data); });
|
|
||||||
}
|
|
||||||
|
|
||||||
string res = String.Empty;
|
|
||||||
LLSDAssetUploadComplete uploadComplete = new LLSDAssetUploadComplete();
|
|
||||||
uploadComplete.new_asset = newAssetID.ToString();
|
|
||||||
uploadComplete.new_inventory_item = UUID.Zero;
|
|
||||||
uploadComplete.state = "complete";
|
|
||||||
|
|
||||||
res = LLSDHelpers.SerialiseLLSDReply(uploadComplete);
|
|
||||||
|
|
||||||
httpListener.RemoveStreamHandler("POST", uploaderPath);
|
|
||||||
|
|
||||||
// m_log.InfoFormat("[CAPS] baked texture upload completed for {0}",newAssetID);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.IO;
|
||||||
|
using System.Web;
|
||||||
|
using log4net;
|
||||||
|
using Nini.Config;
|
||||||
|
using Mono.Addins;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
using OpenMetaverse.Imaging;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Framework.Servers;
|
||||||
|
using OpenSim.Framework.Servers.HttpServer;
|
||||||
|
using OpenSim.Region.Framework.Interfaces;
|
||||||
|
using OpenSim.Region.Framework.Scenes;
|
||||||
|
using OpenSim.Services.Interfaces;
|
||||||
|
using Caps = OpenSim.Framework.Capabilities.Caps;
|
||||||
|
using OpenSim.Capabilities.Handlers;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.ClientStack.Linden
|
||||||
|
{
|
||||||
|
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
|
||||||
|
public class UploadBakedTextureModule : INonSharedRegionModule
|
||||||
|
{
|
||||||
|
// private static readonly ILog m_log =
|
||||||
|
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For historical reasons this is fixed, but there
|
||||||
|
/// </summary>
|
||||||
|
private static readonly string m_uploadBakedTexturePath = "0010/";// This is in the LandManagementModule.
|
||||||
|
|
||||||
|
private Scene m_scene;
|
||||||
|
private bool m_persistBakedTextures;
|
||||||
|
|
||||||
|
public void Initialise(IConfigSource source)
|
||||||
|
{
|
||||||
|
IConfig sconfig = source.Configs["Startup"];
|
||||||
|
if (sconfig != null)
|
||||||
|
m_persistBakedTextures = sconfig.GetBoolean("PersistBakedTextures", m_persistBakedTextures);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRegion(Scene s)
|
||||||
|
{
|
||||||
|
m_scene = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveRegion(Scene s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegionLoaded(Scene s)
|
||||||
|
{
|
||||||
|
m_scene.EventManager.OnRegisterCaps += RegisterCaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PostInitialise()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close() { }
|
||||||
|
|
||||||
|
public string Name { get { return "UploadBakedTextureModule"; } }
|
||||||
|
|
||||||
|
public Type ReplaceableInterface
|
||||||
|
{
|
||||||
|
get { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterCaps(UUID agentID, Caps caps)
|
||||||
|
{
|
||||||
|
caps.RegisterHandler(
|
||||||
|
"UploadBakedTexture",
|
||||||
|
new RestStreamHandler(
|
||||||
|
"POST",
|
||||||
|
"/CAPS/" + m_uploadBakedTexturePath,
|
||||||
|
new UploadBakedTextureHandler(
|
||||||
|
caps, m_scene.AssetService, m_persistBakedTextures).UploadBakedTexture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -495,7 +495,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
// Well, this is it. The agent is over there.
|
// Well, this is it. The agent is over there.
|
||||||
KillEntity(sp.Scene, sp.LocalId);
|
KillEntity(sp.Scene, sp.LocalId);
|
||||||
|
|
||||||
|
|
||||||
// Now let's make it officially a child agent
|
// Now let's make it officially a child agent
|
||||||
sp.MakeChildAgent();
|
sp.MakeChildAgent();
|
||||||
|
|
||||||
|
@ -510,9 +509,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
sp.Scene.IncomingCloseAgent(sp.UUID);
|
sp.Scene.IncomingCloseAgent(sp.UUID);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
// now we have a child agent in this region.
|
// now we have a child agent in this region.
|
||||||
sp.Reset();
|
sp.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
// REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE!
|
// REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE!
|
||||||
if (sp.Scene.NeedSceneCacheClear(sp.UUID))
|
if (sp.Scene.NeedSceneCacheClear(sp.UUID))
|
||||||
|
@ -946,111 +946,121 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, GridRegion neighbourRegion,
|
ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, GridRegion neighbourRegion,
|
||||||
bool isFlying, string version)
|
bool isFlying, string version)
|
||||||
{
|
{
|
||||||
ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize));
|
try
|
||||||
|
|
||||||
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Crossing agent {0} {1} to {2}-{3} running version {4}", agent.Firstname, agent.Lastname, neighbourx, neighboury, version);
|
|
||||||
|
|
||||||
Scene m_scene = agent.Scene;
|
|
||||||
|
|
||||||
if (neighbourRegion != null)
|
|
||||||
{
|
{
|
||||||
if (!agent.ValidateAttachments())
|
ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize));
|
||||||
m_log.DebugFormat(
|
|
||||||
"[ENTITY TRANSFER MODULE]: Failed validation of all attachments for region crossing of {0} from {1} to {2}. Continuing.",
|
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Crossing agent {0} {1} to {2}-{3} running version {4}", agent.Firstname, agent.Lastname, neighbourx, neighboury, version);
|
||||||
agent.Name, agent.Scene.RegionInfo.RegionName, neighbourRegion.RegionName);
|
|
||||||
|
Scene m_scene = agent.Scene;
|
||||||
pos = pos + (agent.Velocity);
|
|
||||||
|
if (neighbourRegion != null)
|
||||||
SetInTransit(agent.UUID);
|
|
||||||
AgentData cAgent = new AgentData();
|
|
||||||
agent.CopyTo(cAgent);
|
|
||||||
cAgent.Position = pos;
|
|
||||||
if (isFlying)
|
|
||||||
cAgent.ControlFlags |= (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY;
|
|
||||||
cAgent.CallbackURI = m_scene.RegionInfo.ServerURI +
|
|
||||||
"agent/" + agent.UUID.ToString() + "/" + m_scene.RegionInfo.RegionID.ToString() + "/release/";
|
|
||||||
|
|
||||||
if (!m_scene.SimulationService.UpdateAgent(neighbourRegion, cAgent))
|
|
||||||
{
|
{
|
||||||
// region doesn't take it
|
if (!agent.ValidateAttachments())
|
||||||
ReInstantiateScripts(agent);
|
m_log.DebugFormat(
|
||||||
ResetFromTransit(agent.UUID);
|
"[ENTITY TRANSFER MODULE]: Failed validation of all attachments for region crossing of {0} from {1} to {2}. Continuing.",
|
||||||
return agent;
|
agent.Name, agent.Scene.RegionInfo.RegionName, neighbourRegion.RegionName);
|
||||||
|
|
||||||
|
pos = pos + (agent.Velocity);
|
||||||
|
|
||||||
|
SetInTransit(agent.UUID);
|
||||||
|
AgentData cAgent = new AgentData();
|
||||||
|
agent.CopyTo(cAgent);
|
||||||
|
cAgent.Position = pos;
|
||||||
|
if (isFlying)
|
||||||
|
cAgent.ControlFlags |= (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY;
|
||||||
|
cAgent.CallbackURI = m_scene.RegionInfo.ServerURI +
|
||||||
|
"agent/" + agent.UUID.ToString() + "/" + m_scene.RegionInfo.RegionID.ToString() + "/release/";
|
||||||
|
|
||||||
|
if (!m_scene.SimulationService.UpdateAgent(neighbourRegion, cAgent))
|
||||||
|
{
|
||||||
|
// region doesn't take it
|
||||||
|
ReInstantiateScripts(agent);
|
||||||
|
ResetFromTransit(agent.UUID);
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, let's close the child agent connections that are too far away.
|
||||||
|
agent.CloseChildAgents(neighbourx, neighboury);
|
||||||
|
|
||||||
|
//AgentCircuitData circuitdata = m_controllingClient.RequestClientInfo();
|
||||||
|
agent.ControllingClient.RequestClientInfo();
|
||||||
|
|
||||||
|
//m_log.Debug("BEFORE CROSS");
|
||||||
|
//Scene.DumpChildrenSeeds(UUID);
|
||||||
|
//DumpKnownRegions();
|
||||||
|
string agentcaps;
|
||||||
|
if (!agent.KnownRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps))
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: No ENTITY TRANSFER MODULE information for region handle {0}, exiting CrossToNewRegion.",
|
||||||
|
neighbourRegion.RegionHandle);
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
string capsPath = neighbourRegion.ServerURI + CapsUtil.GetCapsSeedPath(agentcaps);
|
||||||
|
|
||||||
|
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} to client {1}", capsPath, agent.UUID);
|
||||||
|
|
||||||
|
IEventQueue eq = agent.Scene.RequestModuleInterface<IEventQueue>();
|
||||||
|
if (eq != null)
|
||||||
|
{
|
||||||
|
eq.CrossRegion(neighbourHandle, pos, agent.Velocity, neighbourRegion.ExternalEndPoint,
|
||||||
|
capsPath, agent.UUID, agent.ControllingClient.SessionId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
agent.ControllingClient.CrossRegion(neighbourHandle, pos, agent.Velocity, neighbourRegion.ExternalEndPoint,
|
||||||
|
capsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WaitForCallback(agent.UUID))
|
||||||
|
{
|
||||||
|
m_log.Debug("[ENTITY TRANSFER MODULE]: Callback never came in crossing agent");
|
||||||
|
ReInstantiateScripts(agent);
|
||||||
|
ResetFromTransit(agent.UUID);
|
||||||
|
|
||||||
|
// Yikes! We should just have a ref to scene here.
|
||||||
|
//agent.Scene.InformClientOfNeighbours(agent);
|
||||||
|
EnableChildAgents(agent);
|
||||||
|
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
agent.MakeChildAgent();
|
||||||
|
|
||||||
|
// now we have a child agent in this region. Request all interesting data about other (root) agents
|
||||||
|
agent.SendOtherAgentsAvatarDataToMe();
|
||||||
|
agent.SendOtherAgentsAppearanceToMe();
|
||||||
|
|
||||||
|
// Backwards compatibility
|
||||||
|
if (version == "Unknown" || version == string.Empty)
|
||||||
|
{
|
||||||
|
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Old neighbor, passing attachments one by one...");
|
||||||
|
CrossAttachmentsIntoNewRegion(neighbourRegion, agent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentHasMovedAway(agent, false);
|
||||||
|
|
||||||
|
// the user may change their profile information in other region,
|
||||||
|
// so the userinfo in UserProfileCache is not reliable any more, delete it
|
||||||
|
// REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE!
|
||||||
|
if (agent.Scene.NeedSceneCacheClear(agent.UUID))
|
||||||
|
{
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, let's close the child agent connections that are too far away.
|
//m_log.Debug("AFTER CROSS");
|
||||||
agent.CloseChildAgents(neighbourx, neighboury);
|
|
||||||
|
|
||||||
//AgentCircuitData circuitdata = m_controllingClient.RequestClientInfo();
|
|
||||||
agent.ControllingClient.RequestClientInfo();
|
|
||||||
|
|
||||||
//m_log.Debug("BEFORE CROSS");
|
|
||||||
//Scene.DumpChildrenSeeds(UUID);
|
//Scene.DumpChildrenSeeds(UUID);
|
||||||
//DumpKnownRegions();
|
//DumpKnownRegions();
|
||||||
string agentcaps;
|
}
|
||||||
if (!agent.KnownRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps))
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: No ENTITY TRANSFER MODULE information for region handle {0}, exiting CrossToNewRegion.",
|
m_log.ErrorFormat(
|
||||||
neighbourRegion.RegionHandle);
|
"[ENTITY TRANSFER MODULE]: Problem crossing user {0} to new region {1} from {2}. Exception {3}{4}",
|
||||||
return agent;
|
agent.Name, neighbourRegion.RegionName, agent.Scene.RegionInfo.RegionName, e.Message, e.StackTrace);
|
||||||
}
|
|
||||||
string capsPath = neighbourRegion.ServerURI + CapsUtil.GetCapsSeedPath(agentcaps);
|
|
||||||
|
|
||||||
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} to client {1}", capsPath, agent.UUID);
|
|
||||||
|
|
||||||
IEventQueue eq = agent.Scene.RequestModuleInterface<IEventQueue>();
|
|
||||||
if (eq != null)
|
|
||||||
{
|
|
||||||
eq.CrossRegion(neighbourHandle, pos, agent.Velocity, neighbourRegion.ExternalEndPoint,
|
|
||||||
capsPath, agent.UUID, agent.ControllingClient.SessionId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
agent.ControllingClient.CrossRegion(neighbourHandle, pos, agent.Velocity, neighbourRegion.ExternalEndPoint,
|
|
||||||
capsPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!WaitForCallback(agent.UUID))
|
|
||||||
{
|
|
||||||
m_log.Debug("[ENTITY TRANSFER MODULE]: Callback never came in crossing agent");
|
|
||||||
ReInstantiateScripts(agent);
|
|
||||||
ResetFromTransit(agent.UUID);
|
|
||||||
|
|
||||||
// Yikes! We should just have a ref to scene here.
|
|
||||||
//agent.Scene.InformClientOfNeighbours(agent);
|
|
||||||
EnableChildAgents(agent);
|
|
||||||
|
|
||||||
return agent;
|
|
||||||
}
|
|
||||||
|
|
||||||
agent.MakeChildAgent();
|
|
||||||
|
|
||||||
// now we have a child agent in this region. Request all interesting data about other (root) agents
|
|
||||||
agent.SendOtherAgentsAvatarDataToMe();
|
|
||||||
agent.SendOtherAgentsAppearanceToMe();
|
|
||||||
|
|
||||||
// Backwards compatibility
|
|
||||||
if (version == "Unknown" || version == string.Empty)
|
|
||||||
{
|
|
||||||
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Old neighbor, passing attachments one by one...");
|
|
||||||
CrossAttachmentsIntoNewRegion(neighbourRegion, agent, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
AgentHasMovedAway(agent, false);
|
|
||||||
|
|
||||||
// the user may change their profile information in other region,
|
|
||||||
// so the userinfo in UserProfileCache is not reliable any more, delete it
|
|
||||||
// REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE!
|
|
||||||
if (agent.Scene.NeedSceneCacheClear(agent.UUID))
|
|
||||||
{
|
|
||||||
m_log.DebugFormat(
|
|
||||||
"[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//m_log.Debug("AFTER CROSS");
|
|
||||||
//Scene.DumpChildrenSeeds(UUID);
|
|
||||||
//DumpKnownRegions();
|
|
||||||
return agent;
|
return agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,8 +131,11 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
||||||
|
|
||||||
public void ResetAnimations()
|
public void ResetAnimations()
|
||||||
{
|
{
|
||||||
|
// m_log.DebugFormat(
|
||||||
|
// "[SCENE PRESENCE ANIMATOR]: Resetting animations for {0} in {1}",
|
||||||
|
// m_scenePresence.Name, m_scenePresence.Scene.RegionInfo.RegionName);
|
||||||
|
|
||||||
m_animations.Clear();
|
m_animations.Clear();
|
||||||
TrySetMovementAnimation("STAND");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -155,6 +158,14 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
||||||
SendAnimPack();
|
SendAnimPack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Don't leave this on since on teleports SP.HandleAgentUpdate() still hammers us for a while after it teleports
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// m_log.WarnFormat(
|
||||||
|
// "[SCENE PRESENCE ANIMATOR]: Tried to set movement animation {0} on child presence {1}",
|
||||||
|
// anim, m_scenePresence.Name);
|
||||||
|
// throw new Exception(string.Format("aaargh on setting {0}", anim));
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -3700,7 +3700,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID);
|
ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID);
|
||||||
|
|
||||||
if (childAgentUpdate != null)
|
if (childAgentUpdate != null)
|
||||||
|
|
|
@ -782,9 +782,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
AdjustKnownSeeds();
|
AdjustKnownSeeds();
|
||||||
|
|
||||||
// TODO: I think, this won't send anything, as we are still a child here...
|
|
||||||
Animator.TrySetMovementAnimation("STAND");
|
|
||||||
|
|
||||||
// we created a new ScenePresence (a new child agent) in a fresh region.
|
// we created a new ScenePresence (a new child agent) in a fresh region.
|
||||||
// Request info about all the (root) agents in this region
|
// Request info about all the (root) agents in this region
|
||||||
// Note: This won't send data *to* other clients in that region (children don't send)
|
// Note: This won't send data *to* other clients in that region (children don't send)
|
||||||
|
@ -998,13 +995,17 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This turns a root agent into a child agent
|
/// This turns a root agent into a child agent
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
/// when an agent departs this region for a neighbor, this gets called.
|
/// when an agent departs this region for a neighbor, this gets called.
|
||||||
///
|
///
|
||||||
/// It doesn't get called for a teleport. Reason being, an agent that
|
/// It doesn't get called for a teleport. Reason being, an agent that
|
||||||
/// teleports out may not end up anywhere near this region
|
/// teleports out may not end up anywhere near this region
|
||||||
/// </summary>
|
/// </remarks>
|
||||||
public void MakeChildAgent()
|
public void MakeChildAgent()
|
||||||
{
|
{
|
||||||
|
m_log.DebugFormat("[SCENE PRESENCE]: Making {0} a child agent in {1}", Name, Scene.RegionInfo.RegionName);
|
||||||
|
|
||||||
// Reset these so that teleporting in and walking out isn't seen
|
// Reset these so that teleporting in and walking out isn't seen
|
||||||
// as teleporting back
|
// as teleporting back
|
||||||
TeleportFlags = TeleportFlags.Default;
|
TeleportFlags = TeleportFlags.Default;
|
||||||
|
@ -2298,11 +2299,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
{
|
{
|
||||||
m_updateCount = 0; // Kill animation update burst so that the SIT_G.. will stick.
|
m_updateCount = 0; // Kill animation update burst so that the SIT_G.. will stick.
|
||||||
Animator.TrySetMovementAnimation("SIT_GROUND_CONSTRAINED");
|
Animator.TrySetMovementAnimation("SIT_GROUND_CONSTRAINED");
|
||||||
|
|
||||||
// TODO: This doesn't prevent the user from walking yet.
|
|
||||||
// Setting parent ID would fix this, if we knew what value
|
|
||||||
// to use. Or we could add a m_isSitting variable.
|
|
||||||
//Animator.TrySetMovementAnimation("SIT_GROUND_CONSTRAINED");
|
|
||||||
SitGround = true;
|
SitGround = true;
|
||||||
RemoveFromPhysicalScene();
|
RemoveFromPhysicalScene();
|
||||||
}
|
}
|
||||||
|
@ -2911,9 +2907,12 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
|
// m_log.DebugFormat("[SCENE PRESENCE]: Resetting {0} in {1}", Name, Scene.RegionInfo.RegionName);
|
||||||
|
|
||||||
// Put the child agent back at the center
|
// Put the child agent back at the center
|
||||||
AbsolutePosition
|
AbsolutePosition
|
||||||
= new Vector3(((float)Constants.RegionSize * 0.5f), ((float)Constants.RegionSize * 0.5f), 70);
|
= new Vector3(((float)Constants.RegionSize * 0.5f), ((float)Constants.RegionSize * 0.5f), 70);
|
||||||
|
|
||||||
Animator.ResetAnimations();
|
Animator.ResetAnimations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3136,7 +3135,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyFrom(AgentData cAgent)
|
private void CopyFrom(AgentData cAgent)
|
||||||
{
|
{
|
||||||
m_originRegionID = cAgent.RegionID;
|
m_originRegionID = cAgent.RegionID;
|
||||||
|
|
||||||
|
@ -3195,13 +3194,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
// Animations
|
|
||||||
try
|
Animator.Animations.FromArray(cAgent.Anims);
|
||||||
{
|
|
||||||
Animator.ResetAnimations();
|
|
||||||
Animator.Animations.FromArray(cAgent.Anims);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
|
|
||||||
if (cAgent.AttachmentObjects != null && cAgent.AttachmentObjects.Count > 0)
|
if (cAgent.AttachmentObjects != null && cAgent.AttachmentObjects.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue