diff options
author | Adam Frisby | 2008-04-30 21:16:36 +0000 |
---|---|---|
committer | Adam Frisby | 2008-04-30 21:16:36 +0000 |
commit | f5c312bc3c2567449c7268a54a08a54119f58d53 (patch) | |
tree | 424668a4bbec6873ebc5b8256f3671db102f5e9c /OpenSim/Region/Environment/Modules/Agent | |
parent | * Adds the AuthbuyerID field to sqlite and makes use of it. (diff) | |
download | opensim-SC_OLD-f5c312bc3c2567449c7268a54a08a54119f58d53.zip opensim-SC_OLD-f5c312bc3c2567449c7268a54a08a54119f58d53.tar.gz opensim-SC_OLD-f5c312bc3c2567449c7268a54a08a54119f58d53.tar.bz2 opensim-SC_OLD-f5c312bc3c2567449c7268a54a08a54119f58d53.tar.xz |
* Refactored Environment/Modules directory - modules now reside in their own directory with any associated module-specific classes.
* Each module directory is currently inside one of the following category folders: Agent (Anything relating to do with Client<->Server communications.), Avatar (Anything to do with the avatar or presence inworld), Framework (Classes modules can use), Grid (Grid traffic, new OGS2 grid comms), Scripting (Scripting functions, etc), World (The enrivonment/scene, IE Sun/Tree modules.)
* This should be moved into a seperate project file.
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Agent')
8 files changed, 2031 insertions, 0 deletions
diff --git a/OpenSim/Region/Environment/Modules/Agent/AssetDownload/AssetDownloadModule.cs b/OpenSim/Region/Environment/Modules/Agent/AssetDownload/AssetDownloadModule.cs new file mode 100644 index 0000000..47c1479 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Agent/AssetDownload/AssetDownloadModule.cs | |||
@@ -0,0 +1,333 @@ | |||
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 | |||
28 | using System.Collections.Generic; | ||
29 | using libsecondlife; | ||
30 | using libsecondlife.Packets; | ||
31 | using Nini.Config; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.Environment.Interfaces; | ||
34 | using OpenSim.Region.Environment.Scenes; | ||
35 | |||
36 | namespace OpenSim.Region.Environment.Modules.Agent.AssetDownload | ||
37 | { | ||
38 | public class AssetDownloadModule : IRegionModule | ||
39 | { | ||
40 | private Scene m_scene; | ||
41 | private Dictionary<LLUUID, Scene> RegisteredScenes = new Dictionary<LLUUID, Scene>(); | ||
42 | /// | ||
43 | /// Assets requests (for each user) which are waiting for asset server data. This includes texture requests | ||
44 | /// </summary> | ||
45 | private Dictionary<LLUUID, Dictionary<LLUUID,AssetRequest>> RequestedAssets; | ||
46 | |||
47 | /// <summary> | ||
48 | /// Asset requests with data which are ready to be sent back to requesters. This includes textures. | ||
49 | /// </summary> | ||
50 | private List<AssetRequest> AssetRequests; | ||
51 | |||
52 | public AssetDownloadModule() | ||
53 | { | ||
54 | RequestedAssets = new Dictionary<LLUUID, Dictionary<LLUUID, AssetRequest>>(); | ||
55 | AssetRequests = new List<AssetRequest>(); | ||
56 | } | ||
57 | |||
58 | public void Initialise(Scene scene, IConfigSource config) | ||
59 | { | ||
60 | if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID)) | ||
61 | { | ||
62 | RegisteredScenes.Add(scene.RegionInfo.RegionID, scene); | ||
63 | // scene.EventManager.OnNewClient += NewClient; | ||
64 | } | ||
65 | |||
66 | if (m_scene == null) | ||
67 | { | ||
68 | m_scene = scene; | ||
69 | // m_thread = new Thread(new ThreadStart(RunAssetQueue)); | ||
70 | // m_thread.Name = "AssetDownloadQueueThread"; | ||
71 | // m_thread.IsBackground = true; | ||
72 | // m_thread.Start(); | ||
73 | // OpenSim.Framework.ThreadTracker.Add(m_thread); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | public void PostInitialise() | ||
78 | { | ||
79 | } | ||
80 | |||
81 | public void Close() | ||
82 | { | ||
83 | } | ||
84 | |||
85 | public string Name | ||
86 | { | ||
87 | get { return "AssetDownloadModule"; } | ||
88 | } | ||
89 | |||
90 | public bool IsSharedModule | ||
91 | { | ||
92 | get { return true; } | ||
93 | } | ||
94 | |||
95 | public void NewClient(IClientAPI client) | ||
96 | { | ||
97 | // client.OnRequestAsset += AddAssetRequest; | ||
98 | } | ||
99 | |||
100 | /// <summary> | ||
101 | /// Make an asset request the result of which will be packeted up and sent directly back to the client. | ||
102 | /// </summary> | ||
103 | /// <param name="userInfo"></param> | ||
104 | /// <param name="transferRequest"></param> | ||
105 | public void AddAssetRequest(IClientAPI userInfo, TransferRequestPacket transferRequest) | ||
106 | { | ||
107 | LLUUID requestID = null; | ||
108 | byte source = 2; | ||
109 | if (transferRequest.TransferInfo.SourceType == 2) | ||
110 | { | ||
111 | //direct asset request | ||
112 | requestID = new LLUUID(transferRequest.TransferInfo.Params, 0); | ||
113 | } | ||
114 | else if (transferRequest.TransferInfo.SourceType == 3) | ||
115 | { | ||
116 | //inventory asset request | ||
117 | requestID = new LLUUID(transferRequest.TransferInfo.Params, 80); | ||
118 | source = 3; | ||
119 | //Console.WriteLine("asset request " + requestID); | ||
120 | } | ||
121 | |||
122 | //not found asset | ||
123 | // so request from asset server | ||
124 | Dictionary<LLUUID, AssetRequest> userRequests = null; | ||
125 | if (RequestedAssets.TryGetValue(userInfo.AgentId, out userRequests)) | ||
126 | { | ||
127 | if (!userRequests.ContainsKey(requestID)) | ||
128 | { | ||
129 | AssetRequest request = new AssetRequest(); | ||
130 | request.RequestUser = userInfo; | ||
131 | request.RequestAssetID = requestID; | ||
132 | request.TransferRequestID = transferRequest.TransferInfo.TransferID; | ||
133 | request.AssetRequestSource = source; | ||
134 | request.Params = transferRequest.TransferInfo.Params; | ||
135 | userRequests[requestID] = request; | ||
136 | m_scene.AssetCache.GetAsset(requestID, AssetCallback, false); | ||
137 | } | ||
138 | } | ||
139 | else | ||
140 | { | ||
141 | userRequests = new Dictionary<LLUUID, AssetRequest>(); | ||
142 | AssetRequest request = new AssetRequest(); | ||
143 | request.RequestUser = userInfo; | ||
144 | request.RequestAssetID = requestID; | ||
145 | request.TransferRequestID = transferRequest.TransferInfo.TransferID; | ||
146 | request.AssetRequestSource = source; | ||
147 | request.Params = transferRequest.TransferInfo.Params; | ||
148 | userRequests.Add(requestID, request); | ||
149 | RequestedAssets[userInfo.AgentId] = userRequests; | ||
150 | m_scene.AssetCache.GetAsset(requestID, AssetCallback, false); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | public void AssetCallback(LLUUID assetID, AssetBase asset) | ||
155 | { | ||
156 | if (asset != null) | ||
157 | { | ||
158 | foreach (Dictionary<LLUUID, AssetRequest> userRequests in RequestedAssets.Values) | ||
159 | { | ||
160 | if (userRequests.ContainsKey(assetID)) | ||
161 | { | ||
162 | AssetRequest req = userRequests[assetID]; | ||
163 | if (req != null) | ||
164 | { | ||
165 | req.AssetInf = asset; | ||
166 | req.NumPackets = CalculateNumPackets(asset.Data); | ||
167 | |||
168 | userRequests.Remove(assetID); | ||
169 | AssetRequests.Add(req); | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | // TODO: unused | ||
177 | // private void RunAssetQueue() | ||
178 | // { | ||
179 | // while (true) | ||
180 | // { | ||
181 | // try | ||
182 | // { | ||
183 | // ProcessAssetQueue(); | ||
184 | // Thread.Sleep(500); | ||
185 | // } | ||
186 | // catch (Exception) | ||
187 | // { | ||
188 | // // m_log.Error("[ASSET CACHE]: " + e.ToString()); | ||
189 | // } | ||
190 | // } | ||
191 | // } | ||
192 | |||
193 | // TODO: unused | ||
194 | // /// <summary> | ||
195 | // /// Process the asset queue which sends packets directly back to the client. | ||
196 | // /// </summary> | ||
197 | // private void ProcessAssetQueue() | ||
198 | // { | ||
199 | // //should move the asset downloading to a module, like has been done with texture downloading | ||
200 | // if (AssetRequests.Count == 0) | ||
201 | // { | ||
202 | // //no requests waiting | ||
203 | // return; | ||
204 | // } | ||
205 | // // if less than 5, do all of them | ||
206 | // int num = Math.Min(5, AssetRequests.Count); | ||
207 | |||
208 | // AssetRequest req; | ||
209 | // for (int i = 0; i < num; i++) | ||
210 | // { | ||
211 | // req = (AssetRequest)AssetRequests[i]; | ||
212 | // //Console.WriteLine("sending asset " + req.RequestAssetID); | ||
213 | // TransferInfoPacket Transfer = new TransferInfoPacket(); | ||
214 | // Transfer.TransferInfo.ChannelType = 2; | ||
215 | // Transfer.TransferInfo.Status = 0; | ||
216 | // Transfer.TransferInfo.TargetType = 0; | ||
217 | // if (req.AssetRequestSource == 2) | ||
218 | // { | ||
219 | // Transfer.TransferInfo.Params = new byte[20]; | ||
220 | // Array.Copy(req.RequestAssetID.GetBytes(), 0, Transfer.TransferInfo.Params, 0, 16); | ||
221 | // int assType = (int)req.AssetInf.Type; | ||
222 | // Array.Copy(Helpers.IntToBytes(assType), 0, Transfer.TransferInfo.Params, 16, 4); | ||
223 | // } | ||
224 | // else if (req.AssetRequestSource == 3) | ||
225 | // { | ||
226 | // Transfer.TransferInfo.Params = req.Params; | ||
227 | // // Transfer.TransferInfo.Params = new byte[100]; | ||
228 | // //Array.Copy(req.RequestUser.AgentId.GetBytes(), 0, Transfer.TransferInfo.Params, 0, 16); | ||
229 | // //Array.Copy(req.RequestUser.SessionId.GetBytes(), 0, Transfer.TransferInfo.Params, 16, 16); | ||
230 | // } | ||
231 | // Transfer.TransferInfo.Size = (int)req.AssetInf.Data.Length; | ||
232 | // Transfer.TransferInfo.TransferID = req.TransferRequestID; | ||
233 | // req.RequestUser.OutPacket(Transfer, ThrottleOutPacketType.Asset); | ||
234 | |||
235 | // if (req.NumPackets == 1) | ||
236 | // { | ||
237 | // TransferPacketPacket TransferPacket = new TransferPacketPacket(); | ||
238 | // TransferPacket.TransferData.Packet = 0; | ||
239 | // TransferPacket.TransferData.ChannelType = 2; | ||
240 | // TransferPacket.TransferData.TransferID = req.TransferRequestID; | ||
241 | // TransferPacket.TransferData.Data = req.AssetInf.Data; | ||
242 | // TransferPacket.TransferData.Status = 1; | ||
243 | // req.RequestUser.OutPacket(TransferPacket, ThrottleOutPacketType.Asset); | ||
244 | // } | ||
245 | // else | ||
246 | // { | ||
247 | // int processedLength = 0; | ||
248 | // // libsecondlife hardcodes 1500 as the maximum data chunk size | ||
249 | // int maxChunkSize = 1250; | ||
250 | // int packetNumber = 0; | ||
251 | |||
252 | // while (processedLength < req.AssetInf.Data.Length) | ||
253 | // { | ||
254 | // TransferPacketPacket TransferPacket = new TransferPacketPacket(); | ||
255 | // TransferPacket.TransferData.Packet = packetNumber; | ||
256 | // TransferPacket.TransferData.ChannelType = 2; | ||
257 | // TransferPacket.TransferData.TransferID = req.TransferRequestID; | ||
258 | |||
259 | // int chunkSize = Math.Min(req.AssetInf.Data.Length - processedLength, maxChunkSize); | ||
260 | // byte[] chunk = new byte[chunkSize]; | ||
261 | // Array.Copy(req.AssetInf.Data, processedLength, chunk, 0, chunk.Length); | ||
262 | |||
263 | // TransferPacket.TransferData.Data = chunk; | ||
264 | |||
265 | // // 0 indicates more packets to come, 1 indicates last packet | ||
266 | // if (req.AssetInf.Data.Length - processedLength > maxChunkSize) | ||
267 | // { | ||
268 | // TransferPacket.TransferData.Status = 0; | ||
269 | // } | ||
270 | // else | ||
271 | // { | ||
272 | // TransferPacket.TransferData.Status = 1; | ||
273 | // } | ||
274 | |||
275 | // req.RequestUser.OutPacket(TransferPacket, ThrottleOutPacketType.Asset); | ||
276 | |||
277 | // processedLength += chunkSize; | ||
278 | // packetNumber++; | ||
279 | // } | ||
280 | // } | ||
281 | // } | ||
282 | |||
283 | // //remove requests that have been completed | ||
284 | // for (int i = 0; i < num; i++) | ||
285 | // { | ||
286 | // AssetRequests.RemoveAt(0); | ||
287 | // } | ||
288 | // } | ||
289 | |||
290 | /// <summary> | ||
291 | /// Calculate the number of packets required to send the asset to the client. | ||
292 | /// </summary> | ||
293 | /// <param name="data"></param> | ||
294 | /// <returns></returns> | ||
295 | private int CalculateNumPackets(byte[] data) | ||
296 | { | ||
297 | const uint m_maxPacketSize = 600; | ||
298 | int numPackets = 1; | ||
299 | |||
300 | if (data.LongLength > m_maxPacketSize) | ||
301 | { | ||
302 | // over max number of bytes so split up file | ||
303 | long restData = data.LongLength - m_maxPacketSize; | ||
304 | int restPackets = (int)((restData + m_maxPacketSize - 1) / m_maxPacketSize); | ||
305 | numPackets += restPackets; | ||
306 | } | ||
307 | |||
308 | return numPackets; | ||
309 | } | ||
310 | |||
311 | public class AssetRequest | ||
312 | { | ||
313 | public IClientAPI RequestUser; | ||
314 | public LLUUID RequestAssetID; | ||
315 | public AssetBase AssetInf; | ||
316 | public AssetBase ImageInfo; | ||
317 | public LLUUID TransferRequestID; | ||
318 | public long DataPointer = 0; | ||
319 | public int NumPackets = 0; | ||
320 | public int PacketCounter = 0; | ||
321 | public bool IsTextureRequest; | ||
322 | public byte AssetRequestSource = 2; | ||
323 | public byte[] Params = null; | ||
324 | //public bool AssetInCache; | ||
325 | //public int TimeRequested; | ||
326 | public int DiscardLevel = -1; | ||
327 | |||
328 | public AssetRequest() | ||
329 | { | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/Environment/Modules/Agent/AssetTransaction/AgentAssetsTransactions.cs b/OpenSim/Region/Environment/Modules/Agent/AssetTransaction/AgentAssetsTransactions.cs new file mode 100644 index 0000000..74bb247 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Agent/AssetTransaction/AgentAssetsTransactions.cs | |||
@@ -0,0 +1,407 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using libsecondlife; | ||
32 | using libsecondlife.Packets; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Framework.Communications.Cache; | ||
35 | |||
36 | namespace OpenSim.Region.Environment.Modules.Agent.AssetTransaction | ||
37 | { | ||
38 | /// <summary> | ||
39 | /// Manage asset transactions for a single agent. | ||
40 | /// </summary> | ||
41 | public class AgentAssetTransactions | ||
42 | { | ||
43 | //private static readonly log4net.ILog m_log | ||
44 | // = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||
45 | |||
46 | // Fields | ||
47 | public LLUUID UserID; | ||
48 | public Dictionary<LLUUID, AssetXferUploader> XferUploaders = new Dictionary<LLUUID, AssetXferUploader>(); | ||
49 | public AgentAssetTransactionsManager Manager; | ||
50 | private bool m_dumpAssetsToFile; | ||
51 | |||
52 | // Methods | ||
53 | public AgentAssetTransactions(LLUUID agentID, AgentAssetTransactionsManager manager, bool dumpAssetsToFile) | ||
54 | { | ||
55 | UserID = agentID; | ||
56 | Manager = manager; | ||
57 | m_dumpAssetsToFile = dumpAssetsToFile; | ||
58 | } | ||
59 | |||
60 | public AssetXferUploader RequestXferUploader(LLUUID transactionID) | ||
61 | { | ||
62 | if (!XferUploaders.ContainsKey(transactionID)) | ||
63 | { | ||
64 | AssetXferUploader uploader = new AssetXferUploader(this, m_dumpAssetsToFile); | ||
65 | |||
66 | lock (XferUploaders) | ||
67 | { | ||
68 | XferUploaders.Add(transactionID, uploader); | ||
69 | } | ||
70 | |||
71 | return uploader; | ||
72 | } | ||
73 | return null; | ||
74 | } | ||
75 | |||
76 | public void HandleXfer(ulong xferID, uint packetID, byte[] data) | ||
77 | { | ||
78 | // AssetXferUploader uploaderFound = null; | ||
79 | |||
80 | lock (XferUploaders) | ||
81 | { | ||
82 | foreach (AssetXferUploader uploader in XferUploaders.Values) | ||
83 | { | ||
84 | if (uploader.XferID == xferID) | ||
85 | { | ||
86 | uploader.HandleXferPacket(xferID, packetID, data); | ||
87 | break; | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | public void RequestCreateInventoryItem(IClientAPI remoteClient, LLUUID transactionID, LLUUID folderID, | ||
94 | uint callbackID, string description, string name, sbyte invType, | ||
95 | sbyte type, byte wearableType, uint nextOwnerMask) | ||
96 | { | ||
97 | if (XferUploaders.ContainsKey(transactionID)) | ||
98 | { | ||
99 | XferUploaders[transactionID].RequestCreateInventoryItem(remoteClient, transactionID, folderID, | ||
100 | callbackID, description, name, invType, type, | ||
101 | wearableType, nextOwnerMask); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | public void RequestUpdateInventoryItem(IClientAPI remoteClient, LLUUID transactionID, | ||
106 | InventoryItemBase item) | ||
107 | { | ||
108 | if (XferUploaders.ContainsKey(transactionID)) | ||
109 | { | ||
110 | XferUploaders[transactionID].RequestUpdateInventoryItem(remoteClient, transactionID, item); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | /// <summary> | ||
115 | /// Get an uploaded asset. If the data is successfully retrieved, the transaction will be removed. | ||
116 | /// </summary> | ||
117 | /// <param name="transactionID"></param> | ||
118 | /// <returns>The asset if the upload has completed, null if it has not.</returns> | ||
119 | public AssetBase GetTransactionAsset(LLUUID transactionID) | ||
120 | { | ||
121 | if (XferUploaders.ContainsKey(transactionID)) | ||
122 | { | ||
123 | AssetXferUploader uploader = XferUploaders[transactionID]; | ||
124 | AssetBase asset = uploader.GetAssetData(); | ||
125 | |||
126 | lock (XferUploaders) | ||
127 | { | ||
128 | XferUploaders.Remove(transactionID); | ||
129 | } | ||
130 | |||
131 | return asset; | ||
132 | } | ||
133 | |||
134 | return null; | ||
135 | } | ||
136 | |||
137 | // Nested Types | ||
138 | public class AssetXferUploader | ||
139 | { | ||
140 | // Fields | ||
141 | public bool AddToInventory; | ||
142 | public AssetBase Asset; | ||
143 | public LLUUID InventFolder = LLUUID.Zero; | ||
144 | private IClientAPI ourClient; | ||
145 | public LLUUID TransactionID = LLUUID.Zero; | ||
146 | public bool UploadComplete; | ||
147 | public ulong XferID; | ||
148 | private string m_name = String.Empty; | ||
149 | private string m_description = String.Empty; | ||
150 | private sbyte type = 0; | ||
151 | private sbyte invType = 0; | ||
152 | private byte wearableType = 0; | ||
153 | private uint nextPerm = 0; | ||
154 | private bool m_finished = false; | ||
155 | private bool m_createItem = false; | ||
156 | private AgentAssetTransactions m_userTransactions; | ||
157 | private bool m_storeLocal; | ||
158 | private bool m_dumpAssetToFile; | ||
159 | |||
160 | public AssetXferUploader(AgentAssetTransactions transactions, bool dumpAssetToFile) | ||
161 | { | ||
162 | m_userTransactions = transactions; | ||
163 | m_dumpAssetToFile = dumpAssetToFile; | ||
164 | } | ||
165 | |||
166 | /// <summary> | ||
167 | /// Process transfer data received from the client. | ||
168 | /// </summary> | ||
169 | /// <param name="xferID"></param> | ||
170 | /// <param name="packetID"></param> | ||
171 | /// <param name="data"></param> | ||
172 | /// <returns>True if the transfer is complete, false otherwise or if the xferID was not valid</returns> | ||
173 | public bool HandleXferPacket(ulong xferID, uint packetID, byte[] data) | ||
174 | { | ||
175 | if (XferID == xferID) | ||
176 | { | ||
177 | if (Asset.Data.Length > 1) | ||
178 | { | ||
179 | byte[] destinationArray = new byte[Asset.Data.Length + data.Length]; | ||
180 | Array.Copy(Asset.Data, 0, destinationArray, 0, Asset.Data.Length); | ||
181 | Array.Copy(data, 0, destinationArray, Asset.Data.Length, data.Length); | ||
182 | Asset.Data = destinationArray; | ||
183 | } | ||
184 | else | ||
185 | { | ||
186 | byte[] buffer2 = new byte[data.Length - 4]; | ||
187 | Array.Copy(data, 4, buffer2, 0, data.Length - 4); | ||
188 | Asset.Data = buffer2; | ||
189 | } | ||
190 | ConfirmXferPacketPacket newPack = new ConfirmXferPacketPacket(); | ||
191 | newPack.XferID.ID = xferID; | ||
192 | newPack.XferID.Packet = packetID; | ||
193 | ourClient.OutPacket(newPack, ThrottleOutPacketType.Asset); | ||
194 | if ((packetID & 0x80000000) != 0) | ||
195 | { | ||
196 | SendCompleteMessage(); | ||
197 | return true; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | return false; | ||
202 | } | ||
203 | |||
204 | /// <summary> | ||
205 | /// Initialise asset transfer from the client | ||
206 | /// </summary> | ||
207 | /// <param name="xferID"></param> | ||
208 | /// <param name="packetID"></param> | ||
209 | /// <param name="data"></param> | ||
210 | /// <returns>True if the transfer is complete, false otherwise</returns> | ||
211 | public bool Initialise(IClientAPI remoteClient, LLUUID assetID, LLUUID transaction, sbyte type, byte[] data, | ||
212 | bool storeLocal, bool tempFile) | ||
213 | { | ||
214 | ourClient = remoteClient; | ||
215 | Asset = new AssetBase(); | ||
216 | Asset.FullID = assetID; | ||
217 | Asset.InvType = type; | ||
218 | Asset.Type = type; | ||
219 | Asset.Data = data; | ||
220 | Asset.Name = "blank"; | ||
221 | Asset.Description = "empty"; | ||
222 | Asset.Local = storeLocal; | ||
223 | Asset.Temporary = tempFile; | ||
224 | |||
225 | TransactionID = transaction; | ||
226 | m_storeLocal = storeLocal; | ||
227 | if (Asset.Data.Length > 2) | ||
228 | { | ||
229 | SendCompleteMessage(); | ||
230 | return true; | ||
231 | } | ||
232 | else | ||
233 | { | ||
234 | RequestStartXfer(); | ||
235 | } | ||
236 | |||
237 | return false; | ||
238 | } | ||
239 | |||
240 | protected void RequestStartXfer() | ||
241 | { | ||
242 | UploadComplete = false; | ||
243 | XferID = Util.GetNextXferID(); | ||
244 | RequestXferPacket newPack = new RequestXferPacket(); | ||
245 | newPack.XferID.ID = XferID; | ||
246 | newPack.XferID.VFileType = Asset.Type; | ||
247 | newPack.XferID.VFileID = Asset.FullID; | ||
248 | newPack.XferID.FilePath = 0; | ||
249 | newPack.XferID.Filename = new byte[0]; | ||
250 | ourClient.OutPacket(newPack, ThrottleOutPacketType.Asset); | ||
251 | } | ||
252 | |||
253 | protected void SendCompleteMessage() | ||
254 | { | ||
255 | UploadComplete = true; | ||
256 | AssetUploadCompletePacket newPack = new AssetUploadCompletePacket(); | ||
257 | newPack.AssetBlock.Type = Asset.Type; | ||
258 | newPack.AssetBlock.Success = true; | ||
259 | newPack.AssetBlock.UUID = Asset.FullID; | ||
260 | ourClient.OutPacket(newPack, ThrottleOutPacketType.Asset); | ||
261 | m_finished = true; | ||
262 | if (m_createItem) | ||
263 | { | ||
264 | DoCreateItem(); | ||
265 | } | ||
266 | else if (m_storeLocal) | ||
267 | { | ||
268 | m_userTransactions.Manager.MyScene.CommsManager.AssetCache.AddAsset(Asset); | ||
269 | } | ||
270 | |||
271 | // Console.WriteLine("upload complete "+ this.TransactionID); | ||
272 | |||
273 | if (m_dumpAssetToFile) | ||
274 | { | ||
275 | DateTime now = DateTime.Now; | ||
276 | string filename = | ||
277 | String.Format("{6}_{7}_{0:d2}{1:d2}{2:d2}_{3:d2}{4:d2}{5:d2}.dat", now.Year, now.Month, now.Day, | ||
278 | now.Hour, now.Minute, now.Second, Asset.Name, Asset.Type); | ||
279 | SaveAssetToFile(filename, Asset.Data); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | ///Left this in and commented in case there are unforseen issues | ||
284 | //private void SaveAssetToFile(string filename, byte[] data) | ||
285 | //{ | ||
286 | // FileStream fs = File.Create(filename); | ||
287 | // BinaryWriter bw = new BinaryWriter(fs); | ||
288 | // bw.Write(data); | ||
289 | // bw.Close(); | ||
290 | // fs.Close(); | ||
291 | //} | ||
292 | private void SaveAssetToFile(string filename, byte[] data) | ||
293 | { | ||
294 | string assetPath = "UserAssets"; | ||
295 | if (!Directory.Exists(assetPath)) | ||
296 | { | ||
297 | Directory.CreateDirectory(assetPath); | ||
298 | } | ||
299 | FileStream fs = File.Create(Path.Combine(assetPath, filename)); | ||
300 | BinaryWriter bw = new BinaryWriter(fs); | ||
301 | bw.Write(data); | ||
302 | bw.Close(); | ||
303 | fs.Close(); | ||
304 | } | ||
305 | |||
306 | public void RequestCreateInventoryItem(IClientAPI remoteClient, LLUUID transactionID, LLUUID folderID, | ||
307 | uint callbackID, string description, string name, sbyte invType, | ||
308 | sbyte type, byte wearableType, uint nextOwnerMask) | ||
309 | { | ||
310 | if (TransactionID == transactionID) | ||
311 | { | ||
312 | InventFolder = folderID; | ||
313 | m_name = name; | ||
314 | m_description = description; | ||
315 | this.type = type; | ||
316 | this.invType = invType; | ||
317 | this.wearableType = wearableType; | ||
318 | nextPerm = nextOwnerMask; | ||
319 | Asset.Name = name; | ||
320 | Asset.Description = description; | ||
321 | Asset.Type = type; | ||
322 | Asset.InvType = invType; | ||
323 | m_createItem = true; | ||
324 | if (m_finished) | ||
325 | { | ||
326 | DoCreateItem(); | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | |||
331 | public void RequestUpdateInventoryItem(IClientAPI remoteClient, LLUUID transactionID, | ||
332 | InventoryItemBase item) | ||
333 | { | ||
334 | if (TransactionID == transactionID) | ||
335 | { | ||
336 | CachedUserInfo userInfo = | ||
337 | m_userTransactions.Manager.MyScene.CommsManager.UserProfileCacheService.GetUserDetails( | ||
338 | remoteClient.AgentId); | ||
339 | |||
340 | if (userInfo != null) | ||
341 | { | ||
342 | LLUUID assetID = LLUUID.Combine(transactionID, remoteClient.SecureSessionId); | ||
343 | |||
344 | AssetBase asset | ||
345 | = m_userTransactions.Manager.MyScene.CommsManager.AssetCache.GetAsset( | ||
346 | assetID, (item.AssetType == (int) AssetType.Texture ? true : false)); | ||
347 | |||
348 | if (asset == null) | ||
349 | { | ||
350 | asset = m_userTransactions.GetTransactionAsset(transactionID); | ||
351 | } | ||
352 | |||
353 | if (asset != null && asset.FullID == assetID) | ||
354 | { | ||
355 | asset.Name = item.Name; | ||
356 | asset.Description = item.Description; | ||
357 | asset.InvType = (sbyte) item.InvType; | ||
358 | asset.Type = (sbyte) item.AssetType; | ||
359 | item.AssetID = asset.FullID; | ||
360 | |||
361 | m_userTransactions.Manager.MyScene.CommsManager.AssetCache.AddAsset(Asset); | ||
362 | } | ||
363 | |||
364 | userInfo.UpdateItem(remoteClient.AgentId, item); | ||
365 | } | ||
366 | } | ||
367 | } | ||
368 | |||
369 | private void DoCreateItem() | ||
370 | { | ||
371 | //really need to fix this call, if lbsa71 saw this he would die. | ||
372 | m_userTransactions.Manager.MyScene.CommsManager.AssetCache.AddAsset(Asset); | ||
373 | CachedUserInfo userInfo = | ||
374 | m_userTransactions.Manager.MyScene.CommsManager.UserProfileCacheService.GetUserDetails(ourClient.AgentId); | ||
375 | if (userInfo != null) | ||
376 | { | ||
377 | InventoryItemBase item = new InventoryItemBase(); | ||
378 | item.Owner = ourClient.AgentId; | ||
379 | item.Creator = ourClient.AgentId; | ||
380 | item.ID = LLUUID.Random(); | ||
381 | item.AssetID = Asset.FullID; | ||
382 | item.Description = m_description; | ||
383 | item.Name = m_name; | ||
384 | item.AssetType = type; | ||
385 | item.InvType = invType; | ||
386 | item.Folder = InventFolder; | ||
387 | item.BasePermissions = 2147483647; | ||
388 | item.CurrentPermissions = 2147483647; | ||
389 | item.NextPermissions = nextPerm; | ||
390 | item.Flags = (uint)wearableType; | ||
391 | |||
392 | userInfo.AddItem(ourClient.AgentId, item); | ||
393 | ourClient.SendInventoryItemCreateUpdate(item); | ||
394 | } | ||
395 | } | ||
396 | |||
397 | public AssetBase GetAssetData() | ||
398 | { | ||
399 | if (m_finished) | ||
400 | { | ||
401 | return Asset; | ||
402 | } | ||
403 | return null; | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/Environment/Modules/Agent/AssetTransaction/AssetTransactionModule.cs b/OpenSim/Region/Environment/Modules/Agent/AssetTransaction/AssetTransactionModule.cs new file mode 100644 index 0000000..ef81625 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Agent/AssetTransaction/AssetTransactionModule.cs | |||
@@ -0,0 +1,286 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using libsecondlife; | ||
32 | using log4net; | ||
33 | using Nini.Config; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Region.Environment.Interfaces; | ||
36 | using OpenSim.Region.Environment.Modules.Agent.AssetTransaction; | ||
37 | using OpenSim.Region.Environment.Scenes; | ||
38 | |||
39 | namespace OpenSim.Region.Environment.Modules.Agent.AssetTransaction | ||
40 | { | ||
41 | public class AssetTransactionModule : IRegionModule, IAgentAssetTransactions | ||
42 | { | ||
43 | private readonly Dictionary<LLUUID, Scene> RegisteredScenes = new Dictionary<LLUUID, Scene>(); | ||
44 | private Scene m_scene = null; | ||
45 | private bool m_dumpAssetsToFile = false; | ||
46 | |||
47 | private AgentAssetTransactionsManager m_transactionManager; | ||
48 | |||
49 | public AssetTransactionModule() | ||
50 | { | ||
51 | // System.Console.WriteLine("creating AgentAssetTransactionModule"); | ||
52 | } | ||
53 | |||
54 | public void Initialise(Scene scene, IConfigSource config) | ||
55 | { | ||
56 | if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID)) | ||
57 | { | ||
58 | // System.Console.WriteLine("initialising AgentAssetTransactionModule"); | ||
59 | RegisteredScenes.Add(scene.RegionInfo.RegionID, scene); | ||
60 | scene.RegisterModuleInterface<IAgentAssetTransactions>(this); | ||
61 | |||
62 | scene.EventManager.OnNewClient += NewClient; | ||
63 | } | ||
64 | |||
65 | if (m_scene == null) | ||
66 | { | ||
67 | m_scene = scene; | ||
68 | if (config.Configs["StandAlone"] != null) | ||
69 | { | ||
70 | try | ||
71 | { | ||
72 | m_dumpAssetsToFile = config.Configs["StandAlone"].GetBoolean("dump_assets_to_file", false); | ||
73 | m_transactionManager = new AgentAssetTransactionsManager(m_scene, m_dumpAssetsToFile); | ||
74 | } | ||
75 | catch (Exception) | ||
76 | { | ||
77 | m_transactionManager = new AgentAssetTransactionsManager(m_scene, false); | ||
78 | } | ||
79 | } | ||
80 | else | ||
81 | { | ||
82 | m_transactionManager = new AgentAssetTransactionsManager(m_scene, false); | ||
83 | } | ||
84 | |||
85 | } | ||
86 | } | ||
87 | |||
88 | public void PostInitialise() | ||
89 | { | ||
90 | |||
91 | } | ||
92 | |||
93 | public void Close() | ||
94 | { | ||
95 | } | ||
96 | |||
97 | public string Name | ||
98 | { | ||
99 | get { return "AgentTransactionModule"; } | ||
100 | } | ||
101 | |||
102 | public bool IsSharedModule | ||
103 | { | ||
104 | get { return true; } | ||
105 | } | ||
106 | |||
107 | public void NewClient(IClientAPI client) | ||
108 | { | ||
109 | client.OnAssetUploadRequest += m_transactionManager.HandleUDPUploadRequest; | ||
110 | client.OnXferReceive += m_transactionManager.HandleXfer; | ||
111 | } | ||
112 | |||
113 | public void HandleItemCreationFromTransaction(IClientAPI remoteClient, LLUUID transactionID, LLUUID folderID, | ||
114 | uint callbackID, string description, string name, sbyte invType, | ||
115 | sbyte type, byte wearableType, uint nextOwnerMask) | ||
116 | { | ||
117 | m_transactionManager.HandleItemCreationFromTransaction(remoteClient, transactionID, folderID, callbackID, description, name, invType, type, wearableType, nextOwnerMask); | ||
118 | } | ||
119 | |||
120 | public void HandleItemUpdateFromTransaction(IClientAPI remoteClient, LLUUID transactionID, | ||
121 | InventoryItemBase item) | ||
122 | { | ||
123 | m_transactionManager.HandleItemUpdateFromTransaction(remoteClient, transactionID, item); | ||
124 | } | ||
125 | |||
126 | public void RemoveAgentAssetTransactions(LLUUID userID) | ||
127 | { | ||
128 | m_transactionManager.RemoveAgentAssetTransactions(userID); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | public class AgentAssetTransactionsManager | ||
133 | { | ||
134 | private static readonly ILog m_log | ||
135 | = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
136 | |||
137 | // Fields | ||
138 | public Scene MyScene; | ||
139 | |||
140 | /// <summary> | ||
141 | /// Each agent has its own singleton collection of transactions | ||
142 | /// </summary> | ||
143 | private Dictionary<LLUUID, AgentAssetTransactions> AgentTransactions = | ||
144 | new Dictionary<LLUUID, AgentAssetTransactions>(); | ||
145 | |||
146 | /// <summary> | ||
147 | /// Should we dump uploaded assets to the filesystem? | ||
148 | /// </summary> | ||
149 | private bool m_dumpAssetsToFile; | ||
150 | |||
151 | public AgentAssetTransactionsManager(Scene scene, bool dumpAssetsToFile) | ||
152 | { | ||
153 | MyScene = scene; | ||
154 | m_dumpAssetsToFile = dumpAssetsToFile; | ||
155 | } | ||
156 | |||
157 | /// <summary> | ||
158 | /// Get the collection of asset transactions for the given user. If one does not already exist, it | ||
159 | /// is created. | ||
160 | /// </summary> | ||
161 | /// <param name="userID"></param> | ||
162 | /// <returns></returns> | ||
163 | private AgentAssetTransactions GetUserTransactions(LLUUID userID) | ||
164 | { | ||
165 | lock (AgentTransactions) | ||
166 | { | ||
167 | if (!AgentTransactions.ContainsKey(userID)) | ||
168 | { | ||
169 | AgentAssetTransactions transactions | ||
170 | = new AgentAssetTransactions(userID, this, m_dumpAssetsToFile); | ||
171 | AgentTransactions.Add(userID, transactions); | ||
172 | } | ||
173 | |||
174 | return AgentTransactions[userID]; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | /// <summary> | ||
179 | /// Remove the given agent asset transactions. This should be called when a client is departing | ||
180 | /// from a scene (and hence won't be making any more transactions here). | ||
181 | /// </summary> | ||
182 | /// <param name="userID"></param> | ||
183 | public void RemoveAgentAssetTransactions(LLUUID userID) | ||
184 | { | ||
185 | // m_log.DebugFormat("Removing agent asset transactions structure for agent {0}", userID); | ||
186 | |||
187 | lock (AgentTransactions) | ||
188 | { | ||
189 | AgentTransactions.Remove(userID); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | /// <summary> | ||
194 | /// Create an inventory item from data that has been received through a transaction. | ||
195 | /// | ||
196 | /// This is called when new clothing or body parts are created. It may also be called in other | ||
197 | /// situations. | ||
198 | /// </summary> | ||
199 | /// <param name="remoteClient"></param> | ||
200 | /// <param name="transactionID"></param> | ||
201 | /// <param name="folderID"></param> | ||
202 | /// <param name="callbackID"></param> | ||
203 | /// <param name="description"></param> | ||
204 | /// <param name="name"></param> | ||
205 | /// <param name="invType"></param> | ||
206 | /// <param name="type"></param> | ||
207 | /// <param name="wearableType"></param> | ||
208 | /// <param name="nextOwnerMask"></param> | ||
209 | public void HandleItemCreationFromTransaction(IClientAPI remoteClient, LLUUID transactionID, LLUUID folderID, | ||
210 | uint callbackID, string description, string name, sbyte invType, | ||
211 | sbyte type, byte wearableType, uint nextOwnerMask) | ||
212 | { | ||
213 | m_log.DebugFormat( | ||
214 | "[TRANSACTIONS MANAGER] Called HandleItemCreationFromTransaction with item {0}", name); | ||
215 | |||
216 | AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId); | ||
217 | |||
218 | transactions.RequestCreateInventoryItem( | ||
219 | remoteClient, transactionID, folderID, callbackID, description, | ||
220 | name, invType, type, wearableType, nextOwnerMask); | ||
221 | } | ||
222 | |||
223 | /// <summary> | ||
224 | /// Update an inventory item with data that has been received through a transaction. | ||
225 | /// | ||
226 | /// This is called when clothing or body parts are updated (for instance, with new textures or | ||
227 | /// colours). It may also be called in other situations. | ||
228 | /// </summary> | ||
229 | /// <param name="remoteClient"></param> | ||
230 | /// <param name="transactionID"></param> | ||
231 | /// <param name="item"></param> | ||
232 | public void HandleItemUpdateFromTransaction(IClientAPI remoteClient, LLUUID transactionID, | ||
233 | InventoryItemBase item) | ||
234 | { | ||
235 | m_log.DebugFormat( | ||
236 | "[TRANSACTIONS MANAGER] Called HandleItemUpdateFromTransaction with item {0}", | ||
237 | item.Name); | ||
238 | |||
239 | AgentAssetTransactions transactions | ||
240 | = GetUserTransactions(remoteClient.AgentId); | ||
241 | |||
242 | transactions.RequestUpdateInventoryItem(remoteClient, transactionID, item); | ||
243 | } | ||
244 | |||
245 | /// <summary> | ||
246 | /// Request that a client (agent) begin an asset transfer. | ||
247 | /// </summary> | ||
248 | /// <param name="remoteClient"></param> | ||
249 | /// <param name="assetID"></param> | ||
250 | /// <param name="transaction"></param> | ||
251 | /// <param name="type"></param> | ||
252 | /// <param name="data"></param></param> | ||
253 | /// <param name="tempFile"></param> | ||
254 | public void HandleUDPUploadRequest(IClientAPI remoteClient, LLUUID assetID, LLUUID transaction, sbyte type, | ||
255 | byte[] data, bool storeLocal, bool tempFile) | ||
256 | { | ||
257 | // Console.WriteLine("asset upload of " + assetID); | ||
258 | AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId); | ||
259 | |||
260 | AgentAssetTransactions.AssetXferUploader uploader = transactions.RequestXferUploader(transaction); | ||
261 | if (uploader != null) | ||
262 | { | ||
263 | |||
264 | if (uploader.Initialise(remoteClient, assetID, transaction, type, data, storeLocal, tempFile)) | ||
265 | { | ||
266 | |||
267 | } | ||
268 | } | ||
269 | } | ||
270 | |||
271 | /// <summary> | ||
272 | /// Handle asset transfer data packets received in response to the asset upload request in | ||
273 | /// HandleUDPUploadRequest() | ||
274 | /// </summary> | ||
275 | /// <param name="remoteClient"></param> | ||
276 | /// <param name="xferID"></param> | ||
277 | /// <param name="packetID"></param> | ||
278 | /// <param name="data"></param> | ||
279 | public void HandleXfer(IClientAPI remoteClient, ulong xferID, uint packetID, byte[] data) | ||
280 | { | ||
281 | AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId); | ||
282 | |||
283 | transactions.HandleXfer(xferID, packetID, data); | ||
284 | } | ||
285 | } | ||
286 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/Environment/Modules/Agent/TextureDownload/TextureDownloadModule.cs b/OpenSim/Region/Environment/Modules/Agent/TextureDownload/TextureDownloadModule.cs new file mode 100644 index 0000000..b8899d2 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Agent/TextureDownload/TextureDownloadModule.cs | |||
@@ -0,0 +1,215 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Threading; | ||
31 | using libsecondlife; | ||
32 | using Nini.Config; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Region.Environment.Interfaces; | ||
35 | using OpenSim.Region.Environment.Scenes; | ||
36 | |||
37 | namespace OpenSim.Region.Environment.Modules.Agent.TextureDownload | ||
38 | { | ||
39 | public class TextureDownloadModule : IRegionModule | ||
40 | { | ||
41 | //private static readonly log4net.ILog m_log | ||
42 | // = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||
43 | |||
44 | private Scene m_scene; | ||
45 | private List<Scene> m_scenes = new List<Scene>(); | ||
46 | |||
47 | /// <summary> | ||
48 | /// There is one queue for all textures waiting to be sent, regardless of the requesting user. | ||
49 | /// </summary> | ||
50 | private readonly BlockingQueue<ITextureSender> m_queueSenders | ||
51 | = new BlockingQueue<ITextureSender>(); | ||
52 | |||
53 | /// <summary> | ||
54 | /// Each user has their own texture download service. | ||
55 | /// </summary> | ||
56 | private readonly Dictionary<LLUUID, UserTextureDownloadService> m_userTextureServices = | ||
57 | new Dictionary<LLUUID, UserTextureDownloadService>(); | ||
58 | |||
59 | private Thread m_thread; | ||
60 | |||
61 | public TextureDownloadModule() | ||
62 | { | ||
63 | } | ||
64 | |||
65 | public void Initialise(Scene scene, IConfigSource config) | ||
66 | { | ||
67 | if (m_scene == null) | ||
68 | { | ||
69 | //Console.WriteLine("Creating Texture download module"); | ||
70 | m_thread = new Thread(new ThreadStart(ProcessTextureSenders)); | ||
71 | m_thread.Name = "ProcessTextureSenderThread"; | ||
72 | m_thread.IsBackground = true; | ||
73 | m_thread.Start(); | ||
74 | ThreadTracker.Add(m_thread); | ||
75 | } | ||
76 | |||
77 | if (!m_scenes.Contains(scene)) | ||
78 | { | ||
79 | m_scenes.Add(scene); | ||
80 | m_scene = scene; | ||
81 | m_scene.EventManager.OnNewClient += NewClient; | ||
82 | m_scene.EventManager.OnRemovePresence += EventManager_OnRemovePresence; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | /// <summary> | ||
87 | /// Cleanup the texture service related objects for the removed presence. | ||
88 | /// </summary> | ||
89 | /// <param name="agentId"> </param> | ||
90 | private void EventManager_OnRemovePresence(LLUUID agentId) | ||
91 | { | ||
92 | UserTextureDownloadService textureService; | ||
93 | |||
94 | lock (m_userTextureServices) | ||
95 | { | ||
96 | if (m_userTextureServices.TryGetValue(agentId, out textureService)) | ||
97 | { | ||
98 | textureService.Close(); | ||
99 | |||
100 | m_userTextureServices.Remove(agentId); | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | public void PostInitialise() | ||
106 | { | ||
107 | } | ||
108 | |||
109 | public void Close() | ||
110 | { | ||
111 | } | ||
112 | |||
113 | public string Name | ||
114 | { | ||
115 | get { return "TextureDownloadModule"; } | ||
116 | } | ||
117 | |||
118 | public bool IsSharedModule | ||
119 | { | ||
120 | get { return false; } | ||
121 | } | ||
122 | |||
123 | public void NewClient(IClientAPI client) | ||
124 | { | ||
125 | client.OnRequestTexture += TextureRequest; | ||
126 | } | ||
127 | |||
128 | /// <summary> | ||
129 | /// Does this user have a registered texture download service? | ||
130 | /// </summary> | ||
131 | /// <param name="userID"></param> | ||
132 | /// <param name="textureService"></param> | ||
133 | /// <returns>Always returns true, since a service is created if one does not already exist</returns> | ||
134 | private bool TryGetUserTextureService( | ||
135 | IClientAPI client, out UserTextureDownloadService textureService) | ||
136 | { | ||
137 | lock (m_userTextureServices) | ||
138 | { | ||
139 | if (m_userTextureServices.TryGetValue(client.AgentId, out textureService)) | ||
140 | { | ||
141 | return true; | ||
142 | } | ||
143 | |||
144 | textureService = new UserTextureDownloadService(client, m_scene, m_queueSenders); | ||
145 | m_userTextureServices.Add(client.AgentId, textureService); | ||
146 | |||
147 | return true; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | /// <summary> | ||
152 | /// Start the process of requesting a given texture. | ||
153 | /// </summary> | ||
154 | /// <param name="sender"> </param> | ||
155 | /// <param name="e"></param> | ||
156 | public void TextureRequest(Object sender, TextureRequestArgs e) | ||
157 | { | ||
158 | IClientAPI client = (IClientAPI) sender; | ||
159 | UserTextureDownloadService textureService; | ||
160 | |||
161 | if (TryGetUserTextureService(client, out textureService)) | ||
162 | { | ||
163 | textureService.HandleTextureRequest(e); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | /// <summary> | ||
168 | /// Entry point for the thread dedicated to processing the texture queue. | ||
169 | /// </summary> | ||
170 | public void ProcessTextureSenders() | ||
171 | { | ||
172 | ITextureSender sender = null; | ||
173 | |||
174 | while (true) | ||
175 | { | ||
176 | sender = m_queueSenders.Dequeue(); | ||
177 | |||
178 | if (sender.Cancel) | ||
179 | { | ||
180 | TextureSent(sender); | ||
181 | |||
182 | sender.Cancel = false; | ||
183 | } | ||
184 | else | ||
185 | { | ||
186 | bool finished = sender.SendTexturePacket(); | ||
187 | if (finished) | ||
188 | { | ||
189 | TextureSent(sender); | ||
190 | } | ||
191 | else | ||
192 | { | ||
193 | m_queueSenders.Enqueue(sender); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | // Make sure that any sender we currently have can get garbage collected | ||
198 | sender = null; | ||
199 | |||
200 | //m_log.InfoFormat("[TEXTURE DOWNLOAD] Texture sender queue size: {0}", m_queueSenders.Count()); | ||
201 | } | ||
202 | } | ||
203 | |||
204 | /// <summary> | ||
205 | /// Called when the texture has finished sending. | ||
206 | /// </summary> | ||
207 | /// <param name="sender"></param> | ||
208 | private void TextureSent(ITextureSender sender) | ||
209 | { | ||
210 | sender.Sending = false; | ||
211 | //m_log.DebugFormat("[TEXTURE DOWNLOAD]: Removing download stat for {0}", sender.assetID); | ||
212 | m_scene.AddPendingDownloads(-1); | ||
213 | } | ||
214 | } | ||
215 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/Environment/Modules/Agent/TextureDownload/TextureNotFoundSender.cs b/OpenSim/Region/Environment/Modules/Agent/TextureDownload/TextureNotFoundSender.cs new file mode 100644 index 0000000..6f11f73 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Agent/TextureDownload/TextureNotFoundSender.cs | |||
@@ -0,0 +1,93 @@ | |||
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 | |||
28 | using libsecondlife; | ||
29 | using libsecondlife.Packets; | ||
30 | using OpenSim.Framework; | ||
31 | using OpenSim.Region.Environment.Interfaces; | ||
32 | |||
33 | namespace OpenSim.Region.Environment.Modules.Agent.TextureDownload | ||
34 | { | ||
35 | /// <summary> | ||
36 | /// Sends a 'texture not found' packet back to the client | ||
37 | /// </summary> | ||
38 | public class TextureNotFoundSender : ITextureSender | ||
39 | { | ||
40 | //private static readonly log4net.ILog m_log | ||
41 | // = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||
42 | |||
43 | private LLUUID m_textureId; | ||
44 | private IClientAPI m_client; | ||
45 | |||
46 | // See ITextureSender | ||
47 | public bool Sending | ||
48 | { | ||
49 | get { return false; } | ||
50 | set { m_sending = value; } | ||
51 | } | ||
52 | |||
53 | private bool m_sending = false; | ||
54 | |||
55 | // See ITextureSender | ||
56 | public bool Cancel | ||
57 | { | ||
58 | get { return false; } | ||
59 | set { m_cancel = value; } | ||
60 | } | ||
61 | |||
62 | private bool m_cancel = false; | ||
63 | |||
64 | public TextureNotFoundSender(IClientAPI client, LLUUID textureID) | ||
65 | { | ||
66 | m_client = client; | ||
67 | m_textureId = textureID; | ||
68 | } | ||
69 | |||
70 | // See ITextureSender | ||
71 | public void UpdateRequest(int discardLevel, uint packetNumber) | ||
72 | { | ||
73 | // Not need to implement since priority changes don't affect this operation | ||
74 | } | ||
75 | |||
76 | // See ITextureSender | ||
77 | public bool SendTexturePacket() | ||
78 | { | ||
79 | //m_log.InfoFormat( | ||
80 | // "[TEXTURE NOT FOUND SENDER]: Informing the client that texture {0} cannot be found", | ||
81 | // m_textureId); | ||
82 | |||
83 | ImageNotInDatabasePacket notFound = new ImageNotInDatabasePacket(); | ||
84 | notFound.ImageID.ID = m_textureId; | ||
85 | |||
86 | // XXX Temporarily disabling as this appears to be causing client crashes on at least | ||
87 | // 1.19.0(5) of the Linden Second Life client. | ||
88 | // m_client.OutPacket(notFound, ThrottleOutPacketType.Texture); | ||
89 | |||
90 | return true; | ||
91 | } | ||
92 | } | ||
93 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/Environment/Modules/Agent/TextureDownload/UserTextureDownloadService.cs b/OpenSim/Region/Environment/Modules/Agent/TextureDownload/UserTextureDownloadService.cs new file mode 100644 index 0000000..08a22d6 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Agent/TextureDownload/UserTextureDownloadService.cs | |||
@@ -0,0 +1,252 @@ | |||
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 | |||
28 | using System.Collections.Generic; | ||
29 | using System.Reflection; | ||
30 | using libsecondlife; | ||
31 | using log4net; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Framework.Communications.Limit; | ||
34 | using OpenSim.Region.Environment.Interfaces; | ||
35 | using OpenSim.Region.Environment.Modules.Agent.TextureDownload; | ||
36 | using OpenSim.Region.Environment.Modules.Agent.TextureSender; | ||
37 | using OpenSim.Region.Environment.Scenes; | ||
38 | |||
39 | namespace OpenSim.Region.Environment.Modules.Agent.TextureDownload | ||
40 | { | ||
41 | /// <summary> | ||
42 | /// This module sets up texture senders in response to client texture requests, and places them on a | ||
43 | /// processing queue once those senders have the appropriate data (i.e. a texture retrieved from the | ||
44 | /// asset cache). | ||
45 | /// </summary> | ||
46 | public class UserTextureDownloadService | ||
47 | { | ||
48 | private static readonly ILog m_log | ||
49 | = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
50 | |||
51 | /// <summary> | ||
52 | /// We will allow the client to request the same texture n times before dropping further requests | ||
53 | /// | ||
54 | /// This number includes repeated requests for the same texture at different resolutions (which we don't | ||
55 | /// currently handle properly as far as I know). However, this situation should be handled in a more | ||
56 | /// sophisticated way. | ||
57 | /// </summary> | ||
58 | private static readonly int MAX_ALLOWED_TEXTURE_REQUESTS = 5; | ||
59 | |||
60 | /// <summary> | ||
61 | /// We're going to limit requests for the same missing texture. | ||
62 | /// XXX This is really a temporary solution to deal with the situation where a client continually requests | ||
63 | /// the same missing textures | ||
64 | /// </summary> | ||
65 | private readonly IRequestLimitStrategy<LLUUID> missingTextureLimitStrategy | ||
66 | = new RepeatLimitStrategy<LLUUID>(MAX_ALLOWED_TEXTURE_REQUESTS); | ||
67 | |||
68 | /// <summary> | ||
69 | /// XXX Also going to limit requests for found textures. | ||
70 | /// </summary> | ||
71 | private readonly IRequestLimitStrategy<LLUUID> foundTextureLimitStrategy | ||
72 | = new RepeatLimitStrategy<LLUUID>(MAX_ALLOWED_TEXTURE_REQUESTS); | ||
73 | |||
74 | /// <summary> | ||
75 | /// Holds texture senders before they have received the appropriate texture from the asset cache. | ||
76 | /// </summary> | ||
77 | private readonly Dictionary<LLUUID, TextureSender.TextureSender> m_textureSenders = new Dictionary<LLUUID, TextureSender.TextureSender>(); | ||
78 | |||
79 | /// <summary> | ||
80 | /// Texture Senders are placed in this queue once they have received their texture from the asset | ||
81 | /// cache. Another module actually invokes the send. | ||
82 | /// </summary> | ||
83 | private readonly BlockingQueue<ITextureSender> m_sharedSendersQueue; | ||
84 | |||
85 | private readonly Scene m_scene; | ||
86 | |||
87 | private readonly IClientAPI m_client; | ||
88 | |||
89 | public UserTextureDownloadService( | ||
90 | IClientAPI client, Scene scene, BlockingQueue<ITextureSender> sharedQueue) | ||
91 | { | ||
92 | m_client = client; | ||
93 | m_scene = scene; | ||
94 | m_sharedSendersQueue = sharedQueue; | ||
95 | } | ||
96 | |||
97 | /// <summary> | ||
98 | /// Handle a texture request. This involves creating a texture sender and placing it on the | ||
99 | /// previously passed in shared queue. | ||
100 | /// </summary> | ||
101 | /// <param name="e"></param> | ||
102 | public void HandleTextureRequest(TextureRequestArgs e) | ||
103 | { | ||
104 | TextureSender.TextureSender textureSender; | ||
105 | |||
106 | //TODO: should be working out the data size/ number of packets to be sent for each discard level | ||
107 | if ((e.DiscardLevel >= 0) || (e.Priority != 0)) | ||
108 | { | ||
109 | lock (m_textureSenders) | ||
110 | { | ||
111 | if (m_textureSenders.TryGetValue(e.RequestedAssetID, out textureSender)) | ||
112 | { | ||
113 | // If we've received new non UUID information for this request and it hasn't dispatched | ||
114 | // yet, then update the request accordingly. | ||
115 | textureSender.UpdateRequest(e.DiscardLevel, e.PacketNumber); | ||
116 | } | ||
117 | else | ||
118 | { | ||
119 | if (!foundTextureLimitStrategy.AllowRequest(e.RequestedAssetID)) | ||
120 | { | ||
121 | // m_log.DebugFormat( | ||
122 | // "[USER TEXTURE DOWNLOAD SERVICE]: Refusing request for {0} from client {1}", | ||
123 | // e.RequestedAssetID, m_client.AgentId); | ||
124 | |||
125 | return; | ||
126 | } | ||
127 | else if (!missingTextureLimitStrategy.AllowRequest(e.RequestedAssetID)) | ||
128 | { | ||
129 | if (missingTextureLimitStrategy.IsFirstRefusal(e.RequestedAssetID)) | ||
130 | { | ||
131 | // Commenting out this message for now as it causes too much noise with other | ||
132 | // debug messages. | ||
133 | // TODO: possibly record this as a statistic in the future | ||
134 | // | ||
135 | // m_log.DebugFormat( | ||
136 | // "[USER TEXTURE DOWNLOAD SERVICE]: Dropping requests for notified missing texture {0} for client {1} since we have received more than {2} requests", | ||
137 | // e.RequestedAssetID, m_client.AgentId, MAX_ALLOWED_TEXTURE_REQUESTS); | ||
138 | } | ||
139 | |||
140 | return; | ||
141 | } | ||
142 | |||
143 | m_scene.AddPendingDownloads(1); | ||
144 | |||
145 | TextureSender.TextureSender requestHandler = new TextureSender.TextureSender(m_client, e.DiscardLevel, e.PacketNumber); | ||
146 | m_textureSenders.Add(e.RequestedAssetID, requestHandler); | ||
147 | |||
148 | m_scene.AssetCache.GetAsset(e.RequestedAssetID, TextureCallback, true); | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | else | ||
153 | { | ||
154 | lock (m_textureSenders) | ||
155 | { | ||
156 | if (m_textureSenders.TryGetValue(e.RequestedAssetID, out textureSender)) | ||
157 | { | ||
158 | textureSender.Cancel = true; | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | |||
164 | /// <summary> | ||
165 | /// The callback for the asset cache when a texture has been retrieved. This method queues the | ||
166 | /// texture sender for processing. | ||
167 | /// </summary> | ||
168 | /// <param name="textureID"></param> | ||
169 | /// <param name="texture"></param> | ||
170 | public void TextureCallback(LLUUID textureID, AssetBase texture) | ||
171 | { | ||
172 | //m_log.DebugFormat("[USER TEXTURE DOWNLOAD SERVICE]: Calling TextureCallback with {0}, texture == null is {1}", textureID, (texture == null ? true : false)); | ||
173 | |||
174 | lock (m_textureSenders) | ||
175 | { | ||
176 | TextureSender.TextureSender textureSender; | ||
177 | |||
178 | if (m_textureSenders.TryGetValue(textureID, out textureSender)) | ||
179 | { | ||
180 | // XXX It may be perfectly valid for a texture to have no data... but if we pass | ||
181 | // this on to the TextureSender it will blow up, so just discard for now. | ||
182 | // Needs investigation. | ||
183 | if (texture == null || texture.Data == null) | ||
184 | { | ||
185 | if (!missingTextureLimitStrategy.IsMonitoringRequests(textureID)) | ||
186 | { | ||
187 | missingTextureLimitStrategy.MonitorRequests(textureID); | ||
188 | |||
189 | m_log.DebugFormat( | ||
190 | "[USER TEXTURE DOWNLOAD SERVICE]: Queueing first TextureNotFoundSender for {0}, client {1}", | ||
191 | textureID, m_client.AgentId); | ||
192 | } | ||
193 | |||
194 | ITextureSender textureNotFoundSender = new TextureNotFoundSender(m_client, textureID); | ||
195 | EnqueueTextureSender(textureNotFoundSender); | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | if (!textureSender.ImageLoaded) | ||
200 | { | ||
201 | textureSender.TextureReceived(texture); | ||
202 | EnqueueTextureSender(textureSender); | ||
203 | |||
204 | foundTextureLimitStrategy.MonitorRequests(textureID); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | //m_log.InfoFormat("[TEXTURE SENDER] Removing texture sender with uuid {0}", textureID); | ||
209 | m_textureSenders.Remove(textureID); | ||
210 | //m_log.InfoFormat("[TEXTURE SENDER] Current texture senders in dictionary: {0}", m_textureSenders.Count); | ||
211 | } | ||
212 | else | ||
213 | { | ||
214 | m_log.WarnFormat( | ||
215 | "Got a texture uuid {0} with no sender object to handle it, this shouldn't happen", | ||
216 | textureID); | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | /// <summary> | ||
222 | /// Place a ready texture sender on the processing queue. | ||
223 | /// </summary> | ||
224 | /// <param name="textureSender"></param> | ||
225 | private void EnqueueTextureSender(ITextureSender textureSender) | ||
226 | { | ||
227 | textureSender.Cancel = false; | ||
228 | textureSender.Sending = true; | ||
229 | |||
230 | if (!m_sharedSendersQueue.Contains(textureSender)) | ||
231 | { | ||
232 | m_sharedSendersQueue.Enqueue(textureSender); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | /// <summary> | ||
237 | /// Close this module. | ||
238 | /// </summary> | ||
239 | internal void Close() | ||
240 | { | ||
241 | lock (m_textureSenders) | ||
242 | { | ||
243 | foreach( TextureSender.TextureSender textureSender in m_textureSenders.Values ) | ||
244 | { | ||
245 | textureSender.Cancel = true; | ||
246 | } | ||
247 | |||
248 | m_textureSenders.Clear(); | ||
249 | } | ||
250 | } | ||
251 | } | ||
252 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/Environment/Modules/Agent/TextureSender/TextureSender.cs b/OpenSim/Region/Environment/Modules/Agent/TextureSender/TextureSender.cs new file mode 100644 index 0000000..37b0652 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Agent/TextureSender/TextureSender.cs | |||
@@ -0,0 +1,220 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Reflection; | ||
30 | using libsecondlife.Packets; | ||
31 | using log4net; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.Environment.Interfaces; | ||
34 | |||
35 | namespace OpenSim.Region.Environment.Modules.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 | /// <summary> | ||
52 | /// Holds the texture asset to send. | ||
53 | /// </summary> | ||
54 | private AssetBase m_asset; | ||
55 | |||
56 | //public LLUUID assetID { get { return m_asset.FullID; } } | ||
57 | |||
58 | /// <summary> | ||
59 | /// This is actually the number of extra packets required to send the texture data! We always assume | ||
60 | /// at least one is required. | ||
61 | /// </summary> | ||
62 | private int NumPackets = 0; | ||
63 | |||
64 | /// <summary> | ||
65 | /// Holds the packet number to send next. In this case, each packet is 1000 bytes long and starts | ||
66 | /// at the 600th byte (0th indexed). | ||
67 | /// </summary> | ||
68 | private int PacketCounter = 0; | ||
69 | |||
70 | // See ITextureSender | ||
71 | public bool Cancel | ||
72 | { | ||
73 | get { return false; } | ||
74 | set { m_cancel = value; } | ||
75 | } | ||
76 | |||
77 | private bool m_cancel = false; | ||
78 | |||
79 | // See ITextureSender | ||
80 | public bool Sending | ||
81 | { | ||
82 | get { return false; } | ||
83 | set { m_sending = value; } | ||
84 | } | ||
85 | |||
86 | private bool m_sending = false; | ||
87 | |||
88 | public bool ImageLoaded = false; | ||
89 | |||
90 | private IClientAPI RequestUser; | ||
91 | |||
92 | private int RequestedDiscardLevel = -1; | ||
93 | private uint StartPacketNumber = 0; | ||
94 | |||
95 | public TextureSender(IClientAPI client, int discardLevel, uint packetNumber) | ||
96 | { | ||
97 | RequestUser = client; | ||
98 | RequestedDiscardLevel = discardLevel; | ||
99 | StartPacketNumber = packetNumber; | ||
100 | } | ||
101 | |||
102 | /// <summary> | ||
103 | /// Load up the texture data to send. | ||
104 | /// </summary> | ||
105 | /// <param name="asset"> | ||
106 | /// A <see cref="AssetBase"/> | ||
107 | /// </param> | ||
108 | public void TextureReceived(AssetBase asset) | ||
109 | { | ||
110 | m_asset = asset; | ||
111 | NumPackets = CalculateNumPackets(asset.Data.Length); | ||
112 | PacketCounter = (int) StartPacketNumber; | ||
113 | ImageLoaded = true; | ||
114 | } | ||
115 | |||
116 | // See ITextureSender | ||
117 | public void UpdateRequest(int discardLevel, uint packetNumber) | ||
118 | { | ||
119 | RequestedDiscardLevel = discardLevel; | ||
120 | StartPacketNumber = packetNumber; | ||
121 | PacketCounter = (int) StartPacketNumber; | ||
122 | } | ||
123 | |||
124 | // See ITextureSender | ||
125 | public bool SendTexturePacket() | ||
126 | { | ||
127 | //m_log.DebugFormat("[TEXTURE SENDER]: Sending packet for {0}", m_asset.FullID); | ||
128 | |||
129 | SendPacket(); | ||
130 | counter++; | ||
131 | if ((NumPackets == 0) || (RequestedDiscardLevel == -1) || (PacketCounter > NumPackets) || | ||
132 | ((RequestedDiscardLevel > 0) && (counter > 50 + (NumPackets/(RequestedDiscardLevel + 1))))) | ||
133 | { | ||
134 | return true; | ||
135 | } | ||
136 | return false; | ||
137 | } | ||
138 | |||
139 | /// <summary> | ||
140 | /// Sends a texture packet to the client. | ||
141 | /// </summary> | ||
142 | private void SendPacket() | ||
143 | { | ||
144 | if (PacketCounter <= NumPackets) | ||
145 | { | ||
146 | if (PacketCounter == 0) | ||
147 | { | ||
148 | if (NumPackets == 0) | ||
149 | { | ||
150 | ImageDataPacket im = new ImageDataPacket(); | ||
151 | im.Header.Reliable = false; | ||
152 | im.ImageID.Packets = 1; | ||
153 | im.ImageID.ID = m_asset.FullID; | ||
154 | im.ImageID.Size = (uint) m_asset.Data.Length; | ||
155 | im.ImageData.Data = m_asset.Data; | ||
156 | im.ImageID.Codec = 2; | ||
157 | RequestUser.OutPacket(im, ThrottleOutPacketType.Texture); | ||
158 | PacketCounter++; | ||
159 | } | ||
160 | else | ||
161 | { | ||
162 | ImageDataPacket im = new ImageDataPacket(); | ||
163 | im.Header.Reliable = false; | ||
164 | im.ImageID.Packets = (ushort) (NumPackets); | ||
165 | im.ImageID.ID = m_asset.FullID; | ||
166 | im.ImageID.Size = (uint) m_asset.Data.Length; | ||
167 | im.ImageData.Data = new byte[600]; | ||
168 | Array.Copy(m_asset.Data, 0, im.ImageData.Data, 0, 600); | ||
169 | im.ImageID.Codec = 2; | ||
170 | RequestUser.OutPacket(im, ThrottleOutPacketType.Texture); | ||
171 | PacketCounter++; | ||
172 | } | ||
173 | } | ||
174 | else | ||
175 | { | ||
176 | ImagePacketPacket im = new ImagePacketPacket(); | ||
177 | im.Header.Reliable = false; | ||
178 | im.ImageID.Packet = (ushort) (PacketCounter); | ||
179 | im.ImageID.ID = m_asset.FullID; | ||
180 | int size = m_asset.Data.Length - 600 - (1000*(PacketCounter - 1)); | ||
181 | if (size > 1000) size = 1000; | ||
182 | im.ImageData.Data = new byte[size]; | ||
183 | try | ||
184 | { | ||
185 | Array.Copy(m_asset.Data, 600 + (1000*(PacketCounter - 1)), im.ImageData.Data, 0, size); | ||
186 | } | ||
187 | catch (ArgumentOutOfRangeException) | ||
188 | { | ||
189 | m_log.Error("[TEXTURE SENDER]: Unable to separate texture into multiple packets: Array bounds failure on asset:" + | ||
190 | m_asset.FullID.ToString() ); | ||
191 | return; | ||
192 | } | ||
193 | RequestUser.OutPacket(im, ThrottleOutPacketType.Texture); | ||
194 | PacketCounter++; | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /// <summary> | ||
200 | /// Calculate the number of packets that will be required to send the texture loaded into this sender | ||
201 | /// This is actually the number of 1000 byte packets not including an initial 600 byte packet... | ||
202 | /// </summary> | ||
203 | /// <param name="length"></param> | ||
204 | /// <returns></returns> | ||
205 | private int CalculateNumPackets(int length) | ||
206 | { | ||
207 | int numPackets = 0; | ||
208 | |||
209 | if (length > 600) | ||
210 | { | ||
211 | //over 600 bytes so split up file | ||
212 | int restData = (length - 600); | ||
213 | int restPackets = ((restData + 999)/1000); | ||
214 | numPackets = restPackets; | ||
215 | } | ||
216 | |||
217 | return numPackets; | ||
218 | } | ||
219 | } | ||
220 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/Environment/Modules/Agent/Xfer/XferModule.cs b/OpenSim/Region/Environment/Modules/Agent/Xfer/XferModule.cs new file mode 100644 index 0000000..b609f93 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/Agent/Xfer/XferModule.cs | |||
@@ -0,0 +1,225 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using libsecondlife; | ||
31 | using Nini.Config; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.Environment.Interfaces; | ||
34 | using OpenSim.Region.Environment.Scenes; | ||
35 | |||
36 | namespace OpenSim.Region.Environment.Modules.Agent.Xfer | ||
37 | { | ||
38 | public class XferModule : IRegionModule, IXfer | ||
39 | { | ||
40 | public Dictionary<string, byte[]> NewFiles = new Dictionary<string, byte[]>(); | ||
41 | public Dictionary<ulong, XferDownLoad> Transfers = new Dictionary<ulong, XferDownLoad>(); | ||
42 | |||
43 | private Scene m_scene; | ||
44 | |||
45 | public XferModule() | ||
46 | { | ||
47 | } | ||
48 | |||
49 | public void Initialise(Scene scene, IConfigSource config) | ||
50 | { | ||
51 | m_scene = scene; | ||
52 | m_scene.EventManager.OnNewClient += NewClient; | ||
53 | |||
54 | m_scene.RegisterModuleInterface<IXfer>(this); | ||
55 | } | ||
56 | |||
57 | public void PostInitialise() | ||
58 | { | ||
59 | } | ||
60 | |||
61 | public void Close() | ||
62 | { | ||
63 | } | ||
64 | |||
65 | public string Name | ||
66 | { | ||
67 | get { return "XferModule"; } | ||
68 | } | ||
69 | |||
70 | public bool IsSharedModule | ||
71 | { | ||
72 | get { return false; } | ||
73 | } | ||
74 | |||
75 | public void NewClient(IClientAPI client) | ||
76 | { | ||
77 | client.OnRequestXfer += RequestXfer; | ||
78 | client.OnConfirmXfer += AckPacket; | ||
79 | } | ||
80 | |||
81 | /// <summary> | ||
82 | /// | ||
83 | /// </summary> | ||
84 | /// <param name="remoteClient"></param> | ||
85 | /// <param name="xferID"></param> | ||
86 | /// <param name="fileName"></param> | ||
87 | public void RequestXfer(IClientAPI remoteClient, ulong xferID, string fileName) | ||
88 | { | ||
89 | lock (NewFiles) | ||
90 | { | ||
91 | if (NewFiles.ContainsKey(fileName)) | ||
92 | { | ||
93 | if (!Transfers.ContainsKey(xferID)) | ||
94 | { | ||
95 | byte[] fileData = NewFiles[fileName]; | ||
96 | XferDownLoad transaction = new XferDownLoad(fileName, fileData, xferID, remoteClient); | ||
97 | Transfers.Add(xferID, transaction); | ||
98 | NewFiles.Remove(fileName); | ||
99 | |||
100 | if (transaction.StartSend()) | ||
101 | { | ||
102 | Transfers.Remove(xferID); | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | public void AckPacket(IClientAPI remoteClient, ulong xferID, uint packet) | ||
110 | { | ||
111 | if (Transfers.ContainsKey(xferID)) | ||
112 | { | ||
113 | if (Transfers[xferID].AckPacket(packet)) | ||
114 | { | ||
115 | { | ||
116 | Transfers.Remove(xferID); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | |||
122 | public bool AddNewFile(string fileName, byte[] data) | ||
123 | { | ||
124 | lock (NewFiles) | ||
125 | { | ||
126 | if (NewFiles.ContainsKey(fileName)) | ||
127 | { | ||
128 | NewFiles[fileName] = data; | ||
129 | } | ||
130 | else | ||
131 | { | ||
132 | NewFiles.Add(fileName, data); | ||
133 | } | ||
134 | } | ||
135 | return true; | ||
136 | } | ||
137 | |||
138 | |||
139 | public class XferDownLoad | ||
140 | { | ||
141 | public byte[] Data = new byte[0]; | ||
142 | public string FileName = String.Empty; | ||
143 | public ulong XferID = 0; | ||
144 | public int DataPointer = 0; | ||
145 | public uint Packet = 0; | ||
146 | public IClientAPI Client; | ||
147 | public uint Serial = 1; | ||
148 | private bool complete; | ||
149 | |||
150 | public XferDownLoad(string fileName, byte[] data, ulong xferID, IClientAPI client) | ||
151 | { | ||
152 | FileName = fileName; | ||
153 | Data = data; | ||
154 | XferID = xferID; | ||
155 | Client = client; | ||
156 | } | ||
157 | |||
158 | public XferDownLoad() | ||
159 | { | ||
160 | } | ||
161 | |||
162 | /// <summary> | ||
163 | /// Start a transfer | ||
164 | /// </summary> | ||
165 | /// <returns>True if the transfer is complete, false if not</returns> | ||
166 | public bool StartSend() | ||
167 | { | ||
168 | if (Data.Length < 1000) | ||
169 | { | ||
170 | // for now (testing ) we only support files under 1000 bytes | ||
171 | byte[] transferData = new byte[Data.Length + 4]; | ||
172 | Array.Copy(Helpers.IntToBytes(Data.Length), 0, transferData, 0, 4); | ||
173 | Array.Copy(Data, 0, transferData, 4, Data.Length); | ||
174 | Client.SendXferPacket(XferID, 0 + 0x80000000, transferData); | ||
175 | |||
176 | complete = true; | ||
177 | } | ||
178 | else | ||
179 | { | ||
180 | byte[] transferData = new byte[1000 + 4]; | ||
181 | Array.Copy(Helpers.IntToBytes(Data.Length), 0, transferData, 0, 4); | ||
182 | Array.Copy(Data, 0, transferData, 4, 1000); | ||
183 | Client.SendXferPacket(XferID, 0, transferData); | ||
184 | Packet++; | ||
185 | DataPointer = 1000; | ||
186 | } | ||
187 | |||
188 | return complete; | ||
189 | } | ||
190 | |||
191 | /// <summary> | ||
192 | /// Respond to an ack packet from the client | ||
193 | /// </summary> | ||
194 | /// <param name="packet"></param> | ||
195 | /// <returns>True if the transfer is complete, false otherwise</returns> | ||
196 | public bool AckPacket(uint packet) | ||
197 | { | ||
198 | if (!complete) | ||
199 | { | ||
200 | if ((Data.Length - DataPointer) > 1000) | ||
201 | { | ||
202 | byte[] transferData = new byte[1000]; | ||
203 | Array.Copy(Data, DataPointer, transferData, 0, 1000); | ||
204 | Client.SendXferPacket(XferID, Packet, transferData); | ||
205 | Packet++; | ||
206 | DataPointer += 1000; | ||
207 | } | ||
208 | else | ||
209 | { | ||
210 | byte[] transferData = new byte[Data.Length - DataPointer]; | ||
211 | Array.Copy(Data, DataPointer, transferData, 0, Data.Length - DataPointer); | ||
212 | uint endPacket = Packet |= (uint) 0x80000000; | ||
213 | Client.SendXferPacket(XferID, endPacket, transferData); | ||
214 | Packet++; | ||
215 | DataPointer += (Data.Length - DataPointer); | ||
216 | |||
217 | complete = true; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | return complete; | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | } \ No newline at end of file | ||