aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Agent
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/Agent')
-rw-r--r--OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetTransactionsManager.cs231
-rw-r--r--OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs232
-rw-r--r--OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs288
-rw-r--r--OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs267
-rw-r--r--OpenSim/Region/CoreModules/Agent/Capabilities/CapabilitiesModule.cs210
-rw-r--r--OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs295
-rw-r--r--OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs90
-rw-r--r--OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs267
-rw-r--r--OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs533
-rw-r--r--OpenSim/Region/CoreModules/Agent/TextureSender/Tests/TextureSenderTests.cs177
-rw-r--r--OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs213
-rw-r--r--OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs232
12 files changed, 3035 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetTransactionsManager.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetTransactionsManager.cs
new file mode 100644
index 0000000..2be1eaa
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetTransactionsManager.cs
@@ -0,0 +1,231 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System.Collections.Generic;
29//using System.Reflection;
30//using log4net;
31using OpenMetaverse;
32using OpenSim.Framework;
33using OpenSim.Region.Framework.Scenes;
34using OpenSim.Region.Framework.Interfaces;
35
36namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
37{
38 /*
39 public class AgentAssetTransactionsManager
40 {
41 //private static readonly ILog m_log
42 // = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
43
44 /// <summary>
45 /// Each agent has its own singleton collection of transactions
46 /// </summary>
47 private Dictionary<UUID, AgentAssetTransactions> AgentTransactions =
48 new Dictionary<UUID, AgentAssetTransactions>();
49
50 /// <summary>
51 /// Should we dump uploaded assets to the filesystem?
52 /// </summary>
53 private bool m_dumpAssetsToFile;
54
55 public Scene MyScene;
56
57 public AgentAssetTransactionsManager(Scene scene, bool dumpAssetsToFile)
58 {
59 MyScene = scene;
60 m_dumpAssetsToFile = dumpAssetsToFile;
61 }
62
63 /// <summary>
64 /// Get the collection of asset transactions for the given user. If one does not already exist, it
65 /// is created.
66 /// </summary>
67 /// <param name="userID"></param>
68 /// <returns></returns>
69 private AgentAssetTransactions GetUserTransactions(UUID userID)
70 {
71 lock (AgentTransactions)
72 {
73 if (!AgentTransactions.ContainsKey(userID))
74 {
75 AgentAssetTransactions transactions = null;
76 //= new AgentAssetTransactions(userID, this, m_dumpAssetsToFile);
77 AgentTransactions.Add(userID, transactions);
78 }
79
80 return AgentTransactions[userID];
81 }
82 }
83
84 /// <summary>
85 /// Remove the given agent asset transactions. This should be called when a client is departing
86 /// from a scene (and hence won't be making any more transactions here).
87 /// </summary>
88 /// <param name="userID"></param>
89 public void RemoveAgentAssetTransactions(UUID userID)
90 {
91 // m_log.DebugFormat("Removing agent asset transactions structure for agent {0}", userID);
92
93 lock (AgentTransactions)
94 {
95 AgentTransactions.Remove(userID);
96 }
97 }
98
99 /// <summary>
100 /// Create an inventory item from data that has been received through a transaction.
101 ///
102 /// This is called when new clothing or body parts are created. It may also be called in other
103 /// situations.
104 /// </summary>
105 /// <param name="remoteClient"></param>
106 /// <param name="transactionID"></param>
107 /// <param name="folderID"></param>
108 /// <param name="callbackID"></param>
109 /// <param name="description"></param>
110 /// <param name="name"></param>
111 /// <param name="invType"></param>
112 /// <param name="type"></param>
113 /// <param name="wearableType"></param>
114 /// <param name="nextOwnerMask"></param>
115 public void HandleItemCreationFromTransaction(IClientAPI remoteClient, UUID transactionID, UUID folderID,
116 uint callbackID, string description, string name, sbyte invType,
117 sbyte type, byte wearableType, uint nextOwnerMask)
118 {
119// m_log.DebugFormat(
120// "[TRANSACTIONS MANAGER] Called HandleItemCreationFromTransaction with item {0}", name);
121
122 AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
123
124 transactions.RequestCreateInventoryItem(
125 remoteClient, transactionID, folderID, callbackID, description,
126 name, invType, type, wearableType, nextOwnerMask);
127 }
128
129 /// <summary>
130 /// Update an inventory item with data that has been received through a transaction.
131 ///
132 /// This is called when clothing or body parts are updated (for instance, with new textures or
133 /// colours). It may also be called in other situations.
134 /// </summary>
135 /// <param name="remoteClient"></param>
136 /// <param name="transactionID"></param>
137 /// <param name="item"></param>
138 public void HandleItemUpdateFromTransaction(IClientAPI remoteClient, UUID transactionID,
139 InventoryItemBase item)
140 {
141// m_log.DebugFormat(
142// "[TRANSACTIONS MANAGER] Called HandleItemUpdateFromTransaction with item {0}",
143// item.Name);
144
145 AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
146
147 transactions.RequestUpdateInventoryItem(remoteClient, transactionID, item);
148 }
149
150 /// <summary>
151 /// Update a task inventory item with data that has been received through a transaction.
152 ///
153 /// This is currently called when, for instance, a notecard in a prim is saved. The data is sent
154 /// up through a single AssetUploadRequest. A subsequent UpdateTaskInventory then references the transaction
155 /// and comes through this method.
156 /// </summary>
157 /// <param name="remoteClient"></param>
158 /// <param name="transactionID"></param>
159 /// <param name="item"></param>
160 public void HandleTaskItemUpdateFromTransaction(
161 IClientAPI remoteClient, SceneObjectPart part, UUID transactionID, TaskInventoryItem item)
162 {
163// m_log.DebugFormat(
164// "[TRANSACTIONS MANAGER] Called HandleTaskItemUpdateFromTransaction with item {0}",
165// item.Name);
166
167 AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
168
169 transactions.RequestUpdateTaskInventoryItem(remoteClient, part, transactionID, item);
170 }
171
172 /// <summary>
173 /// Request that a client (agent) begin an asset transfer.
174 /// </summary>
175 /// <param name="remoteClient"></param>
176 /// <param name="assetID"></param>
177 /// <param name="transaction"></param>
178 /// <param name="type"></param>
179 /// <param name="data"></param></param>
180 /// <param name="tempFile"></param>
181 public void HandleUDPUploadRequest(IClientAPI remoteClient, UUID assetID, UUID transaction, sbyte type,
182 byte[] data, bool storeLocal, bool tempFile)
183 {
184 //System.Console.WriteLine("HandleUDPUploadRequest - assetID: " + assetID.ToString() + " transaction: " + transaction.ToString() + " type: " + type.ToString() + " storelocal: " + storeLocal + " tempFile: " + tempFile);
185 if (((AssetType)type == AssetType.Texture ||
186 (AssetType)type == AssetType.Sound ||
187 (AssetType)type == AssetType.TextureTGA ||
188 (AssetType)type == AssetType.Animation) &&
189 tempFile == false)
190 {
191 Scene scene = (Scene)remoteClient.Scene;
192 IMoneyModule mm = scene.RequestModuleInterface<IMoneyModule>();
193
194 if (mm != null)
195 {
196 if (!mm.UploadCovered(remoteClient))
197 {
198 remoteClient.SendAgentAlertMessage("Unable to upload asset. Insufficient funds.", false);
199 return;
200 }
201 }
202 }
203
204 //Console.WriteLine("asset upload of " + assetID);
205 AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
206
207 AssetXferUploader uploader = transactions.RequestXferUploader(transaction);
208 if (uploader != null)
209 {
210 uploader.Initialise(remoteClient, assetID, transaction, type, data, storeLocal, tempFile);
211 }
212 }
213
214 /// <summary>
215 /// Handle asset transfer data packets received in response to the asset upload request in
216 /// HandleUDPUploadRequest()
217 /// </summary>
218 /// <param name="remoteClient"></param>
219 /// <param name="xferID"></param>
220 /// <param name="packetID"></param>
221 /// <param name="data"></param>
222 public void HandleXfer(IClientAPI remoteClient, ulong xferID, uint packetID, byte[] data)
223 {
224 //System.Console.WriteLine("xferID: " + xferID + " packetID: " + packetID + " data!");
225 AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
226
227 transactions.HandleXfer(xferID, packetID, data);
228 }
229 }
230 */
231}
diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs
new file mode 100644
index 0000000..b4dbaa1
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs
@@ -0,0 +1,232 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using OpenMetaverse;
31using OpenSim.Framework;
32using OpenSim.Region.Framework.Scenes;
33using OpenSim.Framework.Communications.Cache;
34
35namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
36{
37 /// <summary>
38 /// Manage asset transactions for a single agent.
39 /// </summary>
40 public class AgentAssetTransactions
41 {
42 private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
43
44 // Fields
45 private bool m_dumpAssetsToFile;
46 public AssetTransactionModule Manager;
47 public UUID UserID;
48 public Dictionary<UUID, AssetXferUploader> XferUploaders = new Dictionary<UUID, AssetXferUploader>();
49
50 // Methods
51 public AgentAssetTransactions(UUID agentID, AssetTransactionModule manager, bool dumpAssetsToFile)
52 {
53 UserID = agentID;
54 Manager = manager;
55 m_dumpAssetsToFile = dumpAssetsToFile;
56 }
57
58 public AssetXferUploader RequestXferUploader(UUID transactionID)
59 {
60 if (!XferUploaders.ContainsKey(transactionID))
61 {
62 AssetXferUploader uploader = new AssetXferUploader(this, m_dumpAssetsToFile);
63
64 lock (XferUploaders)
65 {
66 XferUploaders.Add(transactionID, uploader);
67 }
68
69 return uploader;
70 }
71 return null;
72 }
73
74 public void HandleXfer(ulong xferID, uint packetID, byte[] data)
75 {
76 lock (XferUploaders)
77 {
78 foreach (AssetXferUploader uploader in XferUploaders.Values)
79 {
80 if (uploader.XferID == xferID)
81 {
82 uploader.HandleXferPacket(xferID, packetID, data);
83 break;
84 }
85 }
86 }
87 }
88
89 public void RequestCreateInventoryItem(IClientAPI remoteClient, UUID transactionID, UUID folderID,
90 uint callbackID, string description, string name, sbyte invType,
91 sbyte type, byte wearableType, uint nextOwnerMask)
92 {
93 if (XferUploaders.ContainsKey(transactionID))
94 {
95 XferUploaders[transactionID].RequestCreateInventoryItem(remoteClient, transactionID, folderID,
96 callbackID, description, name, invType, type,
97 wearableType, nextOwnerMask);
98 }
99 }
100
101
102
103 /// <summary>
104 /// Get an uploaded asset. If the data is successfully retrieved, the transaction will be removed.
105 /// </summary>
106 /// <param name="transactionID"></param>
107 /// <returns>The asset if the upload has completed, null if it has not.</returns>
108 public AssetBase GetTransactionAsset(UUID transactionID)
109 {
110 if (XferUploaders.ContainsKey(transactionID))
111 {
112 AssetXferUploader uploader = XferUploaders[transactionID];
113 AssetBase asset = uploader.GetAssetData();
114
115 lock (XferUploaders)
116 {
117 XferUploaders.Remove(transactionID);
118 }
119
120 return asset;
121 }
122
123 return null;
124 }
125
126 //private void CreateItemFromUpload(AssetBase asset, IClientAPI ourClient, UUID inventoryFolderID, uint nextPerms, uint wearableType)
127 //{
128 // Manager.MyScene.CommsManager.AssetCache.AddAsset(asset);
129 // CachedUserInfo userInfo = Manager.MyScene.CommsManager.UserProfileCacheService.GetUserDetails(
130 // ourClient.AgentId);
131
132 // if (userInfo != null)
133 // {
134 // InventoryItemBase item = new InventoryItemBase();
135 // item.Owner = ourClient.AgentId;
136 // item.Creator = ourClient.AgentId;
137 // item.ID = UUID.Random();
138 // item.AssetID = asset.FullID;
139 // item.Description = asset.Description;
140 // item.Name = asset.Name;
141 // item.AssetType = asset.Type;
142 // item.InvType = asset.Type;
143 // item.Folder = inventoryFolderID;
144 // item.BasePermissions = 0x7fffffff;
145 // item.CurrentPermissions = 0x7fffffff;
146 // item.EveryOnePermissions = 0;
147 // item.NextPermissions = nextPerms;
148 // item.Flags = wearableType;
149 // item.CreationDate = Util.UnixTimeSinceEpoch();
150
151 // userInfo.AddItem(item);
152 // ourClient.SendInventoryItemCreateUpdate(item);
153 // }
154 // else
155 // {
156 // m_log.ErrorFormat(
157 // "[ASSET TRANSACTIONS]: Could not find user {0} for inventory item creation",
158 // ourClient.AgentId);
159 // }
160 //}
161
162 public void RequestUpdateTaskInventoryItem(
163 IClientAPI remoteClient, SceneObjectPart part, UUID transactionID, TaskInventoryItem item)
164 {
165 if (XferUploaders.ContainsKey(transactionID))
166 {
167 AssetBase asset = XferUploaders[transactionID].GetAssetData();
168 if (asset != null)
169 {
170 m_log.DebugFormat(
171 "[ASSET TRANSACTIONS]: Updating task item {0} in {1} with asset in transaction {2}",
172 item.Name, part.Name, transactionID);
173
174 asset.Metadata.Name = item.Name;
175 asset.Metadata.Description = item.Description;
176 asset.Metadata.Type = (sbyte)item.Type;
177 item.AssetID = asset.Metadata.FullID;
178
179 Manager.MyScene.CommsManager.AssetCache.AddAsset(asset);
180
181 if (part.Inventory.UpdateInventoryItem(item))
182 part.GetProperties(remoteClient);
183 }
184 }
185 }
186
187
188 public void RequestUpdateInventoryItem(IClientAPI remoteClient, UUID transactionID,
189 InventoryItemBase item)
190 {
191 if (XferUploaders.ContainsKey(transactionID))
192 {
193 CachedUserInfo userInfo = Manager.MyScene.CommsManager.UserProfileCacheService.GetUserDetails(
194 remoteClient.AgentId);
195
196 if (userInfo != null)
197 {
198 UUID assetID = UUID.Combine(transactionID, remoteClient.SecureSessionId);
199
200 AssetBase asset
201 = Manager.MyScene.CommsManager.AssetCache.GetAsset(
202 assetID, (item.AssetType == (int)AssetType.Texture ? true : false));
203
204 if (asset == null)
205 {
206 asset = GetTransactionAsset(transactionID);
207 }
208
209 if (asset != null && asset.Metadata.FullID == assetID)
210 {
211 // Assets never get updated, new ones get created
212 asset.Metadata.FullID = UUID.Random();
213 asset.Metadata.Name = item.Name;
214 asset.Metadata.Description = item.Description;
215 asset.Metadata.Type = (sbyte)item.AssetType;
216 item.AssetID = asset.Metadata.FullID;
217
218 Manager.MyScene.CommsManager.AssetCache.AddAsset(asset);
219 }
220
221 userInfo.UpdateItem(item);
222 }
223 else
224 {
225 m_log.ErrorFormat(
226 "[ASSET TRANSACTIONS]: Could not find user {0} for inventory item update",
227 remoteClient.AgentId);
228 }
229 }
230 }
231 }
232}
diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs
new file mode 100644
index 0000000..77dfd44
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs
@@ -0,0 +1,288 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using OpenMetaverse;
31using Nini.Config;
32using OpenSim.Framework;
33using OpenSim.Region.Framework.Interfaces;
34using OpenSim.Region.Framework.Scenes;
35
36namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
37{
38 public class AssetTransactionModule : IRegionModule, IAgentAssetTransactions
39 {
40 private readonly Dictionary<UUID, Scene> RegisteredScenes = new Dictionary<UUID, Scene>();
41 private bool m_dumpAssetsToFile = false;
42 private Scene m_scene = null;
43
44 public Scene MyScene
45 {
46 get{ return m_scene;}
47 }
48
49 /// <summary>
50 /// Each agent has its own singleton collection of transactions
51 /// </summary>
52 private Dictionary<UUID, AgentAssetTransactions> AgentTransactions =
53 new Dictionary<UUID, AgentAssetTransactions>();
54
55
56 public AssetTransactionModule()
57 {
58 // System.Console.WriteLine("creating AgentAssetTransactionModule");
59 }
60
61 #region IRegionModule Members
62
63 public void Initialise(Scene scene, IConfigSource config)
64 {
65 if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID))
66 {
67 // System.Console.WriteLine("initialising AgentAssetTransactionModule");
68 RegisteredScenes.Add(scene.RegionInfo.RegionID, scene);
69 scene.RegisterModuleInterface<IAgentAssetTransactions>(this);
70
71 scene.EventManager.OnNewClient += NewClient;
72 }
73
74 if (m_scene == null)
75 {
76 m_scene = scene;
77 if (config.Configs["StandAlone"] != null)
78 {
79 try
80 {
81 m_dumpAssetsToFile = config.Configs["StandAlone"].GetBoolean("dump_assets_to_file", false);
82 }
83 catch (Exception)
84 {
85 }
86 }
87 else
88 {
89 }
90 }
91 }
92
93 public void PostInitialise()
94 {
95 }
96
97 public void Close()
98 {
99 }
100
101 public string Name
102 {
103 get { return "AgentTransactionModule"; }
104 }
105
106 public bool IsSharedModule
107 {
108 get { return true; }
109 }
110
111 #endregion
112
113 public void NewClient(IClientAPI client)
114 {
115 client.OnAssetUploadRequest += HandleUDPUploadRequest;
116 client.OnXferReceive += HandleXfer;
117 }
118
119 #region AgentAssetTransactions
120 /// <summary>
121 /// Get the collection of asset transactions for the given user. If one does not already exist, it
122 /// is created.
123 /// </summary>
124 /// <param name="userID"></param>
125 /// <returns></returns>
126 private AgentAssetTransactions GetUserTransactions(UUID userID)
127 {
128 lock (AgentTransactions)
129 {
130 if (!AgentTransactions.ContainsKey(userID))
131 {
132 AgentAssetTransactions transactions = new AgentAssetTransactions(userID, this, m_dumpAssetsToFile);
133 AgentTransactions.Add(userID, transactions);
134 }
135
136 return AgentTransactions[userID];
137 }
138 }
139
140 /// <summary>
141 /// Remove the given agent asset transactions. This should be called when a client is departing
142 /// from a scene (and hence won't be making any more transactions here).
143 /// </summary>
144 /// <param name="userID"></param>
145 public void RemoveAgentAssetTransactions(UUID userID)
146 {
147 // m_log.DebugFormat("Removing agent asset transactions structure for agent {0}", userID);
148
149 lock (AgentTransactions)
150 {
151 AgentTransactions.Remove(userID);
152 }
153 }
154
155 /// <summary>
156 /// Create an inventory item from data that has been received through a transaction.
157 ///
158 /// This is called when new clothing or body parts are created. It may also be called in other
159 /// situations.
160 /// </summary>
161 /// <param name="remoteClient"></param>
162 /// <param name="transactionID"></param>
163 /// <param name="folderID"></param>
164 /// <param name="callbackID"></param>
165 /// <param name="description"></param>
166 /// <param name="name"></param>
167 /// <param name="invType"></param>
168 /// <param name="type"></param>
169 /// <param name="wearableType"></param>
170 /// <param name="nextOwnerMask"></param>
171 public void HandleItemCreationFromTransaction(IClientAPI remoteClient, UUID transactionID, UUID folderID,
172 uint callbackID, string description, string name, sbyte invType,
173 sbyte type, byte wearableType, uint nextOwnerMask)
174 {
175 // m_log.DebugFormat(
176 // "[TRANSACTIONS MANAGER] Called HandleItemCreationFromTransaction with item {0}", name);
177
178 AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
179
180 transactions.RequestCreateInventoryItem(
181 remoteClient, transactionID, folderID, callbackID, description,
182 name, invType, type, wearableType, nextOwnerMask);
183 }
184
185 /// <summary>
186 /// Update an inventory item with data that has been received through a transaction.
187 ///
188 /// This is called when clothing or body parts are updated (for instance, with new textures or
189 /// colours). It may also be called in other situations.
190 /// </summary>
191 /// <param name="remoteClient"></param>
192 /// <param name="transactionID"></param>
193 /// <param name="item"></param>
194 public void HandleItemUpdateFromTransaction(IClientAPI remoteClient, UUID transactionID,
195 InventoryItemBase item)
196 {
197 // m_log.DebugFormat(
198 // "[TRANSACTIONS MANAGER] Called HandleItemUpdateFromTransaction with item {0}",
199 // item.Name);
200
201 AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
202
203 transactions.RequestUpdateInventoryItem(remoteClient, transactionID, item);
204 }
205
206 /// <summary>
207 /// Update a task inventory item with data that has been received through a transaction.
208 ///
209 /// This is currently called when, for instance, a notecard in a prim is saved. The data is sent
210 /// up through a single AssetUploadRequest. A subsequent UpdateTaskInventory then references the transaction
211 /// and comes through this method.
212 /// </summary>
213 /// <param name="remoteClient"></param>
214 /// <param name="transactionID"></param>
215 /// <param name="item"></param>
216 public void HandleTaskItemUpdateFromTransaction(
217 IClientAPI remoteClient, SceneObjectPart part, UUID transactionID, TaskInventoryItem item)
218 {
219 // m_log.DebugFormat(
220 // "[TRANSACTIONS MANAGER] Called HandleTaskItemUpdateFromTransaction with item {0}",
221 // item.Name);
222
223 AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
224
225 transactions.RequestUpdateTaskInventoryItem(remoteClient, part, transactionID, item);
226 }
227
228 /// <summary>
229 /// Request that a client (agent) begin an asset transfer.
230 /// </summary>
231 /// <param name="remoteClient"></param>
232 /// <param name="assetID"></param>
233 /// <param name="transaction"></param>
234 /// <param name="type"></param>
235 /// <param name="data"></param></param>
236 /// <param name="tempFile"></param>
237 public void HandleUDPUploadRequest(IClientAPI remoteClient, UUID assetID, UUID transaction, sbyte type,
238 byte[] data, bool storeLocal, bool tempFile)
239 {
240 //System.Console.WriteLine("HandleUDPUploadRequest - assetID: " + assetID.ToString() + " transaction: " + transaction.ToString() + " type: " + type.ToString() + " storelocal: " + storeLocal + " tempFile: " + tempFile);
241 if (((AssetType)type == AssetType.Texture ||
242 (AssetType)type == AssetType.Sound ||
243 (AssetType)type == AssetType.TextureTGA ||
244 (AssetType)type == AssetType.Animation) &&
245 tempFile == false)
246 {
247 Scene scene = (Scene)remoteClient.Scene;
248 IMoneyModule mm = scene.RequestModuleInterface<IMoneyModule>();
249
250 if (mm != null)
251 {
252 if (!mm.UploadCovered(remoteClient))
253 {
254 remoteClient.SendAgentAlertMessage("Unable to upload asset. Insufficient funds.", false);
255 return;
256 }
257 }
258 }
259
260 //Console.WriteLine("asset upload of " + assetID);
261 AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
262
263 AssetXferUploader uploader = transactions.RequestXferUploader(transaction);
264 if (uploader != null)
265 {
266 uploader.Initialise(remoteClient, assetID, transaction, type, data, storeLocal, tempFile);
267 }
268 }
269
270 /// <summary>
271 /// Handle asset transfer data packets received in response to the asset upload request in
272 /// HandleUDPUploadRequest()
273 /// </summary>
274 /// <param name="remoteClient"></param>
275 /// <param name="xferID"></param>
276 /// <param name="packetID"></param>
277 /// <param name="data"></param>
278 public void HandleXfer(IClientAPI remoteClient, ulong xferID, uint packetID, byte[] data)
279 {
280 //System.Console.WriteLine("xferID: " + xferID + " packetID: " + packetID + " data!");
281 AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId);
282
283 transactions.HandleXfer(xferID, packetID, data);
284 }
285
286 #endregion
287 }
288}
diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs
new file mode 100644
index 0000000..aef2664
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs
@@ -0,0 +1,267 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.IO;
30using System.Reflection;
31using log4net;
32using OpenMetaverse;
33using OpenMetaverse.Packets;
34using OpenSim.Framework;
35using OpenSim.Framework.Communications.Cache;
36using OpenSim.Region.Framework.Scenes;
37
38namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
39{
40 public class AssetXferUploader
41 {
42 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
43
44 private AssetBase m_asset;
45 private UUID InventFolder = UUID.Zero;
46 private sbyte invType = 0;
47 private bool m_createItem = false;
48 private string m_description = String.Empty;
49 private bool m_dumpAssetToFile;
50 private bool m_finished = false;
51 private string m_name = String.Empty;
52 private bool m_storeLocal;
53 private AgentAssetTransactions m_userTransactions;
54 private uint nextPerm = 0;
55 private IClientAPI ourClient;
56 private UUID TransactionID = UUID.Zero;
57 private sbyte type = 0;
58 private byte wearableType = 0;
59 public ulong XferID;
60
61 public AssetXferUploader(AgentAssetTransactions transactions, bool dumpAssetToFile)
62 {
63 m_userTransactions = transactions;
64 m_dumpAssetToFile = dumpAssetToFile;
65 }
66
67 /// <summary>
68 /// Process transfer data received from the client.
69 /// </summary>
70 /// <param name="xferID"></param>
71 /// <param name="packetID"></param>
72 /// <param name="data"></param>
73 /// <returns>True if the transfer is complete, false otherwise or if the xferID was not valid</returns>
74 public bool HandleXferPacket(ulong xferID, uint packetID, byte[] data)
75 {
76 if (XferID == xferID)
77 {
78 if (m_asset.Data.Length > 1)
79 {
80 byte[] destinationArray = new byte[m_asset.Data.Length + data.Length];
81 Array.Copy(m_asset.Data, 0, destinationArray, 0, m_asset.Data.Length);
82 Array.Copy(data, 0, destinationArray, m_asset.Data.Length, data.Length);
83 m_asset.Data = destinationArray;
84 }
85 else
86 {
87 byte[] buffer2 = new byte[data.Length - 4];
88 Array.Copy(data, 4, buffer2, 0, data.Length - 4);
89 m_asset.Data = buffer2;
90 }
91
92 ourClient.SendConfirmXfer(xferID, packetID);
93
94 if ((packetID & 0x80000000) != 0)
95 {
96 SendCompleteMessage();
97 return true;
98 }
99 }
100
101 return false;
102 }
103
104 /// <summary>
105 /// Initialise asset transfer from the client
106 /// </summary>
107 /// <param name="xferID"></param>
108 /// <param name="packetID"></param>
109 /// <param name="data"></param>
110 /// <returns>True if the transfer is complete, false otherwise</returns>
111 public bool Initialise(IClientAPI remoteClient, UUID assetID, UUID transaction, sbyte type, byte[] data,
112 bool storeLocal, bool tempFile)
113 {
114 ourClient = remoteClient;
115 m_asset = new AssetBase();
116 m_asset.Metadata.FullID = assetID;
117 m_asset.Metadata.Type = type;
118 m_asset.Data = data;
119 m_asset.Metadata.Name = "blank";
120 m_asset.Metadata.Description = "empty";
121 m_asset.Metadata.Local = storeLocal;
122 m_asset.Metadata.Temporary = tempFile;
123
124 TransactionID = transaction;
125 m_storeLocal = storeLocal;
126
127 if (m_asset.Data.Length > 2)
128 {
129 SendCompleteMessage();
130 return true;
131 }
132 else
133 {
134 RequestStartXfer();
135 }
136
137 return false;
138 }
139
140 protected void RequestStartXfer()
141 {
142 XferID = Util.GetNextXferID();
143 ourClient.SendXferRequest(XferID, m_asset.Metadata.Type, m_asset.Metadata.FullID, 0, new byte[0]);
144 }
145
146 protected void SendCompleteMessage()
147 {
148 ourClient.SendAssetUploadCompleteMessage(m_asset.Metadata.Type, true, m_asset.Metadata.FullID);
149
150 m_finished = true;
151 if (m_createItem)
152 {
153 DoCreateItem();
154 }
155 else if (m_storeLocal)
156 {
157 m_userTransactions.Manager.MyScene.CommsManager.AssetCache.AddAsset(m_asset);
158 }
159
160 m_log.DebugFormat("[ASSET TRANSACTIONS]: Uploaded asset data for transaction {0}", TransactionID);
161
162 if (m_dumpAssetToFile)
163 {
164 DateTime now = DateTime.Now;
165 string filename =
166 String.Format("{6}_{7}_{0:d2}{1:d2}{2:d2}_{3:d2}{4:d2}{5:d2}.dat", now.Year, now.Month, now.Day,
167 now.Hour, now.Minute, now.Second, m_asset.Metadata.Name, m_asset.Metadata.Type);
168 SaveAssetToFile(filename, m_asset.Data);
169 }
170 }
171
172 private void SaveAssetToFile(string filename, byte[] data)
173 {
174 string assetPath = "UserAssets";
175 if (!Directory.Exists(assetPath))
176 {
177 Directory.CreateDirectory(assetPath);
178 }
179 FileStream fs = File.Create(Path.Combine(assetPath, filename));
180 BinaryWriter bw = new BinaryWriter(fs);
181 bw.Write(data);
182 bw.Close();
183 fs.Close();
184 }
185
186 public void RequestCreateInventoryItem(IClientAPI remoteClient, UUID transactionID, UUID folderID,
187 uint callbackID, string description, string name, sbyte invType,
188 sbyte type, byte wearableType, uint nextOwnerMask)
189 {
190 if (TransactionID == transactionID)
191 {
192 InventFolder = folderID;
193 m_name = name;
194 m_description = description;
195 this.type = type;
196 this.invType = invType;
197 this.wearableType = wearableType;
198 nextPerm = nextOwnerMask;
199 m_asset.Metadata.Name = name;
200 m_asset.Metadata.Description = description;
201 m_asset.Metadata.Type = type;
202
203 if (m_finished)
204 {
205 DoCreateItem();
206 }
207 else
208 {
209 m_createItem = true; //set flag so the inventory item is created when upload is complete
210 }
211 }
212 }
213
214
215 private void DoCreateItem()
216 {
217 m_userTransactions.Manager.MyScene.CommsManager.AssetCache.AddAsset(m_asset);
218 CachedUserInfo userInfo =
219 m_userTransactions.Manager.MyScene.CommsManager.UserProfileCacheService.GetUserDetails(
220 ourClient.AgentId);
221
222 if (userInfo != null)
223 {
224 InventoryItemBase item = new InventoryItemBase();
225 item.Owner = ourClient.AgentId;
226 item.Creator = ourClient.AgentId;
227 item.ID = UUID.Random();
228 item.AssetID = m_asset.Metadata.FullID;
229 item.Description = m_description;
230 item.Name = m_name;
231 item.AssetType = type;
232 item.InvType = invType;
233 item.Folder = InventFolder;
234 item.BasePermissions = 0x7fffffff;
235 item.CurrentPermissions = 0x7fffffff;
236 item.GroupPermissions=0;
237 item.EveryOnePermissions=0;
238 item.NextPermissions = nextPerm;
239 item.Flags = (uint) wearableType;
240 item.CreationDate = Util.UnixTimeSinceEpoch();
241
242 userInfo.AddItem(item);
243 ourClient.SendInventoryItemCreateUpdate(item);
244 }
245 else
246 {
247 m_log.ErrorFormat(
248 "[ASSET TRANSACTIONS]: Could not find user {0} for inventory item creation",
249 ourClient.AgentId);
250 }
251 }
252
253 /// <summary>
254 /// Get the asset data uploaded in this transfer.
255 /// </summary>
256 /// <returns>null if the asset has not finished uploading</returns>
257 public AssetBase GetAssetData()
258 {
259 if (m_finished)
260 {
261 return m_asset;
262 }
263
264 return null;
265 }
266 }
267}
diff --git a/OpenSim/Region/CoreModules/Agent/Capabilities/CapabilitiesModule.cs b/OpenSim/Region/CoreModules/Agent/Capabilities/CapabilitiesModule.cs
new file mode 100644
index 0000000..4f7f177
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/Capabilities/CapabilitiesModule.cs
@@ -0,0 +1,210 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using log4net;
32using Nini.Config;
33using OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Framework.Communications.Cache;
36using OpenSim.Framework.Communications.Capabilities;
37using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Framework.Scenes;
39using Caps = OpenSim.Framework.Communications.Capabilities.Caps;
40
41namespace OpenSim.Region.CoreModules.Agent.Capabilities
42{
43 public class CapabilitiesModule : IRegionModule, ICapabilitiesModule
44 {
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 protected Scene m_scene;
48
49 /// <summary>
50 /// Each agent has its own capabilities handler.
51 /// </summary>
52 protected Dictionary<UUID, Caps> m_capsHandlers = new Dictionary<UUID, Caps>();
53
54 protected Dictionary<UUID, string> capsPaths = new Dictionary<UUID, string>();
55 protected Dictionary<UUID, Dictionary<ulong, string>> childrenSeeds
56 = new Dictionary<UUID, Dictionary<ulong, string>>();
57
58 public void Initialise(Scene scene, IConfigSource source)
59 {
60 m_scene = scene;
61 m_scene.RegisterModuleInterface<ICapabilitiesModule>(this);
62 }
63
64 public void PostInitialise() {}
65 public void Close() {}
66 public string Name { get { return "Capabilities Module"; } }
67 public bool IsSharedModule { get { return false; } }
68
69 public void AddCapsHandler(UUID agentId)
70 {
71 if (m_scene.RegionInfo.EstateSettings.IsBanned(agentId))
72 return;
73
74 String capsObjectPath = GetCapsPath(agentId);
75
76 if (m_capsHandlers.ContainsKey(agentId))
77 {
78 Caps oldCaps = m_capsHandlers[agentId];
79
80 m_log.DebugFormat(
81 "[CAPS]: Reregistering caps for agent {0}. Old caps path {1}, new caps path {2}. ",
82 agentId, oldCaps.CapsObjectPath, capsObjectPath);
83 // This should not happen. The caller code is confused. We need to fix that.
84 // CAPs can never be reregistered, or the client will be confused.
85 // Hence this return here.
86 //return;
87 }
88
89 Caps caps
90 = new Caps(
91 m_scene.AssetCache, m_scene.CommsManager.HttpServer, m_scene.RegionInfo.ExternalHostName,
92 m_scene.CommsManager.HttpServer.Port,
93 capsObjectPath, agentId, m_scene.DumpAssetsToFile, m_scene.RegionInfo.RegionName);
94
95 caps.RegisterHandlers();
96
97 m_scene.EventManager.TriggerOnRegisterCaps(agentId, caps);
98
99 caps.AddNewInventoryItem = m_scene.AddUploadedInventoryItem;
100 caps.ItemUpdatedCall = m_scene.CapsUpdateInventoryItemAsset;
101 caps.TaskScriptUpdatedCall = m_scene.CapsUpdateTaskInventoryScriptAsset;
102 caps.CAPSFetchInventoryDescendents = m_scene.HandleFetchInventoryDescendentsCAPS;
103 caps.GetClient = m_scene.SceneContents.GetControllingClient;
104
105 m_capsHandlers[agentId] = caps;
106 }
107
108 public void RemoveCapsHandler(UUID agentId)
109 {
110 if (childrenSeeds.ContainsKey(agentId))
111 {
112 childrenSeeds.Remove(agentId);
113 }
114
115 lock (m_capsHandlers)
116 {
117 if (m_capsHandlers.ContainsKey(agentId))
118 {
119 m_capsHandlers[agentId].DeregisterHandlers();
120 m_scene.EventManager.TriggerOnDeregisterCaps(agentId, m_capsHandlers[agentId]);
121 m_capsHandlers.Remove(agentId);
122 }
123 else
124 {
125 m_log.WarnFormat(
126 "[CAPS]: Received request to remove CAPS handler for root agent {0} in {1}, but no such CAPS handler found!",
127 agentId, m_scene.RegionInfo.RegionName);
128 }
129 }
130 }
131
132 public Caps GetCapsHandlerForUser(UUID agentId)
133 {
134 lock (m_capsHandlers)
135 {
136 if (m_capsHandlers.ContainsKey(agentId))
137 {
138 return m_capsHandlers[agentId];
139 }
140 }
141
142 return null;
143 }
144
145 public void NewUserConnection(AgentCircuitData agent)
146 {
147 capsPaths[agent.AgentID] = agent.CapsPath;
148 childrenSeeds[agent.AgentID]
149 = ((agent.ChildrenCapSeeds == null) ? new Dictionary<ulong, string>() : agent.ChildrenCapSeeds);
150 }
151
152 public string GetCapsPath(UUID agentId)
153 {
154 if (capsPaths.ContainsKey(agentId))
155 {
156 return capsPaths[agentId];
157 }
158
159 return null;
160 }
161
162 public Dictionary<ulong, string> GetChildrenSeeds(UUID agentID)
163 {
164 Dictionary<ulong, string> seeds = null;
165 if (childrenSeeds.TryGetValue(agentID, out seeds))
166 return seeds;
167 return new Dictionary<ulong, string>();
168 }
169
170 public void DropChildSeed(UUID agentID, ulong handle)
171 {
172 Dictionary<ulong, string> seeds;
173 if (childrenSeeds.TryGetValue(agentID, out seeds))
174 {
175 seeds.Remove(handle);
176 }
177 }
178
179 public string GetChildSeed(UUID agentID, ulong handle)
180 {
181 Dictionary<ulong, string> seeds;
182 string returnval;
183 if (childrenSeeds.TryGetValue(agentID, out seeds))
184 {
185 if (seeds.TryGetValue(handle, out returnval))
186 return returnval;
187 }
188 return null;
189 }
190
191 public void SetChildrenSeed(UUID agentID, Dictionary<ulong, string> seeds)
192 {
193 //Console.WriteLine(" !!! Setting child seeds in {0} to {1}", RegionInfo.RegionName, value.Count);
194 childrenSeeds[agentID] = seeds;
195 }
196
197 public void DumpChildrenSeeds(UUID agentID)
198 {
199 Console.WriteLine("================ ChildrenSeed {0} ================", m_scene.RegionInfo.RegionName);
200 foreach (KeyValuePair<ulong, string> kvp in childrenSeeds[agentID])
201 {
202 uint x, y;
203 Utils.LongToUInts(kvp.Key, out x, out y);
204 x = x / Constants.RegionSize;
205 y = y / Constants.RegionSize;
206 Console.WriteLine(" >> {0}, {1}: {2}", x, y, kvp.Value);
207 }
208 }
209 }
210} \ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs b/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs
new file mode 100644
index 0000000..107855d
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureDownloadModule.cs
@@ -0,0 +1,295 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Threading;
31using OpenMetaverse;
32using Nini.Config;
33using OpenSim.Framework;
34using OpenSim.Region.Framework.Interfaces;
35using OpenSim.Region.Framework.Scenes;
36using OpenSim.Framework.Communications.Cache;
37using BlockingQueue = OpenSim.Framework.BlockingQueue<OpenSim.Region.Framework.Interfaces.ITextureSender>;
38
39namespace OpenSim.Region.CoreModules.Agent.TextureDownload
40{
41 public class TextureDownloadModule : IRegionModule
42 {
43 private static readonly log4net.ILog m_log
44 = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
45
46 /// <summary>
47 /// There is one queue for all textures waiting to be sent, regardless of the requesting user.
48 /// </summary>
49 private readonly OpenSim.Framework.BlockingQueue<ITextureSender> m_queueSenders
50 = new OpenSim.Framework.BlockingQueue<ITextureSender>();
51
52 /// <summary>
53 /// Each user has their own texture download service.
54 /// </summary>
55 private readonly Dictionary<UUID, UserTextureDownloadService> m_userTextureServices =
56 new Dictionary<UUID, UserTextureDownloadService>();
57
58 private Scene m_scene;
59 private List<Scene> m_scenes = new List<Scene>();
60
61 private Thread m_thread;
62
63 public TextureDownloadModule()
64 {
65 }
66
67 #region IRegionModule Members
68
69 public void Initialise(Scene scene, IConfigSource config)
70 {
71 if (m_scene == null)
72 {
73 //Console.WriteLine("Creating Texture download module");
74 m_scene = scene;
75 m_thread = new Thread(new ThreadStart(ProcessTextureSenders));
76 m_thread.Name = "ProcessTextureSenderThread";
77 m_thread.IsBackground = true;
78 m_thread.Start();
79 ThreadTracker.Add(m_thread);
80 }
81
82 if (!m_scenes.Contains(scene))
83 {
84 m_scenes.Add(scene);
85 m_scene = scene;
86 m_scene.EventManager.OnNewClient += NewClient;
87 m_scene.EventManager.OnRemovePresence += EventManager_OnRemovePresence;
88 }
89 }
90
91 public void PostInitialise()
92 {
93 }
94
95 public void Close()
96 {
97 }
98
99 public string Name
100 {
101 get { return "TextureDownloadModule"; }
102 }
103
104 public bool IsSharedModule
105 {
106 get { return false; }
107 }
108
109 #endregion
110
111 /// <summary>
112 /// Cleanup the texture service related objects for the removed presence.
113 /// </summary>
114 /// <param name="agentId"> </param>
115 private void EventManager_OnRemovePresence(UUID agentId)
116 {
117 UserTextureDownloadService textureService;
118
119 lock (m_userTextureServices)
120 {
121 if (m_userTextureServices.TryGetValue(agentId, out textureService))
122 {
123 textureService.Close();
124 //m_log.DebugFormat("[TEXTURE MODULE]: Removing UserTextureServices from {0}", m_scene.RegionInfo.RegionName);
125 m_userTextureServices.Remove(agentId);
126 }
127 }
128 }
129
130 public void NewClient(IClientAPI client)
131 {
132 UserTextureDownloadService textureService;
133
134 lock (m_userTextureServices)
135 {
136 if (m_userTextureServices.TryGetValue(client.AgentId, out textureService))
137 {
138 textureService.Close();
139 //m_log.DebugFormat("[TEXTURE MODULE]: Removing outdated UserTextureServices from {0}", m_scene.RegionInfo.RegionName);
140 m_userTextureServices.Remove(client.AgentId);
141 }
142 m_userTextureServices.Add(client.AgentId, new UserTextureDownloadService(client, m_scene, m_queueSenders));
143 }
144
145 client.OnRequestTexture += TextureRequest;
146 }
147
148 /// I'm commenting this out, and replacing it with the implementation below, which
149 /// may return a null value. This is necessary for avoiding race conditions
150 /// recreating UserTextureServices for clients that have just been closed.
151 /// That behavior of always returning a UserTextureServices was causing the
152 /// A-B-A problem (mantis #2855).
153 ///
154 ///// <summary>
155 ///// Does this user have a registered texture download service?
156 ///// </summary>
157 ///// <param name="userID"></param>
158 ///// <param name="textureService"></param>
159 ///// <returns>Always returns true, since a service is created if one does not already exist</returns>
160 //private bool TryGetUserTextureService(
161 // IClientAPI client, out UserTextureDownloadService textureService)
162 //{
163 // lock (m_userTextureServices)
164 // {
165 // if (m_userTextureServices.TryGetValue(client.AgentId, out textureService))
166 // {
167 // //m_log.DebugFormat("[TEXTURE MODULE]: Found existing UserTextureServices in ", m_scene.RegionInfo.RegionName);
168 // return true;
169 // }
170
171 // m_log.DebugFormat("[TEXTURE MODULE]: Creating new UserTextureServices in ", m_scene.RegionInfo.RegionName);
172 // textureService = new UserTextureDownloadService(client, m_scene, m_queueSenders);
173 // m_userTextureServices.Add(client.AgentId, textureService);
174
175 // return true;
176 // }
177 //}
178
179 /// <summary>
180 /// Does this user have a registered texture download service?
181 /// </summary>
182 /// <param name="userID"></param>
183 /// <param name="textureService"></param>
184 /// <returns>A UserTextureDownloadService or null in the output parameter, and true or false accordingly.</returns>
185 private bool TryGetUserTextureService(IClientAPI client, out UserTextureDownloadService textureService)
186 {
187 lock (m_userTextureServices)
188 {
189 if (m_userTextureServices.TryGetValue(client.AgentId, out textureService))
190 {
191 //m_log.DebugFormat("[TEXTURE MODULE]: Found existing UserTextureServices in ", m_scene.RegionInfo.RegionName);
192 return true;
193 }
194
195 textureService = null;
196 return false;
197 }
198 }
199
200 /// <summary>
201 /// Start the process of requesting a given texture.
202 /// </summary>
203 /// <param name="sender"> </param>
204 /// <param name="e"></param>
205 public void TextureRequest(Object sender, TextureRequestArgs e)
206 {
207 IClientAPI client = (IClientAPI)sender;
208
209 if (e.Priority == 1016001f) // Preview
210 {
211 if (client.Scene is Scene)
212 {
213 Scene scene = (Scene)client.Scene;
214
215 CachedUserInfo profile = scene.CommsManager.UserProfileCacheService.GetUserDetails(client.AgentId);
216 if (profile == null) // Deny unknown user
217 return;
218
219 if (profile.RootFolder == null) // Deny no inventory
220 return;
221
222 if (profile.UserProfile.GodLevel < 200 && profile.RootFolder.FindAsset(e.RequestedAssetID) == null) // Deny if not owned
223 return;
224
225 m_log.Debug("Texture preview");
226 }
227 }
228
229 UserTextureDownloadService textureService;
230
231 if (TryGetUserTextureService(client, out textureService))
232 {
233 textureService.HandleTextureRequest(e);
234 }
235 }
236
237 /// <summary>
238 /// Entry point for the thread dedicated to processing the texture queue.
239 /// </summary>
240 public void ProcessTextureSenders()
241 {
242 ITextureSender sender = null;
243
244 try
245 {
246 while (true)
247 {
248 sender = m_queueSenders.Dequeue();
249
250 if (sender.Cancel)
251 {
252 TextureSent(sender);
253
254 sender.Cancel = false;
255 }
256 else
257 {
258 bool finished = sender.SendTexturePacket();
259 if (finished)
260 {
261 TextureSent(sender);
262 }
263 else
264 {
265 m_queueSenders.Enqueue(sender);
266 }
267 }
268
269 // Make sure that any sender we currently have can get garbage collected
270 sender = null;
271
272 //m_log.InfoFormat("[TEXTURE] Texture sender queue size: {0}", m_queueSenders.Count());
273 }
274 }
275 catch (Exception e)
276 {
277 // TODO: Let users in the sim and those entering it and possibly an external watchdog know what has happened
278 m_log.ErrorFormat(
279 "[TEXTURE]: Texture send thread terminating with exception. PLEASE REBOOT YOUR SIM - TEXTURES WILL NOT BE AVAILABLE UNTIL YOU DO. Exception is {0}",
280 e);
281 }
282 }
283
284 /// <summary>
285 /// Called when the texture has finished sending.
286 /// </summary>
287 /// <param name="sender"></param>
288 private void TextureSent(ITextureSender sender)
289 {
290 sender.Sending = false;
291 //m_log.DebugFormat("[TEXTURE]: Removing download stat for {0}", sender.assetID);
292 m_scene.AddPendingDownloads(-1);
293 }
294 }
295}
diff --git a/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs b/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs
new file mode 100644
index 0000000..7309282
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/TextureDownload/TextureNotFoundSender.cs
@@ -0,0 +1,90 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System.Reflection;
29using log4net;
30using OpenMetaverse;
31using OpenMetaverse.Packets;
32using OpenSim.Framework;
33using OpenSim.Region.Framework.Interfaces;
34
35namespace OpenSim.Region.CoreModules.Agent.TextureDownload
36{
37 /// <summary>
38 /// Sends a 'texture not found' packet back to the client
39 /// </summary>
40 public class TextureNotFoundSender : ITextureSender
41 {
42 // private static readonly log4net.ILog m_log
43 // = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
44
45 // private IClientAPI m_client;
46 // private UUID m_textureId;
47
48 public TextureNotFoundSender(IClientAPI client, UUID textureID)
49 {
50 //m_client = client;
51 //m_textureId = textureID;
52 }
53
54 #region ITextureSender Members
55
56 public bool Sending
57 {
58 get { return false; }
59 set { }
60 }
61
62 public bool Cancel
63 {
64 get { return false; }
65 set { }
66 }
67
68 // See ITextureSender
69 public void UpdateRequest(int discardLevel, uint packetNumber)
70 {
71 // No need to implement since priority changes don't affect this operation
72 }
73
74 // See ITextureSender
75 public bool SendTexturePacket()
76 {
77 // m_log.DebugFormat(
78 // "[TEXTURE NOT FOUND SENDER]: Informing the client that texture {0} cannot be found",
79 // m_textureId);
80
81 // XXX Temporarily disabling as this appears to be causing client crashes on at least
82 // 1.19.0(5) of the Linden Second Life client.
83 // m_client.SendImageNotFound(m_textureId);
84
85 return true;
86 }
87
88 #endregion
89 }
90}
diff --git a/OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs b/OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs
new file mode 100644
index 0000000..675bb0f
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/TextureDownload/UserTextureDownloadService.cs
@@ -0,0 +1,267 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System.Collections.Generic;
29using System.Reflection;
30using OpenMetaverse;
31using log4net;
32using OpenSim.Framework;
33using OpenSim.Framework.Communications.Limit;
34using OpenSim.Framework.Statistics;
35using OpenSim.Region.Framework.Interfaces;
36using OpenSim.Region.Framework.Scenes;
37
38namespace OpenSim.Region.CoreModules.Agent.TextureDownload
39{
40 /// <summary>
41 /// This module sets up texture senders in response to client texture requests, and places them on a
42 /// processing queue once those senders have the appropriate data (i.e. a texture retrieved from the
43 /// asset cache).
44 /// </summary>
45 public class UserTextureDownloadService
46 {
47 private static readonly ILog m_log
48 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49
50 /// <summary>
51 /// True if the service has been closed, probably because a user with texture requests still queued
52 /// logged out.
53 /// </summary>
54 private bool closed;
55
56 /// <summary>
57 /// We will allow the client to request the same texture n times before dropping further requests
58 ///
59 /// This number includes repeated requests for the same texture at different resolutions (which we don't
60 /// currently handle properly as far as I know). However, this situation should be handled in a more
61 /// sophisticated way.
62 /// </summary>
63 private static readonly int MAX_ALLOWED_TEXTURE_REQUESTS = 5;
64
65 /// <summary>
66 /// XXX Also going to limit requests for found textures.
67 /// </summary>
68 private readonly IRequestLimitStrategy<UUID> foundTextureLimitStrategy
69 = new RepeatLimitStrategy<UUID>(MAX_ALLOWED_TEXTURE_REQUESTS);
70
71 private readonly IClientAPI m_client;
72 private readonly Scene m_scene;
73
74 /// <summary>
75 /// Texture Senders are placed in this queue once they have received their texture from the asset
76 /// cache. Another module actually invokes the send.
77 /// </summary>
78 private readonly OpenSim.Framework.BlockingQueue<ITextureSender> m_sharedSendersQueue;
79
80 /// <summary>
81 /// Holds texture senders before they have received the appropriate texture from the asset cache.
82 /// </summary>
83 private readonly Dictionary<UUID, TextureSender.TextureSender> m_textureSenders = new Dictionary<UUID, TextureSender.TextureSender>();
84
85 /// <summary>
86 /// We're going to limit requests for the same missing texture.
87 /// XXX This is really a temporary solution to deal with the situation where a client continually requests
88 /// the same missing textures
89 /// </summary>
90 private readonly IRequestLimitStrategy<UUID> missingTextureLimitStrategy
91 = new RepeatLimitStrategy<UUID>(MAX_ALLOWED_TEXTURE_REQUESTS);
92
93 public UserTextureDownloadService(
94 IClientAPI client, Scene scene, OpenSim.Framework.BlockingQueue<ITextureSender> sharedQueue)
95 {
96 m_client = client;
97 m_scene = scene;
98 m_sharedSendersQueue = sharedQueue;
99 }
100
101 /// <summary>
102 /// Handle a texture request. This involves creating a texture sender and placing it on the
103 /// previously passed in shared queue.
104 /// </summary>
105 /// <param name="e"></param>
106 public void HandleTextureRequest(TextureRequestArgs e)
107 {
108 TextureSender.TextureSender textureSender;
109
110 //TODO: should be working out the data size/ number of packets to be sent for each discard level
111 if ((e.DiscardLevel >= 0) || (e.Priority != 0))
112 {
113 lock (m_textureSenders)
114 {
115 if (m_textureSenders.TryGetValue(e.RequestedAssetID, out textureSender))
116 {
117 // If we've received new non UUID information for this request and it hasn't dispatched
118 // yet, then update the request accordingly.
119 textureSender.UpdateRequest(e.DiscardLevel, e.PacketNumber);
120 }
121 else
122 {
123 // m_log.DebugFormat("[TEXTURE]: Received a request for texture {0}", e.RequestedAssetID);
124
125 if (!foundTextureLimitStrategy.AllowRequest(e.RequestedAssetID))
126 {
127 // m_log.DebugFormat(
128 // "[TEXTURE]: Refusing request for {0} from client {1}",
129 // e.RequestedAssetID, m_client.AgentId);
130
131 return;
132 }
133 else if (!missingTextureLimitStrategy.AllowRequest(e.RequestedAssetID))
134 {
135 if (missingTextureLimitStrategy.IsFirstRefusal(e.RequestedAssetID))
136 {
137 if (StatsManager.SimExtraStats != null)
138 StatsManager.SimExtraStats.AddBlockedMissingTextureRequest();
139
140 // Commenting out this message for now as it causes too much noise with other
141 // debug messages.
142 // m_log.DebugFormat(
143 // "[TEXTURE]: Dropping requests for notified missing texture {0} for client {1} since we have received more than {2} requests",
144 // e.RequestedAssetID, m_client.AgentId, MAX_ALLOWED_TEXTURE_REQUESTS);
145 }
146
147 return;
148 }
149
150 m_scene.AddPendingDownloads(1);
151
152 TextureSender.TextureSender requestHandler = new TextureSender.TextureSender(m_client, e.DiscardLevel, e.PacketNumber);
153 m_textureSenders.Add(e.RequestedAssetID, requestHandler);
154
155 m_scene.AssetCache.GetAsset(e.RequestedAssetID, TextureCallback, true);
156 }
157 }
158 }
159 else
160 {
161 lock (m_textureSenders)
162 {
163 if (m_textureSenders.TryGetValue(e.RequestedAssetID, out textureSender))
164 {
165 textureSender.Cancel = true;
166 }
167 }
168 }
169 }
170
171 /// <summary>
172 /// The callback for the asset cache when a texture has been retrieved. This method queues the
173 /// texture sender for processing.
174 /// </summary>
175 /// <param name="textureID"></param>
176 /// <param name="texture"></param>
177 public void TextureCallback(UUID textureID, AssetBase texture)
178 {
179 //m_log.DebugFormat("[USER TEXTURE DOWNLOAD SERVICE]: Calling TextureCallback with {0}, texture == null is {1}", textureID, (texture == null ? true : false));
180
181 // There may still be texture requests pending for a logged out client
182 if (closed)
183 return;
184
185 lock (m_textureSenders)
186 {
187 TextureSender.TextureSender textureSender;
188 if (m_textureSenders.TryGetValue(textureID, out textureSender))
189 {
190 // XXX It may be perfectly valid for a texture to have no data... but if we pass
191 // this on to the TextureSender it will blow up, so just discard for now.
192 // Needs investigation.
193 if (texture == null || texture.Data == null)
194 {
195 if (!missingTextureLimitStrategy.IsMonitoringRequests(textureID))
196 {
197 missingTextureLimitStrategy.MonitorRequests(textureID);
198
199 // m_log.DebugFormat(
200 // "[TEXTURE]: Queueing first TextureNotFoundSender for {0}, client {1}",
201 // textureID, m_client.AgentId);
202 }
203
204 ITextureSender textureNotFoundSender = new TextureNotFoundSender(m_client, textureID);
205 EnqueueTextureSender(textureNotFoundSender);
206 }
207 else
208 {
209 if (!textureSender.ImageLoaded)
210 {
211 textureSender.TextureReceived(texture);
212 EnqueueTextureSender(textureSender);
213
214 foundTextureLimitStrategy.MonitorRequests(textureID);
215 }
216 }
217
218 //m_log.InfoFormat("[TEXTURE] Removing texture sender with uuid {0}", textureID);
219 m_textureSenders.Remove(textureID);
220 //m_log.InfoFormat("[TEXTURE] Current texture senders in dictionary: {0}", m_textureSenders.Count);
221 }
222 else
223 {
224 m_log.WarnFormat(
225 "[TEXTURE]: Got a texture uuid {0} with no sender object to handle it, this shouldn't happen",
226 textureID);
227 }
228 }
229 }
230
231 /// <summary>
232 /// Place a ready texture sender on the processing queue.
233 /// </summary>
234 /// <param name="textureSender"></param>
235 private void EnqueueTextureSender(ITextureSender textureSender)
236 {
237 textureSender.Cancel = false;
238 textureSender.Sending = true;
239
240 if (!m_sharedSendersQueue.Contains(textureSender))
241 {
242 m_sharedSendersQueue.Enqueue(textureSender);
243 }
244 }
245
246 /// <summary>
247 /// Close this module.
248 /// </summary>
249 internal void Close()
250 {
251 closed = true;
252
253 lock (m_textureSenders)
254 {
255 foreach (TextureSender.TextureSender textureSender in m_textureSenders.Values)
256 {
257 textureSender.Cancel = true;
258 }
259
260 m_textureSenders.Clear();
261 }
262
263 // XXX: It might be possible to also remove pending texture requests from the asset cache queues,
264 // though this might also be more trouble than it's worth.
265 }
266 }
267}
diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs
new file mode 100644
index 0000000..df672b8
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs
@@ -0,0 +1,533 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.IO;
30using System.Reflection;
31using System.Text;
32using System.Threading;
33using System.Collections.Generic;
34using log4net;
35using Nini.Config;
36using OpenMetaverse;
37using OpenMetaverse.Imaging;
38using OpenSim.Framework;
39using OpenSim.Region.Framework.Interfaces;
40using OpenSim.Region.Framework.Scenes;
41
42namespace OpenSim.Region.CoreModules.Agent.TextureSender
43{
44 public class J2KDecoderModule : IRegionModule, IJ2KDecoder
45 {
46 #region IRegionModule Members
47
48 private static readonly ILog m_log
49 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50
51 /// <summary>
52 /// Cached Decoded Layers
53 /// </summary>
54 private readonly Dictionary<UUID, OpenJPEG.J2KLayerInfo[]> m_cacheddecode = new Dictionary<UUID, OpenJPEG.J2KLayerInfo[]>();
55 private bool OpenJpegFail = false;
56 private readonly string CacheFolder = Util.dataDir() + "/j2kDecodeCache";
57 private readonly J2KDecodeFileCache fCache;
58
59 /// <summary>
60 /// List of client methods to notify of results of decode
61 /// </summary>
62 private readonly Dictionary<UUID, List<DecodedCallback>> m_notifyList = new Dictionary<UUID, List<DecodedCallback>>();
63
64 public J2KDecoderModule()
65 {
66 fCache = new J2KDecodeFileCache(CacheFolder);
67 }
68
69 public void Initialise(Scene scene, IConfigSource source)
70 {
71 scene.RegisterModuleInterface<IJ2KDecoder>(this);
72 }
73
74 public void PostInitialise()
75 {
76
77 }
78
79 public void Close()
80 {
81
82 }
83
84 public string Name
85 {
86 get { return "J2KDecoderModule"; }
87 }
88
89 public bool IsSharedModule
90 {
91 get { return true; }
92 }
93
94 #endregion
95
96 #region IJ2KDecoder Members
97
98
99 public void decode(UUID AssetId, byte[] assetData, DecodedCallback decodedReturn)
100 {
101 // Dummy for if decoding fails.
102 OpenJPEG.J2KLayerInfo[] result = new OpenJPEG.J2KLayerInfo[0];
103
104 // Check if it's cached
105 bool cached = false;
106 lock (m_cacheddecode)
107 {
108 if (m_cacheddecode.ContainsKey(AssetId))
109 {
110 cached = true;
111 result = m_cacheddecode[AssetId];
112 }
113 }
114
115 // If it's cached, return the cached results
116 if (cached)
117 {
118 decodedReturn(AssetId, result);
119 }
120 else
121 {
122 // not cached, so we need to decode it
123 // Add to notify list and start decoding.
124 // Next request for this asset while it's decoding will only be added to the notify list
125 // once this is decoded, requests will be served from the cache and all clients in the notifylist will be updated
126 bool decode = false;
127 lock (m_notifyList)
128 {
129 if (m_notifyList.ContainsKey(AssetId))
130 {
131 m_notifyList[AssetId].Add(decodedReturn);
132 }
133 else
134 {
135 List<DecodedCallback> notifylist = new List<DecodedCallback>();
136 notifylist.Add(decodedReturn);
137 m_notifyList.Add(AssetId, notifylist);
138 decode = true;
139 }
140 }
141 // Do Decode!
142 if (decode)
143 {
144 doJ2kDecode(AssetId, assetData);
145 }
146 }
147 }
148
149 /// <summary>
150 /// Provides a synchronous decode so that caller can be assured that this executes before the next line
151 /// </summary>
152 /// <param name="AssetId"></param>
153 /// <param name="j2kdata"></param>
154 public void syncdecode(UUID AssetId, byte[] j2kdata)
155 {
156 doJ2kDecode(AssetId, j2kdata);
157 }
158
159 #endregion
160
161 /// <summary>
162 /// Decode Jpeg2000 Asset Data
163 /// </summary>
164 /// <param name="AssetId">UUID of Asset</param>
165 /// <param name="j2kdata">Byte Array Asset Data </param>
166 private void doJ2kDecode(UUID AssetId, byte[] j2kdata)
167 {
168 int DecodeTime = 0;
169 DecodeTime = System.Environment.TickCount;
170 OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[0]; // Dummy result for if it fails. Informs that there's only full quality
171
172 if (!OpenJpegFail)
173 {
174 if (!fCache.TryLoadCacheForAsset(AssetId, out layers))
175 {
176 try
177 {
178
179 AssetTexture texture = new AssetTexture(AssetId, j2kdata);
180 if (texture.DecodeLayerBoundaries())
181 {
182 bool sane = true;
183
184 // Sanity check all of the layers
185 for (int i = 0; i < texture.LayerInfo.Length; i++)
186 {
187 if (texture.LayerInfo[i].End > texture.AssetData.Length)
188 {
189 sane = false;
190 break;
191 }
192 }
193
194 if (sane)
195 {
196 layers = texture.LayerInfo;
197 fCache.SaveFileCacheForAsset(AssetId, layers);
198
199
200 // Write out decode time
201 m_log.InfoFormat("[J2KDecoderModule]: {0} Decode Time: {1}", System.Environment.TickCount - DecodeTime,
202 AssetId);
203
204 }
205 else
206 {
207 m_log.WarnFormat(
208 "[J2KDecoderModule]: JPEG2000 texture decoding succeeded, but sanity check failed for {0}",
209 AssetId);
210 }
211 }
212
213 else
214 {
215 m_log.WarnFormat("[J2KDecoderModule]: JPEG2000 texture decoding failed for {0}", AssetId);
216 }
217 texture = null; // dereference and dispose of ManagedImage
218 }
219 catch (DllNotFoundException)
220 {
221 m_log.Error(
222 "[J2KDecoderModule]: OpenJpeg is not installed properly. Decoding disabled! This will slow down texture performance! Often times this is because of an old version of GLIBC. You must have version 2.4 or above!");
223 OpenJpegFail = true;
224 }
225 catch (Exception ex)
226 {
227 m_log.WarnFormat(
228 "[J2KDecoderModule]: JPEG2000 texture decoding threw an exception for {0}, {1}",
229 AssetId, ex);
230 }
231 }
232
233 }
234
235
236 // Cache Decoded layers
237 lock (m_cacheddecode)
238 {
239 if (!m_cacheddecode.ContainsKey(AssetId))
240 m_cacheddecode.Add(AssetId, layers);
241
242 }
243
244
245
246 // Notify Interested Parties
247 lock (m_notifyList)
248 {
249 if (m_notifyList.ContainsKey(AssetId))
250 {
251 foreach (DecodedCallback d in m_notifyList[AssetId])
252 {
253 if (d != null)
254 d.DynamicInvoke(AssetId, layers);
255 }
256 m_notifyList.Remove(AssetId);
257 }
258 }
259 }
260 }
261
262 public class J2KDecodeFileCache
263 {
264 private readonly string m_cacheDecodeFolder;
265 private bool enabled = true;
266
267 private static readonly ILog m_log
268 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
269
270 /// <summary>
271 /// Creates a new instance of a file cache
272 /// </summary>
273 /// <param name="pFolder">base folder for the cache. Will be created if it doesn't exist</param>
274 public J2KDecodeFileCache(string pFolder)
275 {
276 m_cacheDecodeFolder = pFolder;
277 if (!Directory.Exists(pFolder))
278 {
279 Createj2KCacheFolder(pFolder);
280 }
281
282 }
283
284 /// <summary>
285 /// Save Layers to Disk Cache
286 /// </summary>
287 /// <param name="AssetId">Asset to Save the layers. Used int he file name by default</param>
288 /// <param name="Layers">The Layer Data from OpenJpeg</param>
289 /// <returns></returns>
290 public bool SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers)
291 {
292 if (Layers.Length > 0 && enabled)
293 {
294 FileStream fsCache =
295 new FileStream(String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId)),
296 FileMode.Create);
297 StreamWriter fsSWCache = new StreamWriter(fsCache);
298 StringBuilder stringResult = new StringBuilder();
299 string strEnd = "\n";
300 for (int i = 0; i < Layers.Length; i++)
301 {
302 if (i == (Layers.Length - 1))
303 strEnd = "";
304
305 stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].Size, strEnd);
306 }
307 fsSWCache.Write(stringResult.ToString());
308 fsSWCache.Close();
309 fsSWCache.Dispose();
310 fsCache.Dispose();
311 return true;
312 }
313
314
315 return false;
316 }
317
318
319 /// <summary>
320 /// Loads the Layer data from the disk cache
321 /// Returns true if load succeeded
322 /// </summary>
323 /// <param name="AssetId">AssetId that we're checking the cache for</param>
324 /// <param name="Layers">out layers to save to</param>
325 /// <returns>true if load succeeded</returns>
326 public bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers)
327 {
328 string filename = String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId));
329 Layers = new OpenJPEG.J2KLayerInfo[0];
330
331 if (!File.Exists(filename))
332 return false;
333
334 if (!enabled)
335 {
336 return false;
337 }
338
339 string readResult = string.Empty;
340
341 try
342 {
343 FileStream fsCachefile =
344 new FileStream(filename,
345 FileMode.Open);
346
347 StreamReader sr = new StreamReader(fsCachefile);
348 readResult = sr.ReadToEnd();
349
350 sr.Close();
351 sr.Dispose();
352 fsCachefile.Dispose();
353
354 }
355 catch (IOException ioe)
356 {
357 if (ioe is PathTooLongException)
358 {
359 m_log.Error(
360 "[J2KDecodeCache]: Cache Read failed. Path is too long.");
361 }
362 else if (ioe is DirectoryNotFoundException)
363 {
364 m_log.Error(
365 "[J2KDecodeCache]: Cache Read failed. Cache Directory does not exist!");
366 enabled = false;
367 }
368 else
369 {
370 m_log.Error(
371 "[J2KDecodeCache]: Cache Read failed. IO Exception.");
372 }
373 return false;
374
375 }
376 catch (UnauthorizedAccessException)
377 {
378 m_log.Error(
379 "[J2KDecodeCache]: Cache Read failed. UnauthorizedAccessException Exception. Do you have the proper permissions on this file?");
380 return false;
381 }
382 catch (ArgumentException ae)
383 {
384 if (ae is ArgumentNullException)
385 {
386 m_log.Error(
387 "[J2KDecodeCache]: Cache Read failed. No Filename provided");
388 }
389 else
390 {
391 m_log.Error(
392 "[J2KDecodeCache]: Cache Read failed. Filname was invalid");
393 }
394 return false;
395 }
396 catch (NotSupportedException)
397 {
398 m_log.Error(
399 "[J2KDecodeCache]: Cache Read failed, not supported. Cache disabled!");
400 enabled = false;
401
402 return false;
403 }
404 catch (Exception e)
405 {
406 m_log.ErrorFormat(
407 "[J2KDecodeCache]: Cache Read failed, unknown exception. Error: {0}",
408 e.ToString());
409 return false;
410 }
411
412 string[] lines = readResult.Split('\n');
413
414 if (lines.Length <= 0)
415 return false;
416
417 Layers = new OpenJPEG.J2KLayerInfo[lines.Length];
418
419 for (int i = 0; i < lines.Length; i++)
420 {
421 string[] elements = lines[i].Split('|');
422 if (elements.Length == 3)
423 {
424 int element1, element2;
425
426 try
427 {
428 element1 = Convert.ToInt32(elements[0]);
429 element2 = Convert.ToInt32(elements[1]);
430 }
431 catch (FormatException)
432 {
433 m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed with ErrorConvert for {0}", AssetId);
434 Layers = new OpenJPEG.J2KLayerInfo[0];
435 return false;
436 }
437
438 Layers[i] = new OpenJPEG.J2KLayerInfo();
439 Layers[i].Start = element1;
440 Layers[i].End = element2;
441
442 }
443 else
444 {
445 // reading failed
446 m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed for {0}", AssetId);
447 Layers = new OpenJPEG.J2KLayerInfo[0];
448 return false;
449 }
450 }
451
452
453
454
455 return true;
456 }
457
458 /// <summary>
459 /// Routine which converts assetid to file name
460 /// </summary>
461 /// <param name="AssetId">asset id of the image</param>
462 /// <returns>string filename</returns>
463 public string FileNameFromAssetId(UUID AssetId)
464 {
465 return String.Format("j2kCache_{0}.cache", AssetId);
466 }
467
468 /// <summary>
469 /// Creates the Cache Folder
470 /// </summary>
471 /// <param name="pFolder">Folder to Create</param>
472 public void Createj2KCacheFolder(string pFolder)
473 {
474 try
475 {
476 Directory.CreateDirectory(pFolder);
477 }
478 catch (IOException ioe)
479 {
480 if (ioe is PathTooLongException)
481 {
482 m_log.Error(
483 "[J2KDecodeCache]: Cache Directory does not exist and create failed because the path to the cache folder is too long. Cache disabled!");
484 }
485 else if (ioe is DirectoryNotFoundException)
486 {
487 m_log.Error(
488 "[J2KDecodeCache]: Cache Directory does not exist and create failed because the supplied base of the directory folder does not exist. Cache disabled!");
489 }
490 else
491 {
492 m_log.Error(
493 "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an IO Exception. Cache disabled!");
494 }
495 enabled = false;
496
497 }
498 catch (UnauthorizedAccessException)
499 {
500 m_log.Error(
501 "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an UnauthorizedAccessException Exception. Cache disabled!");
502 enabled = false;
503 }
504 catch (ArgumentException ae)
505 {
506 if (ae is ArgumentNullException)
507 {
508 m_log.Error(
509 "[J2KDecodeCache]: Cache Directory does not exist and create failed because the folder provided is invalid! Cache disabled!");
510 }
511 else
512 {
513 m_log.Error(
514 "[J2KDecodeCache]: Cache Directory does not exist and create failed because no cache folder was provided! Cache disabled!");
515 }
516 enabled = false;
517 }
518 catch (NotSupportedException)
519 {
520 m_log.Error(
521 "[J2KDecodeCache]: Cache Directory does not exist and create failed because it's not supported. Cache disabled!");
522 enabled = false;
523 }
524 catch (Exception e)
525 {
526 m_log.ErrorFormat(
527 "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an unknown exception. Cache disabled! Error: {0}",
528 e.ToString());
529 enabled = false;
530 }
531 }
532 }
533}
diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/Tests/TextureSenderTests.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/Tests/TextureSenderTests.cs
new file mode 100644
index 0000000..f0587bb
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/TextureSender/Tests/TextureSenderTests.cs
@@ -0,0 +1,177 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using NUnit.Framework;
31using NUnit.Framework.SyntaxHelpers;
32using OpenMetaverse;
33using OpenSim.Framework;
34using OpenSim.Tests.Common.Mock;
35
36namespace OpenSim.Region.CoreModules.Agent.TextureSender
37{
38 [TestFixture]
39 public class UserTextureSenderTests
40 {
41 public UUID uuid1;
42 public UUID uuid2;
43 public UUID uuid3;
44 public UUID uuid4;
45 public int npackets, testsize;
46 public TestClient client;
47 public TextureSender ts;
48 public static Random random = new Random();
49
50 [TestFixtureSetUp]
51 public void Init()
52 {
53 AgentCircuitData agent = new AgentCircuitData();
54 agent.AgentID = UUID.Random();
55 agent.firstname = "testfirstname";
56 agent.lastname = "testlastname";
57 agent.SessionID = UUID.Zero;
58 agent.SecureSessionID = UUID.Zero;
59 agent.circuitcode = 123;
60 agent.BaseFolder = UUID.Zero;
61 agent.InventoryFolder = UUID.Zero;
62 agent.startpos = Vector3.Zero;
63 agent.CapsPath = "http://wibble.com";
64 client = new TestClient(agent, null);
65 ts = new TextureSender(client, 0, 0);
66 testsize = random.Next(5000,15000);
67 npackets = CalculateNumPackets(testsize);
68 uuid1 = UUID.Random();
69 uuid2 = UUID.Random();
70 uuid3 = UUID.Random();
71 uuid4 = UUID.Random();
72 }
73
74 /// <summary>
75 /// Test sending package
76 /// </summary>
77 [Test]
78 public void T010_SendPkg()
79 {
80 // Normal sending
81 AssetBase abase = new AssetBase(uuid1, "asset one");
82 byte[] abdata = new byte[testsize];
83 random.NextBytes(abdata);
84 abase.Data = abdata;
85 bool isdone = false;
86 ts.TextureReceived(abase);
87 for (int i = 0; i < npackets; i++) {
88 isdone = ts.SendTexturePacket();
89 }
90
91 Assert.That(isdone,Is.False);
92 isdone = ts.SendTexturePacket();
93 Assert.That(isdone,Is.True);
94 }
95
96 [Test]
97 public void T011_UpdateReq()
98 {
99 // Test packet number start
100 AssetBase abase = new AssetBase(uuid2, "asset two");
101 byte[] abdata = new byte[testsize];
102 random.NextBytes(abdata);
103 abase.Data = abdata;
104
105 bool isdone = false;
106 ts.TextureReceived(abase);
107 ts.UpdateRequest(0,3);
108
109 for (int i = 0; i < npackets-3; i++) {
110 isdone = ts.SendTexturePacket();
111 }
112
113 Assert.That(isdone,Is.False);
114 isdone = ts.SendTexturePacket();
115 Assert.That(isdone,Is.True);
116
117 // Test discard level
118 abase = new AssetBase(uuid3, "asset three");
119 abdata = new byte[testsize];
120 random.NextBytes(abdata);
121 abase.Data = abdata;
122 isdone = false;
123 ts.TextureReceived(abase);
124 ts.UpdateRequest(-1,0);
125
126 Assert.That(ts.SendTexturePacket(),Is.True);
127
128 abase = new AssetBase(uuid4, "asset four");
129 abdata = new byte[testsize];
130 random.NextBytes(abdata);
131 abase.Data = abdata;
132 isdone = false;
133 ts.TextureReceived(abase);
134 ts.UpdateRequest(0,5);
135
136 for (int i = 0; i < npackets-5; i++) {
137 isdone = ts.SendTexturePacket();
138 }
139 Assert.That(isdone,Is.False);
140 isdone = ts.SendTexturePacket();
141 Assert.That(isdone,Is.True);
142 }
143
144 [Test]
145 public void T999_FinishStatus()
146 {
147 // Of the 4 assets "sent", only 2 sent the first part.
148 Assert.That(client.sentdatapkt.Count,Is.EqualTo(2));
149
150 // Sum of all packets sent:
151 int totalpkts = (npackets) + (npackets - 2) + (npackets - 4);
152 Assert.That(client.sentpktpkt.Count,Is.EqualTo(totalpkts));
153 }
154
155 /// <summary>
156 /// Calculate the number of packets that will be required to send the texture loaded into this sender
157 /// This is actually the number of 1000 byte packets not including an initial 600 byte packet...
158 /// Borrowed from TextureSender.cs
159 /// </summary>
160 /// <param name="length"></param>
161 /// <returns></returns>
162 private int CalculateNumPackets(int length)
163 {
164 int numPackets = 0;
165
166 if (length > 600)
167 {
168 //over 600 bytes so split up file
169 int restData = (length - 600);
170 int restPackets = ((restData + 999) / 1000);
171 numPackets = restPackets;
172 }
173
174 return numPackets;
175 }
176 }
177}
diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs
new file mode 100644
index 0000000..b3ac624
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/TextureSender/TextureSender.cs
@@ -0,0 +1,213 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Reflection;
30using OpenMetaverse.Packets;
31using log4net;
32using OpenSim.Framework;
33using OpenSim.Region.Framework.Interfaces;
34
35namespace OpenSim.Region.CoreModules.Agent.TextureSender
36{
37 /// <summary>
38 /// A TextureSender handles the process of receiving a texture requested by the client from the
39 /// AssetCache, and then sending that texture back to the client.
40 /// </summary>
41 public class TextureSender : ITextureSender
42 {
43 private static readonly ILog m_log
44 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45
46 /// <summary>
47 /// Records the number of times texture send has been called.
48 /// </summary>
49 public int counter = 0;
50
51 public bool ImageLoaded = false;
52
53 /// <summary>
54 /// Holds the texture asset to send.
55 /// </summary>
56 private AssetBase m_asset;
57
58 //public UUID assetID { get { return m_asset.Metadata.FullID; } }
59
60 // private bool m_cancel = false;
61
62 // See ITextureSender
63
64 // private bool m_sending = false;
65
66 /// <summary>
67 /// This is actually the number of extra packets required to send the texture data! We always assume
68 /// at least one is required.
69 /// </summary>
70 private int NumPackets = 0;
71
72 /// <summary>
73 /// Holds the packet number to send next. In this case, each packet is 1000 bytes long and starts
74 /// at the 600th byte (0th indexed).
75 /// </summary>
76 private int PacketCounter = 0;
77
78 private int RequestedDiscardLevel = -1;
79 private IClientAPI RequestUser;
80 private uint StartPacketNumber = 0;
81
82 public TextureSender(IClientAPI client, int discardLevel, uint packetNumber)
83 {
84 RequestUser = client;
85 RequestedDiscardLevel = discardLevel;
86 StartPacketNumber = packetNumber;
87 }
88
89 #region ITextureSender Members
90
91 public bool Cancel
92 {
93 get { return false; }
94 set
95 {
96 // m_cancel = value;
97 }
98 }
99
100 public bool Sending
101 {
102 get { return false; }
103 set
104 {
105 // m_sending = value;
106 }
107 }
108
109 // See ITextureSender
110 public void UpdateRequest(int discardLevel, uint packetNumber)
111 {
112 RequestedDiscardLevel = discardLevel;
113 StartPacketNumber = packetNumber;
114 PacketCounter = (int)StartPacketNumber;
115 }
116
117 // See ITextureSender
118 public bool SendTexturePacket()
119 {
120 //m_log.DebugFormat("[TEXTURE SENDER]: Sending packet for {0}", m_asset.Metadata.FullID);
121
122 SendPacket();
123 counter++;
124 if ((NumPackets == 0) || (RequestedDiscardLevel == -1) || (PacketCounter > NumPackets) ||
125 ((RequestedDiscardLevel > 0) && (counter > 50 + (NumPackets / (RequestedDiscardLevel + 1)))))
126 {
127 return true;
128 }
129 return false;
130 }
131
132 #endregion
133
134 /// <summary>
135 /// Load up the texture data to send.
136 /// </summary>
137 /// <param name="asset"></param>
138 public void TextureReceived(AssetBase asset)
139 {
140 m_asset = asset;
141 NumPackets = CalculateNumPackets(asset.Data.Length);
142 PacketCounter = (int)StartPacketNumber;
143 ImageLoaded = true;
144 }
145
146 /// <summary>
147 /// Sends a texture packet to the client.
148 /// </summary>
149 private void SendPacket()
150 {
151 if (PacketCounter <= NumPackets)
152 {
153 if (PacketCounter == 0)
154 {
155 if (NumPackets == 0)
156 {
157 RequestUser.SendImageFirstPart(1, m_asset.Metadata.FullID, (uint)m_asset.Data.Length, m_asset.Data, 2);
158 PacketCounter++;
159 }
160 else
161 {
162 byte[] ImageData1 = new byte[600];
163 Array.Copy(m_asset.Data, 0, ImageData1, 0, 600);
164
165 RequestUser.SendImageFirstPart(
166 (ushort)(NumPackets), m_asset.Metadata.FullID, (uint)m_asset.Data.Length, ImageData1, 2);
167 PacketCounter++;
168 }
169 }
170 else
171 {
172 int size = m_asset.Data.Length - 600 - (1000 * (PacketCounter - 1));
173 if (size > 1000) size = 1000;
174 byte[] imageData = new byte[size];
175 try
176 {
177 Array.Copy(m_asset.Data, 600 + (1000 * (PacketCounter - 1)), imageData, 0, size);
178 }
179 catch (ArgumentOutOfRangeException)
180 {
181 m_log.Error("[TEXTURE SENDER]: Unable to separate texture into multiple packets: Array bounds failure on asset:" +
182 m_asset.Metadata.ID);
183 return;
184 }
185
186 RequestUser.SendImageNextPart((ushort)PacketCounter, m_asset.Metadata.FullID, imageData);
187 PacketCounter++;
188 }
189 }
190 }
191
192 /// <summary>
193 /// Calculate the number of packets that will be required to send the texture loaded into this sender
194 /// This is actually the number of 1000 byte packets not including an initial 600 byte packet...
195 /// </summary>
196 /// <param name="length"></param>
197 /// <returns></returns>
198 private int CalculateNumPackets(int length)
199 {
200 int numPackets = 0;
201
202 if (length > 600)
203 {
204 //over 600 bytes so split up file
205 int restData = (length - 600);
206 int restPackets = ((restData + 999) / 1000);
207 numPackets = restPackets;
208 }
209
210 return numPackets;
211 }
212 }
213}
diff --git a/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs b/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs
new file mode 100644
index 0000000..cb81c0d
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs
@@ -0,0 +1,232 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using OpenMetaverse;
31using Nini.Config;
32using OpenSim.Framework;
33using OpenSim.Region.Framework.Interfaces;
34using OpenSim.Region.Framework.Scenes;
35
36namespace OpenSim.Region.CoreModules.Agent.Xfer
37{
38 public class XferModule : IRegionModule, IXfer
39 {
40 private Scene m_scene;
41 public Dictionary<string, byte[]> NewFiles = new Dictionary<string, byte[]>();
42 public Dictionary<ulong, XferDownLoad> Transfers = new Dictionary<ulong, XferDownLoad>();
43
44 #region IRegionModule Members
45
46 public void Initialise(Scene scene, IConfigSource config)
47 {
48 m_scene = scene;
49 m_scene.EventManager.OnNewClient += NewClient;
50
51 m_scene.RegisterModuleInterface<IXfer>(this);
52 }
53
54 public void PostInitialise()
55 {
56 }
57
58 public void Close()
59 {
60 }
61
62 public string Name
63 {
64 get { return "XferModule"; }
65 }
66
67 public bool IsSharedModule
68 {
69 get { return false; }
70 }
71
72 #endregion
73
74 #region IXfer Members
75
76 public bool AddNewFile(string fileName, byte[] data)
77 {
78 lock (NewFiles)
79 {
80 if (NewFiles.ContainsKey(fileName))
81 {
82 NewFiles[fileName] = data;
83 }
84 else
85 {
86 NewFiles.Add(fileName, data);
87 }
88 }
89 return true;
90 }
91
92 #endregion
93
94 public void NewClient(IClientAPI client)
95 {
96 client.OnRequestXfer += RequestXfer;
97 client.OnConfirmXfer += AckPacket;
98 }
99
100 /// <summary>
101 ///
102 /// </summary>
103 /// <param name="remoteClient"></param>
104 /// <param name="xferID"></param>
105 /// <param name="fileName"></param>
106 public void RequestXfer(IClientAPI remoteClient, ulong xferID, string fileName)
107 {
108
109 lock (NewFiles)
110 {
111 if (NewFiles.ContainsKey(fileName))
112 {
113 if (!Transfers.ContainsKey(xferID))
114 {
115 byte[] fileData = NewFiles[fileName];
116 XferDownLoad transaction = new XferDownLoad(fileName, fileData, xferID, remoteClient);
117 Transfers.Add(xferID, transaction);
118 NewFiles.Remove(fileName);
119
120 if (transaction.StartSend())
121 {
122 Transfers.Remove(xferID);
123 }
124 }
125 }
126 }
127 }
128
129 public void AckPacket(IClientAPI remoteClient, ulong xferID, uint packet)
130 {
131 if (Transfers.ContainsKey(xferID))
132 {
133 if (Transfers[xferID].AckPacket(packet))
134 {
135 {
136 Transfers.Remove(xferID);
137 }
138 }
139 }
140 }
141
142 #region Nested type: XferDownLoad
143
144 public class XferDownLoad
145 {
146 public IClientAPI Client;
147 private bool complete;
148 public byte[] Data = new byte[0];
149 public int DataPointer = 0;
150 public string FileName = String.Empty;
151 public uint Packet = 0;
152 public uint Serial = 1;
153 public ulong XferID = 0;
154
155 public XferDownLoad(string fileName, byte[] data, ulong xferID, IClientAPI client)
156 {
157 FileName = fileName;
158 Data = data;
159 XferID = xferID;
160 Client = client;
161 }
162
163 public XferDownLoad()
164 {
165 }
166
167 /// <summary>
168 /// Start a transfer
169 /// </summary>
170 /// <returns>True if the transfer is complete, false if not</returns>
171 public bool StartSend()
172 {
173 if (Data.Length < 1000)
174 {
175 // for now (testing) we only support files under 1000 bytes
176 byte[] transferData = new byte[Data.Length + 4];
177 Array.Copy(Utils.IntToBytes(Data.Length), 0, transferData, 0, 4);
178 Array.Copy(Data, 0, transferData, 4, Data.Length);
179 Client.SendXferPacket(XferID, 0 + 0x80000000, transferData);
180
181 complete = true;
182 }
183 else
184 {
185 byte[] transferData = new byte[1000 + 4];
186 Array.Copy(Utils.IntToBytes(Data.Length), 0, transferData, 0, 4);
187 Array.Copy(Data, 0, transferData, 4, 1000);
188 Client.SendXferPacket(XferID, 0, transferData);
189 Packet++;
190 DataPointer = 1000;
191 }
192
193 return complete;
194 }
195
196 /// <summary>
197 /// Respond to an ack packet from the client
198 /// </summary>
199 /// <param name="packet"></param>
200 /// <returns>True if the transfer is complete, false otherwise</returns>
201 public bool AckPacket(uint packet)
202 {
203 if (!complete)
204 {
205 if ((Data.Length - DataPointer) > 1000)
206 {
207 byte[] transferData = new byte[1000];
208 Array.Copy(Data, DataPointer, transferData, 0, 1000);
209 Client.SendXferPacket(XferID, Packet, transferData);
210 Packet++;
211 DataPointer += 1000;
212 }
213 else
214 {
215 byte[] transferData = new byte[Data.Length - DataPointer];
216 Array.Copy(Data, DataPointer, transferData, 0, Data.Length - DataPointer);
217 uint endPacket = Packet |= (uint) 0x80000000;
218 Client.SendXferPacket(XferID, endPacket, transferData);
219 Packet++;
220 DataPointer += (Data.Length - DataPointer);
221
222 complete = true;
223 }
224 }
225
226 return complete;
227 }
228 }
229
230 #endregion
231 }
232}