aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Client/MXP/ClientStack/MXPClientView.cs11
-rw-r--r--OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs4
-rw-r--r--OpenSim/Framework/AvatarAppearance.cs12
-rw-r--r--OpenSim/Framework/BlockingQueue.cs5
-rw-r--r--OpenSim/Framework/IClientAPI.cs5
-rw-r--r--OpenSim/Framework/PacketPool.cs3
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Tests/BaseRequestHandlerTests.cs70
-rw-r--r--OpenSim/Framework/Tests/AgentCircuitDataTest.cs3
-rw-r--r--OpenSim/Region/Application/OpenSim.cs15
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs6
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs455
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs115
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs365
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs21
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs122
-rw-r--r--OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs685
-rw-r--r--OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs2
-rw-r--r--OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs4
-rw-r--r--OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs5
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneManager.cs122
-rw-r--r--OpenSim/Region/Framework/Scenes/ScenePresence.cs39
-rw-r--r--OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs11
-rw-r--r--OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs4
-rw-r--r--OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs8
-rw-r--r--OpenSim/Region/Physics/Meshing/Meshmerizer.cs14
-rw-r--r--OpenSim/Server/Handlers/Tests/Asset/AssetServerGetHandlerTests.cs80
-rw-r--r--OpenSim/Tests/Common/Mock/TestClient.cs4
27 files changed, 821 insertions, 1369 deletions
diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs
index 02eaf5d..e901ff6 100644
--- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs
+++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs
@@ -826,12 +826,7 @@ namespace OpenSim.Client.MXP.ClientStack
826 OpenSim.Region.Framework.Scenes.Scene scene=(OpenSim.Region.Framework.Scenes.Scene)Scene; 826 OpenSim.Region.Framework.Scenes.Scene scene=(OpenSim.Region.Framework.Scenes.Scene)Scene;
827 AvatarAppearance appearance; 827 AvatarAppearance appearance;
828 scene.GetAvatarAppearance(this,out appearance); 828 scene.GetAvatarAppearance(this,out appearance);
829 List<byte> visualParams = new List<byte>(); 829 OnSetAppearance(appearance.Texture, (byte[])appearance.VisualParams.Clone());
830 foreach (byte visualParam in appearance.VisualParams)
831 {
832 visualParams.Add(visualParam);
833 }
834 OnSetAppearance(appearance.Texture.GetBytes(), visualParams);
835 } 830 }
836 831
837 public void Stop() 832 public void Stop()
@@ -1649,5 +1644,9 @@ namespace OpenSim.Client.MXP.ClientStack
1649 public void SendPickInfoReply(UUID pickID,UUID creatorID, bool topPick, UUID parcelID, string name, string desc, UUID snapshotID, string user, string originalName, string simName, Vector3 posGlobal, int sortOrder, bool enabled) 1644 public void SendPickInfoReply(UUID pickID,UUID creatorID, bool topPick, UUID parcelID, string name, string desc, UUID snapshotID, string user, string originalName, string simName, Vector3 posGlobal, int sortOrder, bool enabled)
1650 { 1645 {
1651 } 1646 }
1647
1648 public void SendRebakeAvatarTextures(UUID textureID)
1649 {
1650 }
1652 } 1651 }
1653} 1652}
diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs
index bfca954..8c9eb5f 100644
--- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs
+++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs
@@ -1145,5 +1145,9 @@ namespace OpenSim.Client.VWoHTTP.ClientStack
1145 } 1145 }
1146 1146
1147 #endregion 1147 #endregion
1148
1149 public void SendRebakeAvatarTextures(UUID textureID)
1150 {
1151 }
1148 } 1152 }
1149} 1153}
diff --git a/OpenSim/Framework/AvatarAppearance.cs b/OpenSim/Framework/AvatarAppearance.cs
index 940ae3b..3f4214e 100644
--- a/OpenSim/Framework/AvatarAppearance.cs
+++ b/OpenSim/Framework/AvatarAppearance.cs
@@ -380,13 +380,13 @@ namespace OpenSim.Framework
380 /// <summary> 380 /// <summary>
381 /// Set up appearance textures and avatar parameters, including a height calculation 381 /// Set up appearance textures and avatar parameters, including a height calculation
382 /// </summary> 382 /// </summary>
383 /// <param name="texture"></param> 383 public virtual void SetAppearance(Primitive.TextureEntry textureEntry, byte[] visualParams)
384 /// <param name="visualParam"></param>
385 public virtual void SetAppearance(byte[] texture, List<byte> visualParam)
386 { 384 {
387 Primitive.TextureEntry textureEnt = new Primitive.TextureEntry(texture, 0, texture.Length); 385 if (textureEntry != null)
388 m_texture = textureEnt; 386 m_texture = textureEntry;
389 m_visualparams = visualParam.ToArray(); 387 if (visualParams != null)
388 m_visualparams = visualParams;
389
390 m_avatarHeight = 1.23077f // Shortest possible avatar height 390 m_avatarHeight = 1.23077f // Shortest possible avatar height
391 + 0.516945f * (float)m_visualparams[(int)VPElement.SHAPE_HEIGHT] / 255.0f // Body height 391 + 0.516945f * (float)m_visualparams[(int)VPElement.SHAPE_HEIGHT] / 255.0f // Body height
392 + 0.072514f * (float)m_visualparams[(int)VPElement.SHAPE_HEAD_SIZE] / 255.0f // Head size 392 + 0.072514f * (float)m_visualparams[(int)VPElement.SHAPE_HEAD_SIZE] / 255.0f // Head size
diff --git a/OpenSim/Framework/BlockingQueue.cs b/OpenSim/Framework/BlockingQueue.cs
index e03229b..857930a 100644
--- a/OpenSim/Framework/BlockingQueue.cs
+++ b/OpenSim/Framework/BlockingQueue.cs
@@ -66,7 +66,9 @@ namespace OpenSim.Framework
66 if (m_pqueue.Count > 0) 66 if (m_pqueue.Count > 0)
67 return m_pqueue.Dequeue(); 67 return m_pqueue.Dequeue();
68 68
69 return m_queue.Dequeue(); 69 if (m_queue.Count > 0)
70 return m_queue.Dequeue();
71 return default(T);
70 } 72 }
71 } 73 }
72 74
@@ -119,6 +121,7 @@ namespace OpenSim.Framework
119 { 121 {
120 m_pqueue.Clear(); 122 m_pqueue.Clear();
121 m_queue.Clear(); 123 m_queue.Clear();
124 Monitor.Pulse(m_queueSync);
122 } 125 }
123 } 126 }
124 } 127 }
diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs
index 4bc35e6..d3bd9e7 100644
--- a/OpenSim/Framework/IClientAPI.cs
+++ b/OpenSim/Framework/IClientAPI.cs
@@ -65,7 +65,7 @@ namespace OpenSim.Framework
65 65
66 public delegate void NetworkStats(int inPackets, int outPackets, int unAckedBytes); 66 public delegate void NetworkStats(int inPackets, int outPackets, int unAckedBytes);
67 67
68 public delegate void SetAppearance(byte[] texture, List<byte> visualParamList); 68 public delegate void SetAppearance(Primitive.TextureEntry textureEntry, byte[] visualParams);
69 69
70 public delegate void StartAnim(IClientAPI remoteClient, UUID animID); 70 public delegate void StartAnim(IClientAPI remoteClient, UUID animID);
71 71
@@ -1127,7 +1127,6 @@ namespace OpenSim.Framework
1127 1127
1128 void SetClientOption(string option, string value); 1128 void SetClientOption(string option, string value);
1129 string GetClientOption(string option); 1129 string GetClientOption(string option);
1130 void Terminate();
1131 1130
1132 void SendSetFollowCamProperties(UUID objectID, SortedDictionary<int, float> parameters); 1131 void SendSetFollowCamProperties(UUID objectID, SortedDictionary<int, float> parameters);
1133 void SendClearFollowCamProperties(UUID objectID); 1132 void SendClearFollowCamProperties(UUID objectID);
@@ -1175,5 +1174,7 @@ namespace OpenSim.Framework
1175 void KillEndDone(); 1174 void KillEndDone();
1176 1175
1177 bool AddGenericPacketHandler(string MethodName, GenericMessage handler); 1176 bool AddGenericPacketHandler(string MethodName, GenericMessage handler);
1177
1178 void SendRebakeAvatarTextures(UUID textureID);
1178 } 1179 }
1179} 1180}
diff --git a/OpenSim/Framework/PacketPool.cs b/OpenSim/Framework/PacketPool.cs
index 8075ce6..7e2860e 100644
--- a/OpenSim/Framework/PacketPool.cs
+++ b/OpenSim/Framework/PacketPool.cs
@@ -234,7 +234,8 @@ namespace OpenSim.Framework
234 234
235 lock (DataBlocks) 235 lock (DataBlocks)
236 { 236 {
237 DataBlocks[typeof(T)].Push(block); 237 if (DataBlocks[typeof(T)].Count < 50)
238 DataBlocks[typeof(T)].Push(block);
238 } 239 }
239 } 240 }
240 } 241 }
diff --git a/OpenSim/Framework/Servers/HttpServer/Tests/BaseRequestHandlerTests.cs b/OpenSim/Framework/Servers/HttpServer/Tests/BaseRequestHandlerTests.cs
deleted file mode 100644
index 2fa118d..0000000
--- a/OpenSim/Framework/Servers/HttpServer/Tests/BaseRequestHandlerTests.cs
+++ /dev/null
@@ -1,70 +0,0 @@
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 OpenSimulator 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.Text;
31using NUnit.Framework;
32using OpenSim.Tests.Common.Setup;
33
34namespace OpenSim.Framework.Servers.HttpServer.Tests
35{
36 [TestFixture]
37 public class BaseRequestHandlerTests
38 {
39 private const string BASE_PATH = "/testpath";
40
41 private class BaseRequestHandlerImpl : BaseRequestHandler
42 {
43 public BaseRequestHandlerImpl(string httpMethod, string path) : base(httpMethod, path)
44 {
45 }
46 }
47
48 [Test]
49 public void TestConstructor()
50 {
51 new BaseRequestHandlerImpl(null, null);
52 }
53
54 [Test]
55 public void TestGetParams()
56 {
57 BaseRequestHandlerImpl handler = new BaseRequestHandlerImpl(null, BASE_PATH);
58
59 BaseRequestHandlerTestHelper.BaseTestGetParams(handler, BASE_PATH);
60 }
61
62 [Test]
63 public void TestSplitParams()
64 {
65 BaseRequestHandlerImpl handler = new BaseRequestHandlerImpl(null, BASE_PATH);
66
67 BaseRequestHandlerTestHelper.BaseTestSplitParams(handler, BASE_PATH);
68 }
69 }
70}
diff --git a/OpenSim/Framework/Tests/AgentCircuitDataTest.cs b/OpenSim/Framework/Tests/AgentCircuitDataTest.cs
index ecd35c0..2fda6f3 100644
--- a/OpenSim/Framework/Tests/AgentCircuitDataTest.cs
+++ b/OpenSim/Framework/Tests/AgentCircuitDataTest.cs
@@ -227,8 +227,7 @@ namespace OpenSim.Framework.Tests
227 wearbyte.Add(VisualParams[i]); 227 wearbyte.Add(VisualParams[i]);
228 } 228 }
229 229
230 230 AvAppearance.SetAppearance(AvAppearance.Texture, (byte[])VisualParams.Clone());
231 AvAppearance.SetAppearance(AvAppearance.Texture.GetBytes(), wearbyte);
232 } 231 }
233 232
234 /// <summary> 233 /// <summary>
diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs
index f070812..4d8409bb 100644
--- a/OpenSim/Region/Application/OpenSim.cs
+++ b/OpenSim/Region/Application/OpenSim.cs
@@ -306,10 +306,6 @@ namespace OpenSim
306 "delete-region <name>", 306 "delete-region <name>",
307 "Delete a region from disk", RunCommand); 307 "Delete a region from disk", RunCommand);
308 308
309 m_console.Commands.AddCommand("region", false, "predecode-j2k",
310 "predecode-j2k [<num threads>]>",
311 "Precache assets,decode j2k layerdata", RunCommand);
312
313 m_console.Commands.AddCommand("region", false, "modules list", 309 m_console.Commands.AddCommand("region", false, "modules list",
314 "modules list", 310 "modules list",
315 "List modules", HandleModules); 311 "List modules", HandleModules);
@@ -744,17 +740,6 @@ namespace OpenSim
744 } 740 }
745 break; 741 break;
746 742
747 case "predecode-j2k":
748 if (cmdparams.Length > 0)
749 {
750 m_sceneManager.CacheJ2kDecode(Convert.ToInt32(cmdparams[0]));
751 }
752 else
753 {
754 m_sceneManager.CacheJ2kDecode(1);
755 }
756 break;
757
758 } 743 }
759 } 744 }
760 745
diff --git a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs
index 32a4ad4..31f9580 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs
@@ -34,16 +34,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
34{ 34{
35 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); 35 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
36 public delegate void PacketDrop(Packet pack, Object id); 36 public delegate void PacketDrop(Packet pack, Object id);
37 public delegate void QueueEmpty(ThrottleOutPacketType queue);
37 public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, UUID agentID, ThrottleOutPacketType throttlePacketType); 38 public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, UUID agentID, ThrottleOutPacketType throttlePacketType);
38 39
39 /// <summary> 40 /// <summary>
40 /// Interface to a class that handles all the activity involved with maintaining the client circuit (handling acks, 41 /// Interface to a class that handles all the activity involved with maintaining the client circuit (handling acks,
41 /// resends, pings, etc.) 42 /// resends, pings, etc.)
42 /// </summary> 43 /// </summary>
43 public interface ILLPacketHandler 44 public interface ILLPacketHandler : IDisposable
44 { 45 {
45 event PacketStats OnPacketStats; 46 event PacketStats OnPacketStats;
46 event PacketDrop OnPacketDrop; 47 event PacketDrop OnPacketDrop;
48 event QueueEmpty OnQueueEmpty;
47 SynchronizeClientHandler SynchronizeClient { set; } 49 SynchronizeClientHandler SynchronizeClient { set; }
48 50
49 int PacketsReceived { get; } 51 int PacketsReceived { get; }
@@ -70,12 +72,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
70 void OutPacket(Packet NewPack, 72 void OutPacket(Packet NewPack,
71 ThrottleOutPacketType throttlePacketType, Object id); 73 ThrottleOutPacketType throttlePacketType, Object id);
72 LLPacketQueue PacketQueue { get; } 74 LLPacketQueue PacketQueue { get; }
73 void Stop();
74 void Flush(); 75 void Flush();
75 void Clear(); 76 void Clear();
76 ClientInfo GetClientInfo(); 77 ClientInfo GetClientInfo();
77 void SetClientInfo(ClientInfo info); 78 void SetClientInfo(ClientInfo info);
78 void AddImportantPacket(PacketType type); 79 void AddImportantPacket(PacketType type);
79 void RemoveImportantPacket(PacketType type); 80 void RemoveImportantPacket(PacketType type);
81 int GetQueueCount(ThrottleOutPacketType queue);
80 } 82 }
81} 83}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
index 638c765..5f549b5 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
@@ -38,40 +38,37 @@ using System.Reflection;
38namespace OpenSim.Region.ClientStack.LindenUDP 38namespace OpenSim.Region.ClientStack.LindenUDP
39{ 39{
40 /// <summary> 40 /// <summary>
41 /// We use this class to store image data and associated request data and attributes 41 /// Stores information about a current texture download and a reference to the texture asset
42 /// </summary> 42 /// </summary>
43 public class J2KImage 43 public class J2KImage
44 { 44 {
45 private const int IMAGE_PACKET_SIZE = 1000;
46 private const int FIRST_PACKET_SIZE = 600;
47
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46 49
47 public double m_designatedPriorityKey; 50 public uint m_lastSequence;
48 public double m_requestedPriority = 0.0d; 51 public float m_requestedPriority;
49 public uint m_lastSequence = 0;
50 public uint m_requestedPacketNumber; 52 public uint m_requestedPacketNumber;
51 public sbyte m_requestedDiscardLevel; 53 public sbyte m_requestedDiscardLevel;
52 public UUID m_requestedUUID; 54 public UUID m_requestedUUID;
53 public IJ2KDecoder m_j2kDecodeModule; 55 public IJ2KDecoder m_j2kDecodeModule;
54 public IAssetService m_assetCache; 56 public IAssetService m_assetCache;
55 public OpenJPEG.J2KLayerInfo[] Layers = new OpenJPEG.J2KLayerInfo[0]; 57 public OpenJPEG.J2KLayerInfo[] m_layers;
56 public AssetBase m_MissingSubstitute = null; 58 public bool m_decoded;
57 public bool m_decoded = false; 59 public bool m_hasasset;
58 public bool m_completedSendAtCurrentDiscardLevel; 60 public C5.IPriorityQueueHandle<J2KImage> m_priorityQueueHandle;
59 61
60 private sbyte m_discardLevel=-1;
61 private uint m_packetNumber; 62 private uint m_packetNumber;
62 private bool m_decoderequested = false; 63 private bool m_decoderequested;
63 private bool m_hasasset = false; 64 private bool m_asset_requested;
64 private bool m_asset_requested = false; 65 private bool m_sentinfo;
65 private bool m_sentinfo = false; 66 private uint m_stopPacket;
66 private uint m_stopPacket = 0; 67 private AssetBase m_asset;
67 private const int cImagePacketSize = 1000; 68 private int m_assetDataLength;
68 private const int cFirstPacketSize = 600; 69 private LLImageManager m_imageManager;
69 private AssetBase m_asset = null; 70
70 private LLImageManager m_image; 71 #region Properties
71 public J2KImage(LLImageManager image)
72 {
73 m_image = image;
74 }
75 72
76 public uint m_pPacketNumber 73 public uint m_pPacketNumber
77 { 74 {
@@ -84,10 +81,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
84 81
85 public byte[] Data 82 public byte[] Data
86 { 83 {
87 get 84 get
88 { 85 {
89 if (m_asset != null) 86 if (m_asset != null)
90 return m_asset.Data; 87 return m_asset.Data;
91 else 88 else
92 return null; 89 return null;
93 } 90 }
@@ -97,9 +94,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
97 { 94 {
98 if (!m_decoded) 95 if (!m_decoded)
99 return 0; 96 return 0;
97
100 try 98 try
101 { 99 {
102 return (ushort)(((m_asset.Data.Length - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1); 100 return (ushort)(((m_assetDataLength - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1);
103 } 101 }
104 catch (Exception) 102 catch (Exception)
105 { 103 {
@@ -110,119 +108,154 @@ namespace OpenSim.Region.ClientStack.LindenUDP
110 } 108 }
111 } 109 }
112 110
113 public void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers) 111 #endregion Properties
114 {
115 m_image.m_outstandingtextures++;
116 Layers = layers;
117 m_decoded = true;
118 RunUpdate();
119 }
120 112
121 public void AssetDataCallback(UUID AssetID, AssetBase asset) 113 public J2KImage(LLImageManager imageManager)
122 { 114 {
123 m_hasasset = true; 115 m_imageManager = imageManager;
124 if (asset == null || asset.Data == null)
125 {
126 m_asset = m_MissingSubstitute;
127 }
128 else
129 {
130 m_asset = asset;
131 }
132 RunUpdate();
133 } 116 }
134 117
135 protected void AssetReceived(string id, Object sender, AssetBase asset) 118 public bool SendPackets(LLClientView client, int maxpack)
136 { 119 {
137 UUID assetID = UUID.Zero; 120 if (m_packetNumber <= m_stopPacket)
138 if (asset != null) 121 {
139 assetID = asset.FullID; 122 bool SendMore = true;
140 123 if (!m_sentinfo || (m_packetNumber == 0))
141 AssetDataCallback(assetID, asset); 124 {
142 125 if (SendFirstPacket(client))
143 } 126 {
127 SendMore = false;
128 }
129 m_sentinfo = true;
130 m_packetNumber++;
131 }
132 // bool ignoreStop = false;
133 if (m_packetNumber < 2)
134 {
135 m_packetNumber = 2;
136 }
144 137
145 private int GetPacketForBytePosition(int bytePosition) 138 int count = 0;
146 { 139 while (SendMore && count < maxpack && m_packetNumber <= m_stopPacket)
147 return ((bytePosition - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1; 140 {
148 } 141 count++;
142 SendMore = SendPacket(client);
143 m_packetNumber++;
144 }
149 145
150 public int LastPacketSize() 146 if (m_packetNumber > m_stopPacket)
151 { 147 return true;
152 if (m_packetNumber == 1)
153 return m_asset.Data.Length;
154 int lastsize = (m_asset.Data.Length - cFirstPacketSize) % cImagePacketSize;
155 //If the last packet size is zero, it's really cImagePacketSize, it sits on the boundary
156 if (lastsize == 0)
157 {
158 lastsize = cImagePacketSize;
159 } 148 }
160 return lastsize;
161 }
162
163 public int CurrentBytePosition()
164 {
165 if (m_packetNumber == 0)
166 return 0;
167 if (m_packetNumber == 1)
168 return cFirstPacketSize;
169 149
170 int result = cFirstPacketSize + ((int)m_packetNumber - 2) * cImagePacketSize; 150 return false;
171 if (result < 0)
172 {
173 result = cFirstPacketSize;
174 }
175 return result;
176 } 151 }
177 152
178 public bool SendFirstPacket(LLClientView client) 153 public void RunUpdate()
179 { 154 {
180 // this means we don't have 155 //This is where we decide what we need to update
181 if (Data == null) 156 //and assign the real discardLevel and packetNumber
182 { 157 //assuming of course that the connected client might be bonkers
183 client.SendImageNotFound(m_requestedUUID); 158
184 m_log.WarnFormat("[TEXTURE]: Got null Data element on a asset {0}.. and the missing image Data property is al", m_requestedUUID); 159 if (!m_hasasset)
185 return true;
186 }
187 // Do we have less then 1 packet's worth of data?
188 else if (m_asset.Data.Length <= cFirstPacketSize)
189 { 160 {
190 // Send only 1 packet 161 if (!m_asset_requested)
191 client.SendImageFirstPart(1, m_requestedUUID, (uint)m_asset.Data.Length, m_asset.Data, 2); 162 {
192 m_stopPacket = 0; 163 m_asset_requested = true;
193 return true; 164 m_assetCache.Get(m_requestedUUID.ToString(), this, AssetReceived);
165 }
194 } 166 }
195 else 167 else
196 { 168 {
197 byte[] firstImageData = new byte[cFirstPacketSize]; 169 if (!m_decoded)
198 try 170 {
199 { 171 //We need to decode the requested image first
200 Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)cFirstPacketSize); 172 if (!m_decoderequested)
201 client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_asset.Data.Length, firstImageData, 2); 173 {
174 //Request decode
175 m_decoderequested = true;
176 // Do we have a jpeg decoder?
177 if (m_j2kDecodeModule != null)
178 {
179 if (Data == null)
180 {
181 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
182 }
183 else
184 {
185 // Send it off to the jpeg decoder
186 m_j2kDecodeModule.BeginDecode(m_requestedUUID, Data, J2KDecodedCallback);
187 }
188
189 }
190 else
191 {
192 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
193 }
194 }
202 } 195 }
203 catch (Exception) 196 else
204 { 197 {
205 m_log.Error("Texture block copy failed. Possibly out of memory?"); 198 // Check for missing image asset data
206 return true; 199 if (m_asset == null || m_asset.Data == null)
200 {
201 // FIXME:
202 m_packetNumber = m_stopPacket;
203 return;
204 }
205
206 if (m_requestedDiscardLevel >= 0 || m_stopPacket == 0)
207 {
208 int maxDiscardLevel = Math.Max(0, m_layers.Length - 1);
209
210 // Treat initial texture downloads with a DiscardLevel of -1 a request for the highest DiscardLevel
211 if (m_requestedDiscardLevel < 0 && m_stopPacket == 0)
212 m_requestedDiscardLevel = (sbyte)maxDiscardLevel;
213
214 // Clamp at the highest discard level
215 m_requestedDiscardLevel = (sbyte)Math.Min(m_requestedDiscardLevel, maxDiscardLevel);
216
217 //Calculate the m_stopPacket
218 if (m_layers.Length > 0)
219 {
220 m_stopPacket = (uint)GetPacketForBytePosition(m_layers[(m_layers.Length - 1) - m_requestedDiscardLevel].End);
221 //I don't know why, but the viewer seems to expect the final packet if the file
222 //is just one packet bigger.
223 if (TexturePacketCount() == m_stopPacket + 1)
224 {
225 m_stopPacket = TexturePacketCount();
226 }
227 }
228 else
229 {
230 m_stopPacket = TexturePacketCount();
231 }
232
233 m_packetNumber = m_requestedPacketNumber;
234 }
235
236 if (m_imageManager.Client.PacketHandler.GetQueueCount(ThrottleOutPacketType.Texture) == 0)
237 {
238 //m_log.Debug("No textures queued, sending one packet to kickstart it");
239 SendPacket(m_imageManager.Client);
240 }
207 } 241 }
208 } 242 }
209 return false;
210 } 243 }
211 244
212 private bool SendPacket(LLClientView client) 245 private bool SendPacket(LLClientView client)
213 { 246 {
214 bool complete = false; 247 bool complete = false;
215 int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : cImagePacketSize; 248 int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : IMAGE_PACKET_SIZE;
216 249
217 try 250 try
218 { 251 {
219 if ((CurrentBytePosition() + cImagePacketSize) > m_asset.Data.Length) 252 if ((CurrentBytePosition() + IMAGE_PACKET_SIZE) > m_assetDataLength)
220 { 253 {
221 imagePacketSize = LastPacketSize(); 254 imagePacketSize = LastPacketSize();
222 complete=true; 255 complete = true;
223 if ((CurrentBytePosition() + imagePacketSize) > m_asset.Data.Length) 256 if ((CurrentBytePosition() + imagePacketSize) > m_assetDataLength)
224 { 257 {
225 imagePacketSize = m_asset.Data.Length - CurrentBytePosition(); 258 imagePacketSize = m_assetDataLength - CurrentBytePosition();
226 complete = true; 259 complete = true;
227 } 260 }
228 } 261 }
@@ -244,7 +277,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
244 } 277 }
245 278
246 //Send the packet 279 //Send the packet
247 client.SendImageNextPart((ushort)(m_packetNumber-1), m_requestedUUID, imageData); 280 client.SendImageNextPart((ushort)(m_packetNumber - 1), m_requestedUUID, imageData);
248 } 281 }
249 if (complete) 282 if (complete)
250 { 283 {
@@ -260,143 +293,115 @@ namespace OpenSim.Region.ClientStack.LindenUDP
260 return false; 293 return false;
261 } 294 }
262 } 295 }
263 public bool SendPackets(LLClientView client, int maxpack) 296
297 private int GetPacketForBytePosition(int bytePosition)
264 { 298 {
299 return ((bytePosition - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1;
300 }
265 301
266 if (!m_completedSendAtCurrentDiscardLevel) 302 private int LastPacketSize()
303 {
304 if (m_packetNumber == 1)
305 return m_assetDataLength;
306 int lastsize = (m_assetDataLength - FIRST_PACKET_SIZE) % IMAGE_PACKET_SIZE;
307 //If the last packet size is zero, it's really cImagePacketSize, it sits on the boundary
308 if (lastsize == 0)
267 { 309 {
268 if (m_packetNumber <= m_stopPacket) 310 lastsize = IMAGE_PACKET_SIZE;
269 { 311 }
270 bool SendMore = true; 312 return lastsize;
271 if (!m_sentinfo || (m_packetNumber == 0)) 313 }
272 {
273 if (SendFirstPacket(client))
274 {
275 SendMore = false;
276 }
277 m_sentinfo = true;
278 m_packetNumber++;
279 }
280 // bool ignoreStop = false;
281 if (m_packetNumber < 2)
282 {
283 m_packetNumber = 2;
284 }
285 314
286 int count = 0; 315 private int CurrentBytePosition()
287 while (SendMore && count < maxpack && m_packetNumber <= m_stopPacket) 316 {
288 { 317 if (m_packetNumber == 0)
289 count++; 318 return 0;
290 SendMore = SendPacket(client); 319 if (m_packetNumber == 1)
291 m_packetNumber++; 320 return FIRST_PACKET_SIZE;
292 }
293 321
294 if (m_packetNumber > m_stopPacket) 322 int result = FIRST_PACKET_SIZE + ((int)m_packetNumber - 2) * IMAGE_PACKET_SIZE;
295 { 323 if (result < 0)
296 return true; 324 {
297 } 325 result = FIRST_PACKET_SIZE;
326 }
327 return result;
328 }
329
330 private bool SendFirstPacket(LLClientView client)
331 {
332 // this means we don't have
333 if (Data == null)
334 {
335 client.SendImageNotFound(m_requestedUUID);
336 m_log.WarnFormat("[TEXTURE]: Got null Data element on a asset {0}.. and the missing image Data property is also null", m_requestedUUID);
337 return true;
338 }
339 // Do we have less then 1 packet's worth of data?
340 else if (m_assetDataLength <= FIRST_PACKET_SIZE)
341 {
342 // Send only 1 packet
343 client.SendImageFirstPart(1, m_requestedUUID, (uint)m_assetDataLength, m_asset.Data, 2);
344 m_stopPacket = 0;
345 return true;
346 }
347 else
348 {
349 byte[] firstImageData = new byte[FIRST_PACKET_SIZE];
350 try
351 {
352 Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)FIRST_PACKET_SIZE);
353 client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_assetDataLength, firstImageData, 2);
354 }
355 catch (Exception)
356 {
357 m_log.Error("Texture block copy failed. Possibly out of memory?");
358 return true;
298 } 359 }
299 } 360 }
300 return false; 361 return false;
301 } 362 }
302 363
303 public void RunUpdate() 364 private void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers)
304 { 365 {
305 //This is where we decide what we need to update 366 m_layers = layers;
306 //and assign the real discardLevel and packetNumber 367 m_decoded = true;
307 //assuming of course that the connected client might be bonkers 368 RunUpdate();
369 }
308 370
309 if (!m_hasasset) 371 private void AssetDataCallback(UUID AssetID, AssetBase asset)
310 { 372 {
373 m_hasasset = true;
311 374
312 if (!m_asset_requested) 375 if (asset == null || asset.Data == null)
376 {
377 if (m_imageManager.MissingImage != null)
313 { 378 {
314 m_asset_requested = true; 379 m_asset = m_imageManager.MissingImage;
315 m_assetCache.Get(m_requestedUUID.ToString(), this, AssetReceived); 380 m_assetDataLength = m_asset.Data.Length;
316 381 }
382 else
383 {
384 m_asset = null;
385 m_decoded = true;
317 } 386 }
318
319 } 387 }
320 else 388 else
321 { 389 {
390 m_asset = asset;
391 m_assetDataLength = m_asset.Data.Length;
392 }
322 393
394 RunUpdate();
395 }
323 396
324 if (!m_decoded) 397 private void AssetReceived(string id, Object sender, AssetBase asset)
325 { 398 {
326 //We need to decode the requested image first 399 UUID assetID = UUID.Zero;
327 if (!m_decoderequested) 400 if (asset != null)
328 { 401 assetID = asset.FullID;
329 //Request decode
330 m_decoderequested = true;
331 // Do we have a jpeg decoder?
332 if (m_j2kDecodeModule != null)
333 {
334 if (Data == null)
335 {
336 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
337 }
338 // Send it off to the jpeg decoder
339 m_j2kDecodeModule.decode(m_requestedUUID, Data, J2KDecodedCallback);
340
341 }
342 else
343 {
344 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
345 }
346 }
347
348 }
349 else
350 {
351 //discardLevel of -1 means just update the priority
352 if (m_requestedDiscardLevel != -1)
353 {
354 //Evaluate the discard level
355 //First, is it positive?
356 if (m_requestedDiscardLevel >= 0)
357 {
358 if (m_requestedDiscardLevel > Layers.Length - 1)
359 {
360 m_discardLevel = (sbyte)(Layers.Length - 1);
361 }
362 else
363 {
364 m_discardLevel = m_requestedDiscardLevel;
365 }
366 402
367 //Calculate the m_stopPacket 403 AssetDataCallback(assetID, asset);
368 if (Layers.Length > 0)
369 {
370 m_stopPacket = (uint)GetPacketForBytePosition(Layers[(Layers.Length - 1) - m_discardLevel].End);
371 //I don't know why, but the viewer seems to expect the final packet if the file
372 //is just one packet bigger.
373 if (TexturePacketCount() == m_stopPacket + 1)
374 {
375 m_stopPacket = TexturePacketCount();
376 }
377 }
378 else
379 {
380 m_stopPacket = TexturePacketCount();
381 }
382 //Don't reset packet number unless we're waiting or it's ahead of us
383 if (m_completedSendAtCurrentDiscardLevel || m_requestedPacketNumber>m_packetNumber)
384 {
385 m_packetNumber = m_requestedPacketNumber;
386 }
387 404
388 if (m_packetNumber <= m_stopPacket)
389 {
390 m_completedSendAtCurrentDiscardLevel = false;
391 }
392 }
393 }
394 else
395 {
396 m_packetNumber = m_stopPacket;
397 }
398 }
399 }
400 } 405 }
401 } 406 }
402} 407}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 88ace6a..d8bd36d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -81,8 +81,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
81 private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates = 81 private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates =
82 new List<ObjectUpdatePacket.ObjectDataBlock>(); 82 new List<ObjectUpdatePacket.ObjectDataBlock>();
83 83
84 private Timer m_textureRequestTimer;
85
86 private bool m_clientBlocked; 84 private bool m_clientBlocked;
87 85
88 private int m_probesWithNoIngressPackets; 86 private int m_probesWithNoIngressPackets;
@@ -143,9 +141,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
143 protected int m_primTerseUpdateRate = 10; 141 protected int m_primTerseUpdateRate = 10;
144 protected int m_primFullUpdateRate = 14; 142 protected int m_primFullUpdateRate = 14;
145 143
146 protected int m_textureRequestRate = 100; 144 protected int m_textureSendLimit = 20;
147 protected int m_textureSendLimit = 10; 145 protected int m_textureDataLimit = 10;
148 protected int m_textureDataLimit = 5;
149 146
150 protected int m_packetMTU = 1400; 147 protected int m_packetMTU = 1400;
151 148
@@ -534,6 +531,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
534 m_PacketHandler = new LLPacketHandler(this, m_networkServer, userSettings); 531 m_PacketHandler = new LLPacketHandler(this, m_networkServer, userSettings);
535 m_PacketHandler.SynchronizeClient = SynchronizeClient; 532 m_PacketHandler.SynchronizeClient = SynchronizeClient;
536 m_PacketHandler.OnPacketStats += PopulateStats; 533 m_PacketHandler.OnPacketStats += PopulateStats;
534 m_PacketHandler.OnQueueEmpty += HandleQueueEmpty;
537 535
538 if (scene.Config != null) 536 if (scene.Config != null)
539 { 537 {
@@ -555,9 +553,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
555 m_primFullUpdateRate = clientConfig.GetInt("FullUpdateRate", 553 m_primFullUpdateRate = clientConfig.GetInt("FullUpdateRate",
556 m_primFullUpdateRate); 554 m_primFullUpdateRate);
557 555
558 m_textureRequestRate = clientConfig.GetInt("TextureRequestRate",
559 m_textureRequestRate);
560
561 m_textureSendLimit = clientConfig.GetInt("TextureSendLimit", 556 m_textureSendLimit = clientConfig.GetInt("TextureSendLimit",
562 m_textureSendLimit); 557 m_textureSendLimit);
563 558
@@ -607,9 +602,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
607 if (m_primFullUpdateTimer.Enabled) 602 if (m_primFullUpdateTimer.Enabled)
608 lock (m_primFullUpdateTimer) 603 lock (m_primFullUpdateTimer)
609 m_primFullUpdateTimer.Stop(); 604 m_primFullUpdateTimer.Stop();
610 if (m_textureRequestTimer.Enabled)
611 lock (m_textureRequestTimer)
612 m_textureRequestTimer.Stop();
613 605
614 // This is just to give the client a reasonable chance of 606 // This is just to give the client a reasonable chance of
615 // flushing out all it's packets. There should probably 607 // flushing out all it's packets. There should probably
@@ -633,6 +625,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
633 // of the client thread regardless of where Close() is called. 625 // of the client thread regardless of where Close() is called.
634 KillEndDone(); 626 KillEndDone();
635 } 627 }
628
629 Terminate();
636 } 630 }
637 631
638 /// <summary> 632 /// <summary>
@@ -704,10 +698,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
704 if (m_primFullUpdateTimer.Enabled) 698 if (m_primFullUpdateTimer.Enabled)
705 lock (m_primFullUpdateTimer) 699 lock (m_primFullUpdateTimer)
706 m_primFullUpdateTimer.Stop(); 700 m_primFullUpdateTimer.Stop();
707
708 if (m_textureRequestTimer.Enabled)
709 lock (m_textureRequestTimer)
710 m_textureRequestTimer.Stop();
711 } 701 }
712 702
713 public void Restart() 703 public void Restart()
@@ -730,23 +720,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
730 m_primFullUpdateTimer = new Timer(m_primFullUpdateRate); 720 m_primFullUpdateTimer = new Timer(m_primFullUpdateRate);
731 m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); 721 m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates);
732 m_primFullUpdateTimer.AutoReset = false; 722 m_primFullUpdateTimer.AutoReset = false;
733
734 m_textureRequestTimer = new Timer(m_textureRequestRate);
735 m_textureRequestTimer.Elapsed += new ElapsedEventHandler(ProcessTextureRequests);
736 m_textureRequestTimer.AutoReset = false;
737
738 } 723 }
739 724
740 public void Terminate() 725 private void Terminate()
741 { 726 {
727 IsActive = false;
728
729 m_clientPingTimer.Close();
730 m_avatarTerseUpdateTimer.Close();
731 m_primTerseUpdateTimer.Close();
732 m_primFullUpdateTimer.Close();
733
742 m_PacketHandler.OnPacketStats -= PopulateStats; 734 m_PacketHandler.OnPacketStats -= PopulateStats;
743 m_PacketHandler.Stop(); 735 m_PacketHandler.Dispose();
744 736
745 // wait for thread stoped 737 // wait for thread stoped
746 m_clientThread.Join(); 738 // m_clientThread.Join();
747 739
748 // delete circuit code 740 // delete circuit code
749 m_networkServer.CloseClient(this); 741 //m_networkServer.CloseClient(this);
750 } 742 }
751 743
752 #endregion 744 #endregion
@@ -876,6 +868,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
876 while (IsActive) 868 while (IsActive)
877 { 869 {
878 LLQueItem nextPacket = m_PacketHandler.PacketQueue.Dequeue(); 870 LLQueItem nextPacket = m_PacketHandler.PacketQueue.Dequeue();
871
872 if (nextPacket == null) {
873 m_log.DebugFormat("[CLIENT]: PacketQueue return null LLQueItem");
874 continue;
875 }
879 876
880 if (nextPacket.Incoming) 877 if (nextPacket.Incoming)
881 { 878 {
@@ -967,10 +964,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
967 m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); 964 m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates);
968 m_primFullUpdateTimer.AutoReset = false; 965 m_primFullUpdateTimer.AutoReset = false;
969 966
970 m_textureRequestTimer = new Timer(m_textureRequestRate);
971 m_textureRequestTimer.Elapsed += new ElapsedEventHandler(ProcessTextureRequests);
972 m_textureRequestTimer.AutoReset = false;
973
974 m_scene.AddNewClient(this); 967 m_scene.AddNewClient(this);
975 968
976 RefreshGroupMembership(); 969 RefreshGroupMembership();
@@ -1042,26 +1035,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1042 } 1035 }
1043 } 1036 }
1044 1037
1045 protected virtual void TextureRequestHandler()
1046 {
1047 m_log.DebugFormat("[TRH] Thread started");
1048 while (m_imageManager != null)
1049 {
1050 try
1051 {
1052 while (m_imageManager != null)
1053 {
1054 }
1055 }
1056 catch (Exception e)
1057 {
1058 m_log.WarnFormat("[TRH] Exception in handler loop: {0}", e.Message);
1059 m_log.Debug(e);
1060 }
1061 }
1062 m_log.DebugFormat("[TRH] Thread terminated");
1063 }
1064
1065 # endregion 1038 # endregion
1066 1039
1067 // Previously ClientView.API partial class 1040 // Previously ClientView.API partial class
@@ -3164,16 +3137,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3164 // Unlike the other timers, this one is only started after 3137 // Unlike the other timers, this one is only started after
3165 // the first request is seen. 3138 // the first request is seen.
3166 3139
3167 void ProcessTextureRequests(object sender, ElapsedEventArgs e) 3140 void HandleQueueEmpty(ThrottleOutPacketType queue)
3141 {
3142 switch (queue)
3143 {
3144 case ThrottleOutPacketType.Texture:
3145 ProcessTextureRequests();
3146 break;
3147 }
3148 }
3149
3150 void ProcessTextureRequests()
3168 { 3151 {
3169 if (m_imageManager != null) 3152 if (m_imageManager != null)
3170 { 3153 {
3171 if (m_imageManager.ProcessImageQueue(m_textureSendLimit, 3154 m_imageManager.ProcessImageQueue(m_textureSendLimit,
3172 m_textureDataLimit)) 3155 m_textureDataLimit);
3173 {
3174 lock (m_textureRequestTimer)
3175 m_textureRequestTimer.Start();
3176 }
3177 } 3156 }
3178 } 3157 }
3179 3158
@@ -5275,13 +5254,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5275 // for the client session anyway, in order to protect ourselves against bad code in plugins 5254 // for the client session anyway, in order to protect ourselves against bad code in plugins
5276 try 5255 try
5277 { 5256 {
5278 List<byte> visualparams = new List<byte>(); 5257 byte[] visualparams = new byte[appear.VisualParam.Length];
5279 foreach (AgentSetAppearancePacket.VisualParamBlock x in appear.VisualParam) 5258 for (int i = 0; i < appear.VisualParam.Length; i++)
5280 { 5259 visualparams[i] = appear.VisualParam[i].ParamValue;
5281 visualparams.Add(x.ParamValue);
5282 }
5283 5260
5284 handlerSetAppearance(appear.ObjectData.TextureEntry, visualparams); 5261 Primitive.TextureEntry te = null;
5262 if (appear.ObjectData.TextureEntry.Length > 1)
5263 te = new Primitive.TextureEntry(appear.ObjectData.TextureEntry, 0, appear.ObjectData.TextureEntry.Length);
5264
5265 handlerSetAppearance(te, visualparams);
5285 } 5266 }
5286 catch (Exception e) 5267 catch (Exception e)
5287 { 5268 {
@@ -6624,8 +6605,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
6624 if (m_imageManager != null) 6605 if (m_imageManager != null)
6625 { 6606 {
6626 m_imageManager.EnqueueReq(args); 6607 m_imageManager.EnqueueReq(args);
6627 lock (m_textureRequestTimer)
6628 m_textureRequestTimer.Start();
6629 } 6608 }
6630 } 6609 }
6631 } 6610 }
@@ -11026,5 +11005,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11026 } 11005 }
11027 11006
11028 #endregion 11007 #endregion
11008
11009 public void SendRebakeAvatarTextures(UUID textureID)
11010 {
11011 RebakeAvatarTexturesPacket pack =
11012 (RebakeAvatarTexturesPacket)PacketPool.Instance.GetPacket(PacketType.RebakeAvatarTextures);
11013
11014 pack.TextureData = new RebakeAvatarTexturesPacket.TextureDataBlock();
11015 pack.TextureData.TextureID = textureID;
11016 OutPacket(pack, ThrottleOutPacketType.Task);
11017 }
11029 } 11018 }
11030} 11019}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
index 295a5e6..a484fdf 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
@@ -27,58 +27,60 @@
27 27
28using System; 28using System;
29using System.Threading; 29using System.Threading;
30using System.Collections;
30using System.Collections.Generic; 31using System.Collections.Generic;
32using System.Reflection;
31using OpenMetaverse; 33using OpenMetaverse;
32using OpenMetaverse.Imaging; 34using OpenMetaverse.Imaging;
33using OpenSim.Framework; 35using OpenSim.Framework;
34using OpenSim.Region.Framework.Interfaces; 36using OpenSim.Region.Framework.Interfaces;
35using OpenSim.Services.Interfaces; 37using OpenSim.Services.Interfaces;
36using log4net; 38using log4net;
37using System.Reflection;
38 39
39namespace OpenSim.Region.ClientStack.LindenUDP 40namespace OpenSim.Region.ClientStack.LindenUDP
40{ 41{
41
42 public class LLImageManager 42 public class LLImageManager
43 { 43 {
44 44 private sealed class J2KImageComparer : IComparer<J2KImage>
45 //Public interfaces: 45 {
46 //Constructor - (LLClientView, IAssetCache, IJ2KDecoder); 46 public int Compare(J2KImage x, J2KImage y)
47 //void EnqueueReq - (TextureRequestArgs) 47 {
48 //ProcessImageQueue 48 return x.m_requestedPriority.CompareTo(y.m_requestedPriority);
49 //Close 49 }
50 }
51
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 52 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51 private bool m_shuttingdown = false; 53 private bool m_shuttingdown = false;
52 private long m_lastloopprocessed = 0; 54 private long m_lastloopprocessed = 0;
55 private AssetBase m_missingImage = null;
53 56
54 private LLClientView m_client; //Client we're assigned to 57 private LLClientView m_client; //Client we're assigned to
55 private IAssetService m_assetCache; //Asset Cache 58 private IAssetService m_assetCache; //Asset Cache
56 private IJ2KDecoder m_j2kDecodeModule; //Our J2K module 59 private IJ2KDecoder m_j2kDecodeModule; //Our J2K module
60 private C5.IntervalHeap<J2KImage> m_priorityQueue = new C5.IntervalHeap<J2KImage>(10, new J2KImageComparer());
57 61
58 private readonly AssetBase m_missingsubstitute; //Sustitute for bad decodes
59 private Dictionary<UUID,J2KImage> m_imagestore; // Our main image storage dictionary
60 private SortedList<double,UUID> m_priorities; // For fast image lookup based on priority
61 private Dictionary<int, int> m_priorityresolver; //Enabling super fast assignment of images with the same priorities
62
63 private const double doubleMinimum = .0000001;
64
65 public int m_outstandingtextures = 0;
66 //Constructor
67 public LLImageManager(LLClientView client, IAssetService pAssetCache, IJ2KDecoder pJ2kDecodeModule) 62 public LLImageManager(LLClientView client, IAssetService pAssetCache, IJ2KDecoder pJ2kDecodeModule)
68 { 63 {
69
70 m_imagestore = new Dictionary<UUID,J2KImage>();
71 m_priorities = new SortedList<double,UUID>();
72 m_priorityresolver = new Dictionary<int, int>();
73 m_client = client; 64 m_client = client;
74 m_assetCache = pAssetCache; 65 m_assetCache = pAssetCache;
75 if (pAssetCache != null) 66 if (pAssetCache != null)
76 m_missingsubstitute = pAssetCache.Get("5748decc-f629-461c-9a36-a35a221fe21f"); 67 m_missingImage = pAssetCache.Get("5748decc-f629-461c-9a36-a35a221fe21f");
77 else 68 else
78 m_log.Error("[ClientView] - couldn't set missing image, all manner of things will probably break"); 69 m_log.Error("[ClientView] - couldn't set missing image asset, falling back to missing image packet. This is known to crash the client");
70
79 m_j2kDecodeModule = pJ2kDecodeModule; 71 m_j2kDecodeModule = pJ2kDecodeModule;
80 } 72 }
81 73
74 public LLClientView Client
75 {
76 get { return m_client; }
77 }
78
79 public AssetBase MissingImage
80 {
81 get { return m_missingImage; }
82 }
83
82 public void EnqueueReq(TextureRequestArgs newRequest) 84 public void EnqueueReq(TextureRequestArgs newRequest)
83 { 85 {
84 //newRequest is the properties of our new texture fetch request. 86 //newRequest is the properties of our new texture fetch request.
@@ -88,234 +90,203 @@ namespace OpenSim.Region.ClientStack.LindenUDP
88 //Make sure we're not shutting down.. 90 //Make sure we're not shutting down..
89 if (!m_shuttingdown) 91 if (!m_shuttingdown)
90 { 92 {
93 J2KImage imgrequest;
91 94
92 //Do we already know about this UUID? 95 // Do a linear search for this texture download
93 if (m_imagestore.ContainsKey(newRequest.RequestedAssetID)) 96 lock (m_priorityQueue)
94 { 97 m_priorityQueue.Find(delegate(J2KImage img) { return img.m_requestedUUID == newRequest.RequestedAssetID; }, out imgrequest);
95 //Check the packet sequence to make sure this isn't older than
96 //one we've already received
97
98 J2KImage imgrequest = m_imagestore[newRequest.RequestedAssetID];
99
100 // This is the inbound request sequence number. We can ignore
101 // "old" ones.
102 98
103 if (newRequest.requestSequence > imgrequest.m_lastSequence) 99 if (imgrequest != null)
100 {
101 if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f)
104 { 102 {
103 //m_log.Debug("[TEX]: (CAN) ID=" + newRequest.RequestedAssetID);
105 104
106 imgrequest.m_lastSequence = newRequest.requestSequence; 105 try
107
108 //Check the priority
109
110 double priority = imgrequest.m_requestedPriority;
111 if (priority != newRequest.Priority)
112 { 106 {
113 //Remove the old priority 107 lock (m_priorityQueue)
114 m_priorities.Remove(imgrequest.m_designatedPriorityKey); 108 m_priorityQueue.Delete(imgrequest.m_priorityQueueHandle);
115 //Assign a new unique priority
116 imgrequest.m_requestedPriority = newRequest.Priority;
117 imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority);
118 } 109 }
110 catch (Exception) { }
111 }
112 else
113 {
114 //m_log.DebugFormat("[TEX]: (UPD) ID={0}: D={1}, S={2}, P={3}",
115 // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority);
119 116
120 //Update the requested discard level 117 //Check the packet sequence to make sure this isn't older than
121 imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; 118 //one we've already received
119 if (newRequest.requestSequence > imgrequest.m_lastSequence)
120 {
121 //Update the sequence number of the last RequestImage packet
122 imgrequest.m_lastSequence = newRequest.requestSequence;
122 123
123 //Update the requested packet number 124 //Update the requested discard level
124 imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; 125 imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel;
125 126
126 //Check if this will create an outstanding texture request 127 //Update the requested packet number
127 bool activated = imgrequest.m_completedSendAtCurrentDiscardLevel; 128 imgrequest.m_requestedPacketNumber = newRequest.PacketNumber;
128 //Run an update
129 129
130 imgrequest.RunUpdate(); 130 //Update the requested priority
131 imgrequest.m_requestedPriority = newRequest.Priority;
132 try
133 {
134 lock (m_priorityQueue)
135 m_priorityQueue.Replace(imgrequest.m_priorityQueueHandle, imgrequest);
136 }
137 catch (Exception)
138 {
139 imgrequest.m_priorityQueueHandle = null;
140 lock (m_priorityQueue)
141 m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest);
142 }
131 143
132 if (activated && !imgrequest.m_completedSendAtCurrentDiscardLevel && imgrequest.m_decoded) 144 //Run an update
133 { 145 imgrequest.RunUpdate();
134 Interlocked.Increment(ref m_outstandingtextures);
135 } 146 }
136 } 147 }
137 } 148 }
138 else 149 else
139 { 150 {
140 J2KImage imgrequest = new J2KImage(this); 151 if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f)
141 152 {
142 //Assign our missing substitute 153 //m_log.DebugFormat("[TEX]: (IGN) ID={0}: D={1}, S={2}, P={3}",
143 imgrequest.m_MissingSubstitute = m_missingsubstitute; 154 // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority);
155 }
156 else
157 {
158 //m_log.DebugFormat("[TEX]: (NEW) ID={0}: D={1}, S={2}, P={3}",
159 // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority);
144 160
145 //Assign our decoder module 161 imgrequest = new J2KImage(this);
146 imgrequest.m_j2kDecodeModule = m_j2kDecodeModule;
147 162
148 //Assign our asset cache module 163 //Assign our decoder module
149 imgrequest.m_assetCache = m_assetCache; 164 imgrequest.m_j2kDecodeModule = m_j2kDecodeModule;
150 165
151 //Assign a priority based on our request 166 //Assign our asset cache module
152 imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority); 167 imgrequest.m_assetCache = m_assetCache;
153 168
154 //Assign the requested discard level 169 //Assign the requested discard level
155 imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; 170 imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel;
156 171
157 //Assign the requested packet number 172 //Assign the requested packet number
158 imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; 173 imgrequest.m_requestedPacketNumber = newRequest.PacketNumber;
159 174
160 //Assign the requested priority 175 //Assign the requested priority
161 imgrequest.m_requestedPriority = newRequest.Priority; 176 imgrequest.m_requestedPriority = newRequest.Priority;
162 177
163 //Assign the asset uuid 178 //Assign the asset uuid
164 imgrequest.m_requestedUUID = newRequest.RequestedAssetID; 179 imgrequest.m_requestedUUID = newRequest.RequestedAssetID;
165 180
166 m_imagestore.Add(imgrequest.m_requestedUUID, imgrequest); 181 //Assign the requested priority
182 imgrequest.m_requestedPriority = newRequest.Priority;
167 183
168 //Run an update 184 //Add this download to the priority queue
169 imgrequest.RunUpdate(); 185 lock (m_priorityQueue)
186 m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest);
170 187
188 //Run an update
189 imgrequest.RunUpdate();
190 }
171 } 191 }
172 } 192 }
173 } 193 }
174 194
175 private double AssignPriority(UUID pAssetID, double pPriority) 195 public bool ProcessImageQueue(int count, int maxpack)
176 { 196 {
177 197 lock (this)
178 //First, find out if we can just assign directly
179 if (m_priorityresolver.ContainsKey((int)pPriority) == false)
180 { 198 {
181 m_priorities.Add((double)((int)pPriority), pAssetID); 199 //count is the number of textures we want to process in one go.
182 m_priorityresolver.Add((int)pPriority, 0); 200 //As part of this class re-write, that number will probably rise
183 return (double)((int)pPriority); 201 //since we're processing in a more efficient manner.
184 }
185 else
186 {
187 //Use the hash lookup goodness of a secondary dictionary to find a free slot
188 double mFreePriority = ((int)pPriority) + (doubleMinimum * (m_priorityresolver[(int)pPriority] + 1));
189 m_priorities[mFreePriority] = pAssetID;
190 m_priorityresolver[(int)pPriority]++;
191 return mFreePriority;
192 }
193 202
203 // this can happen during Close()
204 if (m_client == null)
205 return false;
194 206
207 int numCollected = 0;
195 208
196 } 209 //Calculate our threshold
210 int threshold;
211 if (m_lastloopprocessed == 0)
212 {
213 if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null)
214 return false;
215 //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below
216 threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300;
217 m_lastloopprocessed = DateTime.Now.Ticks;
218 }
219 else
220 {
221 double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond;
222 throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current;
197 223
198 public bool ProcessImageQueue(int count, int maxpack) 224 //Average of 1000 bytes per packet
199 { 225 throttleseconds = throttleseconds / 1000;
226
227 //Safe-zone multiplier of 2.0
228 threshold = (int)(throttleseconds * 2.0);
229 m_lastloopprocessed = DateTime.Now.Ticks;
230
231 }
232
233 if (m_client.PacketHandler == null)
234 return false;
200 235
201 // this can happen during Close() 236 if (m_client.PacketHandler.PacketQueue == null)
202 if (m_client == null) 237 return false;
203 return false; 238
204 239 if (threshold < 10)
205 //Count is the number of textures we want to process in one go. 240 threshold = 10;
206 //As part of this class re-write, that number will probably rise 241
207 //since we're processing in a more efficient manner. 242 //Uncomment this to see what the texture stack is doing
208 243 //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.getQueueCount(ThrottleOutPacketType.Texture).ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString());
209 int numCollected = 0; 244 if (true) //m_client.PacketHandler.PacketQueue.GetQueueCount(ThrottleOutPacketType.Texture) < threshold)
210
211 //Calculate our threshold
212 int threshold;
213 if (m_lastloopprocessed == 0)
214 {
215 if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null)
216 return false;
217 //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below
218 threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300;
219 m_lastloopprocessed = DateTime.Now.Ticks;
220 }
221 else
222 {
223 double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond;
224 throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current;
225
226 //Average of 1000 bytes per packet
227 throttleseconds = throttleseconds / 1000;
228
229 //Safe-zone multiplier of 2.0
230 threshold = (int)(throttleseconds * 2.0);
231 m_lastloopprocessed = DateTime.Now.Ticks;
232
233 }
234
235 if (threshold < 10)
236 {
237 threshold = 10;
238 }
239
240 if (m_client.PacketHandler == null)
241 return false;
242
243 if (m_client.PacketHandler.PacketQueue == null)
244 return false;
245
246 //First of all make sure our packet queue isn't above our threshold
247
248 //Uncomment this to see what the texture stack is doing
249 //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount.ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString());
250 if (m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount < threshold && m_outstandingtextures > 0)
251 {
252 bool justreset = false;
253
254 for (int x = m_priorities.Count - 1; x > -1; x--)
255 { 245 {
256 246 while (m_priorityQueue.Count > 0)
257 J2KImage imagereq = m_imagestore[m_priorities.Values[x]];
258 if (imagereq.m_decoded == true && !imagereq.m_completedSendAtCurrentDiscardLevel)
259 { 247 {
260 numCollected++; 248 J2KImage imagereq = null;
261 //SendPackets will send up to ten packets per cycle 249 lock (m_priorityQueue)
262 if (imagereq.SendPackets(m_client, maxpack)) 250 imagereq = m_priorityQueue.FindMax();
251
252 if (imagereq.m_decoded == true)
263 { 253 {
264 //Send complete 254 // we need to test this here now that we are dropping assets
265 if (!imagereq.m_completedSendAtCurrentDiscardLevel) 255 if (!imagereq.m_hasasset)
266 { 256 {
267 imagereq.m_completedSendAtCurrentDiscardLevel = true; 257 m_log.WarnFormat("[LLIMAGE MANAGER]: Re-requesting the image asset {0}", imagereq.m_requestedUUID);
268 Interlocked.Decrement(ref m_outstandingtextures); 258 imagereq.RunUpdate();
269 //Re-assign priority to bottom 259 continue;
270 //Remove the old priority 260 }
271 m_priorities.Remove(imagereq.m_designatedPriorityKey); 261
272 int lowest; 262 ++numCollected;
273 if (m_priorities.Count > 0) 263
274 { 264 //SendPackets will send up to ten packets per cycle
275 lowest = (int)m_priorities.Keys[0]; 265 if (imagereq.SendPackets(m_client, maxpack))
276 lowest--; 266 {
277 } 267 // Send complete. Destroy any knowledge of this transfer
278 else 268 try
279 { 269 {
280 lowest = -10000; 270 lock (m_priorityQueue)
281 } 271 m_priorityQueue.Delete(imagereq.m_priorityQueueHandle);
282 m_priorities.Add((double)lowest, imagereq.m_requestedUUID);
283 imagereq.m_designatedPriorityKey = (double)lowest;
284 if (m_priorityresolver.ContainsKey((int)lowest))
285 {
286 m_priorityresolver[(int)lowest]++;
287 }
288 else
289 {
290 m_priorityresolver.Add((int)lowest, 0);
291 } 272 }
273 catch (Exception) { }
292 } 274 }
293 } 275 }
276
294 if (numCollected == count) 277 if (numCollected == count)
295 {
296 break; 278 break;
297 }
298 }
299 if (numCollected == count || m_outstandingtextures == 0)
300 break;
301 if (numCollected % m_outstandingtextures == 0 && !justreset)
302 {
303 //We've gotten as much as we can from the stack,
304 //reset to the top so that we can send MOAR DATA (nomnomnom)!
305 x = m_priorities.Count - 1;
306
307 justreset = true; //prevents us from getting stuck in a loop
308 } 279 }
309 } 280 }
310 }
311 281
312 return m_outstandingtextures != 0; 282 return m_priorityQueue.Count > 0;
283 }
313 } 284 }
314 285
315 //Faux destructor 286 //Faux destructor
316 public void Close() 287 public void Close()
317 { 288 {
318 289
319 m_shuttingdown = true; 290 m_shuttingdown = true;
320 m_j2kDecodeModule = null; 291 m_j2kDecodeModule = null;
321 m_assetCache = null; 292 m_assetCache = null;
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
index eaf8f60..37f6ca7 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
@@ -129,6 +129,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
129 // 129 //
130 public event PacketStats OnPacketStats; 130 public event PacketStats OnPacketStats;
131 public event PacketDrop OnPacketDrop; 131 public event PacketDrop OnPacketDrop;
132 public event QueueEmpty OnQueueEmpty;
132 133
133 134
134 //private SynchronizeClientHandler m_SynchronizeClient = null; 135 //private SynchronizeClientHandler m_SynchronizeClient = null;
@@ -172,13 +173,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
172 173
173 m_PacketQueue = new LLPacketQueue(client.AgentId, userSettings); 174 m_PacketQueue = new LLPacketQueue(client.AgentId, userSettings);
174 175
176 m_PacketQueue.OnQueueEmpty += TriggerOnQueueEmpty;
177
175 m_AckTimer.Elapsed += AckTimerElapsed; 178 m_AckTimer.Elapsed += AckTimerElapsed;
176 m_AckTimer.Start(); 179 m_AckTimer.Start();
177 } 180 }
178 181
179 public void Stop() 182 public void Dispose()
180 { 183 {
181 m_AckTimer.Stop(); 184 m_AckTimer.Stop();
185 m_AckTimer.Close();
182 186
183 m_PacketQueue.Enqueue(null); 187 m_PacketQueue.Enqueue(null);
184 m_PacketQueue.Close(); 188 m_PacketQueue.Close();
@@ -768,6 +772,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
768 handlerPacketDrop(packet, id); 772 handlerPacketDrop(packet, id);
769 } 773 }
770 774
775 private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
776 {
777 QueueEmpty handlerQueueEmpty = OnQueueEmpty;
778
779 if (handlerQueueEmpty == null)
780 return;
781
782 handlerQueueEmpty(queue);
783 }
784
771 // Convert the packet to bytes and stuff it onto the send queue 785 // Convert the packet to bytes and stuff it onto the send queue
772 // 786 //
773 public void ProcessOutPacket(LLQueItem item) 787 public void ProcessOutPacket(LLQueItem item)
@@ -849,5 +863,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
849 m_PacketQueue.Close(); 863 m_PacketQueue.Close();
850 Thread.CurrentThread.Abort(); 864 Thread.CurrentThread.Abort();
851 } 865 }
866
867 public int GetQueueCount(ThrottleOutPacketType queue)
868 {
869 return m_PacketQueue.GetQueueCount(queue);
870 }
852 } 871 }
853} 872}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
index c427870..ff739c5 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
@@ -39,7 +39,7 @@ using Timer=System.Timers.Timer;
39 39
40namespace OpenSim.Region.ClientStack.LindenUDP 40namespace OpenSim.Region.ClientStack.LindenUDP
41{ 41{
42 public class LLPacketQueue : IPullStatsProvider 42 public class LLPacketQueue : IPullStatsProvider, IDisposable
43 { 43 {
44 private static readonly ILog m_log 44 private static readonly ILog m_log
45 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 45 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -62,6 +62,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
62 private Queue<LLQueItem> TextureOutgoingPacketQueue; 62 private Queue<LLQueItem> TextureOutgoingPacketQueue;
63 private Queue<LLQueItem> AssetOutgoingPacketQueue; 63 private Queue<LLQueItem> AssetOutgoingPacketQueue;
64 64
65 private List<ThrottleOutPacketType> Empty = new List<ThrottleOutPacketType>();
66 // m_log.Info("[THROTTLE]: Entering Throttle");
65 // private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>(); 67 // private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>();
66 // private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>(); 68 // private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>();
67 69
@@ -85,26 +87,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
85 87
86 private Dictionary<uint,int> contents = new Dictionary<uint, int>(); 88 private Dictionary<uint,int> contents = new Dictionary<uint, int>();
87 89
88 /// <summary>
89 /// The number of packets in the OutgoingPacketQueue
90 ///
91 /// </summary>
92 internal int TextureOutgoingPacketQueueCount
93 {
94 get
95 {
96 if (TextureOutgoingPacketQueue == null)
97 return 0;
98 return TextureOutgoingPacketQueue.Count;
99 }
100 }
101
102 // private long LastThrottle; 90 // private long LastThrottle;
103 // private long ThrottleInterval; 91 // private long ThrottleInterval;
104 private Timer throttleTimer; 92 private Timer throttleTimer;
105 93
106 private UUID m_agentId; 94 private UUID m_agentId;
107 95
96 public event QueueEmpty OnQueueEmpty;
97
108 public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings) 98 public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings)
109 { 99 {
110 // While working on this, the BlockingQueue had me fooled for a bit. 100 // While working on this, the BlockingQueue had me fooled for a bit.
@@ -210,28 +200,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
210 switch (item.throttleType & ThrottleOutPacketType.TypeMask) 200 switch (item.throttleType & ThrottleOutPacketType.TypeMask)
211 { 201 {
212 case ThrottleOutPacketType.Resend: 202 case ThrottleOutPacketType.Resend:
213 ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item); 203 ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item, ThrottleOutPacketType.Resend);
214 break; 204 break;
215 case ThrottleOutPacketType.Texture: 205 case ThrottleOutPacketType.Texture:
216 ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item); 206 ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item, ThrottleOutPacketType.Texture);
217 break; 207 break;
218 case ThrottleOutPacketType.Task: 208 case ThrottleOutPacketType.Task:
219 if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0) 209 if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0)
220 ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item); 210 ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item, ThrottleOutPacketType.Task);
221 else 211 else
222 ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item); 212 ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item, ThrottleOutPacketType.Task);
223 break; 213 break;
224 case ThrottleOutPacketType.Land: 214 case ThrottleOutPacketType.Land:
225 ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item); 215 ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item, ThrottleOutPacketType.Land);
226 break; 216 break;
227 case ThrottleOutPacketType.Asset: 217 case ThrottleOutPacketType.Asset:
228 ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item); 218 ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item, ThrottleOutPacketType.Asset);
229 break; 219 break;
230 case ThrottleOutPacketType.Cloud: 220 case ThrottleOutPacketType.Cloud:
231 ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item); 221 ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item, ThrottleOutPacketType.Cloud);
232 break; 222 break;
233 case ThrottleOutPacketType.Wind: 223 case ThrottleOutPacketType.Wind:
234 ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item); 224 ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item, ThrottleOutPacketType.Wind);
235 break; 225 break;
236 226
237 default: 227 default:
@@ -293,30 +283,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP
293 if (LandOutgoingPacketQueue.Count > 0) 283 if (LandOutgoingPacketQueue.Count > 0)
294 { 284 {
295 SendQueue.Enqueue(LandOutgoingPacketQueue.Dequeue()); 285 SendQueue.Enqueue(LandOutgoingPacketQueue.Dequeue());
286 TriggerOnQueueEmpty(ThrottleOutPacketType.Land);
296 } 287 }
297 if (WindOutgoingPacketQueue.Count > 0) 288 if (WindOutgoingPacketQueue.Count > 0)
298 { 289 {
299 SendQueue.Enqueue(WindOutgoingPacketQueue.Dequeue()); 290 SendQueue.Enqueue(WindOutgoingPacketQueue.Dequeue());
291 TriggerOnQueueEmpty(ThrottleOutPacketType.Wind);
300 } 292 }
301 if (CloudOutgoingPacketQueue.Count > 0) 293 if (CloudOutgoingPacketQueue.Count > 0)
302 { 294 {
303 SendQueue.Enqueue(CloudOutgoingPacketQueue.Dequeue()); 295 SendQueue.Enqueue(CloudOutgoingPacketQueue.Dequeue());
296 TriggerOnQueueEmpty(ThrottleOutPacketType.Cloud);
304 } 297 }
298 bool tasksSent = false;
305 if (TaskOutgoingPacketQueue.Count > 0) 299 if (TaskOutgoingPacketQueue.Count > 0)
306 { 300 {
301 tasksSent = true;
307 SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue()); 302 SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue());
308 } 303 }
309 if (TaskLowpriorityPacketQueue.Count > 0) 304 if (TaskLowpriorityPacketQueue.Count > 0)
310 { 305 {
306 tasksSent = true;
311 SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue()); 307 SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue());
312 } 308 }
309 if (tasksSent)
310 {
311 TriggerOnQueueEmpty(ThrottleOutPacketType.Task);
312 }
313 if (TextureOutgoingPacketQueue.Count > 0) 313 if (TextureOutgoingPacketQueue.Count > 0)
314 { 314 {
315 SendQueue.Enqueue(TextureOutgoingPacketQueue.Dequeue()); 315 SendQueue.Enqueue(TextureOutgoingPacketQueue.Dequeue());
316 TriggerOnQueueEmpty(ThrottleOutPacketType.Texture);
316 } 317 }
317 if (AssetOutgoingPacketQueue.Count > 0) 318 if (AssetOutgoingPacketQueue.Count > 0)
318 { 319 {
319 SendQueue.Enqueue(AssetOutgoingPacketQueue.Dequeue()); 320 SendQueue.Enqueue(AssetOutgoingPacketQueue.Dequeue());
321 TriggerOnQueueEmpty(ThrottleOutPacketType.Asset);
320 } 322 }
321 } 323 }
322 // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); 324 // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets");
@@ -342,11 +344,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
342 344
343 public void Close() 345 public void Close()
344 { 346 {
347 Dispose();
348 }
349
350 public void Dispose()
351 {
345 Flush(); 352 Flush();
346 WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby 353 WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby
347 354
348 m_enabled = false; 355 m_enabled = false;
349 throttleTimer.Stop(); 356 throttleTimer.Stop();
357 throttleTimer.Close();
350 358
351 if (StatsManager.SimExtraStats != null) 359 if (StatsManager.SimExtraStats != null)
352 { 360 {
@@ -388,6 +396,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
388 396
389 int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once. 397 int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once.
390 int throttleLoops = 0; 398 int throttleLoops = 0;
399 List<ThrottleOutPacketType> e;
391 400
392 // We're going to dequeue all of the saved up packets until 401 // We're going to dequeue all of the saved up packets until
393 // we've hit the throttle limit or there's no more packets to send 402 // we've hit the throttle limit or there's no more packets to send
@@ -399,7 +408,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
399 bool qchanged = true; 408 bool qchanged = true;
400 409
401 ResetCounters(); 410 ResetCounters();
402 // m_log.Info("[THROTTLE]: Entering Throttle"); 411
403 while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops) 412 while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops)
404 { 413 {
405 qchanged = false; // We will break out of the loop if no work was accomplished 414 qchanged = false; // We will break out of the loop if no work was accomplished
@@ -425,6 +434,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
425 TotalThrottle.AddBytes(qpack.Length); 434 TotalThrottle.AddBytes(qpack.Length);
426 LandThrottle.AddBytes(qpack.Length); 435 LandThrottle.AddBytes(qpack.Length);
427 qchanged = true; 436 qchanged = true;
437
438 if (LandOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Land))
439 Empty.Add(ThrottleOutPacketType.Land);
428 } 440 }
429 441
430 if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit()) 442 if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit())
@@ -435,6 +447,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
435 TotalThrottle.AddBytes(qpack.Length); 447 TotalThrottle.AddBytes(qpack.Length);
436 WindThrottle.AddBytes(qpack.Length); 448 WindThrottle.AddBytes(qpack.Length);
437 qchanged = true; 449 qchanged = true;
450
451 if (WindOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Wind))
452 Empty.Add(ThrottleOutPacketType.Wind);
438 } 453 }
439 454
440 if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit()) 455 if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit())
@@ -445,6 +460,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
445 TotalThrottle.AddBytes(qpack.Length); 460 TotalThrottle.AddBytes(qpack.Length);
446 CloudThrottle.AddBytes(qpack.Length); 461 CloudThrottle.AddBytes(qpack.Length);
447 qchanged = true; 462 qchanged = true;
463
464 if (CloudOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Cloud))
465 Empty.Add(ThrottleOutPacketType.Cloud);
448 } 466 }
449 467
450 if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit()) 468 if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit())
@@ -464,6 +482,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
464 TotalThrottle.AddBytes(qpack.Length); 482 TotalThrottle.AddBytes(qpack.Length);
465 TaskThrottle.AddBytes(qpack.Length); 483 TaskThrottle.AddBytes(qpack.Length);
466 qchanged = true; 484 qchanged = true;
485
486 if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Task))
487 Empty.Add(ThrottleOutPacketType.Task);
467 } 488 }
468 489
469 if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit()) 490 if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit())
@@ -474,6 +495,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
474 TotalThrottle.AddBytes(qpack.Length); 495 TotalThrottle.AddBytes(qpack.Length);
475 TextureThrottle.AddBytes(qpack.Length); 496 TextureThrottle.AddBytes(qpack.Length);
476 qchanged = true; 497 qchanged = true;
498
499 if (TextureOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Texture))
500 Empty.Add(ThrottleOutPacketType.Texture);
477 } 501 }
478 502
479 if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit()) 503 if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit())
@@ -484,12 +508,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP
484 TotalThrottle.AddBytes(qpack.Length); 508 TotalThrottle.AddBytes(qpack.Length);
485 AssetThrottle.AddBytes(qpack.Length); 509 AssetThrottle.AddBytes(qpack.Length);
486 qchanged = true; 510 qchanged = true;
511
512 if (AssetOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Asset))
513 Empty.Add(ThrottleOutPacketType.Asset);
487 } 514 }
488 } 515 }
489 // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); 516 // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets");
517
518 e = new List<ThrottleOutPacketType>(Empty);
519 Empty.Clear();
520 }
521
522 foreach (ThrottleOutPacketType t in e)
523 {
524 if (GetQueueCount(t) == 0)
525 TriggerOnQueueEmpty(t);
490 } 526 }
491 } 527 }
492 528
529 private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
530 {
531 QueueEmpty handlerQueueEmpty = OnQueueEmpty;
532
533 if (handlerQueueEmpty == null)
534 return;
535
536 handlerQueueEmpty(queue);
537 }
538
493 private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e) 539 private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e)
494 { 540 {
495 // just to change the signature, and that ProcessThrottle 541 // just to change the signature, and that ProcessThrottle
@@ -497,7 +543,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
497 ProcessThrottle(); 543 ProcessThrottle();
498 } 544 }
499 545
500 private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item) 546 private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item, ThrottleOutPacketType itemType)
501 { 547 {
502 // The idea.. is if the packet throttle queues are empty 548 // The idea.. is if the packet throttle queues are empty
503 // and the client is under throttle for the type. Queue 549 // and the client is under throttle for the type. Queue
@@ -513,6 +559,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
513 throttle.AddBytes(item.Length); 559 throttle.AddBytes(item.Length);
514 TotalThrottle.AddBytes(item.Length); 560 TotalThrottle.AddBytes(item.Length);
515 SendQueue.Enqueue(item); 561 SendQueue.Enqueue(item);
562 lock (this)
563 {
564 if (!Empty.Contains(itemType))
565 Empty.Add(itemType);
566 }
516 } 567 }
517 catch (Exception e) 568 catch (Exception e)
518 { 569 {
@@ -698,5 +749,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
698 { 749 {
699 get { return throttleMultiplier; } 750 get { return throttleMultiplier; }
700 } 751 }
752
753 public int GetQueueCount(ThrottleOutPacketType queue)
754 {
755 switch (queue)
756 {
757 case ThrottleOutPacketType.Land:
758 return LandOutgoingPacketQueue.Count;
759 case ThrottleOutPacketType.Wind:
760 return WindOutgoingPacketQueue.Count;
761 case ThrottleOutPacketType.Cloud:
762 return CloudOutgoingPacketQueue.Count;
763 case ThrottleOutPacketType.Task:
764 return TaskOutgoingPacketQueue.Count;
765 case ThrottleOutPacketType.Texture:
766 return TextureOutgoingPacketQueue.Count;
767 case ThrottleOutPacketType.Asset:
768 return AssetOutgoingPacketQueue.Count;
769 }
770
771 return 0;
772 }
701 } 773 }
702} 774}
diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs
index 1fdb003..a0f359b 100644
--- a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs
+++ b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs
@@ -34,8 +34,8 @@ using System.Threading;
34using log4net; 34using log4net;
35using Nini.Config; 35using Nini.Config;
36using OpenMetaverse; 36using OpenMetaverse;
37using OpenMetaverse.Assets;
38using OpenMetaverse.Imaging; 37using OpenMetaverse.Imaging;
38using CSJ2K;
39using OpenSim.Framework; 39using OpenSim.Framework;
40using OpenSim.Region.Framework.Interfaces; 40using OpenSim.Region.Framework.Interfaces;
41using OpenSim.Region.Framework.Scenes; 41using OpenSim.Region.Framework.Scenes;
@@ -43,31 +43,25 @@ using OpenSim.Services.Interfaces;
43 43
44namespace OpenSim.Region.CoreModules.Agent.TextureSender 44namespace OpenSim.Region.CoreModules.Agent.TextureSender
45{ 45{
46 public delegate void J2KDecodeDelegate(UUID AssetId); 46 public delegate void J2KDecodeDelegate(UUID assetID);
47 47
48 public class J2KDecoderModule : IRegionModule, IJ2KDecoder 48 public class J2KDecoderModule : IRegionModule, IJ2KDecoder
49 { 49 {
50 #region IRegionModule Members 50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51 51
52 private static readonly ILog m_log 52 /// <summary>Temporarily holds deserialized layer data information in memory</summary>
53 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 53 private readonly ExpiringCache<UUID, OpenJPEG.J2KLayerInfo[]> m_decodedCache = new ExpiringCache<UUID,OpenJPEG.J2KLayerInfo[]>();
54 /// <summary>List of client methods to notify of results of decode</summary>
55 private readonly Dictionary<UUID, List<DecodedCallback>> m_notifyList = new Dictionary<UUID, List<DecodedCallback>>();
56 /// <summary>Cache that will store decoded JPEG2000 layer boundary data</summary>
57 private IImprovedAssetCache m_cache;
58 /// <summary>Reference to a scene (doesn't matter which one as long as it can load the cache module)</summary>
59 private Scene m_scene;
54 60
55 /// <summary> 61 #region IRegionModule
56 /// Cached Decoded Layers
57 /// </summary>
58 private readonly Dictionary<UUID, OpenJPEG.J2KLayerInfo[]> m_cacheddecode = new Dictionary<UUID, OpenJPEG.J2KLayerInfo[]>();
59 private bool OpenJpegFail = false;
60 private string CacheFolder = Util.dataDir() + "/j2kDecodeCache";
61 private int CacheTimeout = 720;
62 private J2KDecodeFileCache fCache = null;
63 private Thread CleanerThread = null;
64 private IAssetService AssetService = null;
65 private Scene m_Scene = null;
66 62
67 /// <summary> 63 public string Name { get { return "J2KDecoderModule"; } }
68 /// List of client methods to notify of results of decode 64 public bool IsSharedModule { get { return true; } }
69 /// </summary>
70 private readonly Dictionary<UUID, List<DecodedCallback>> m_notifyList = new Dictionary<UUID, List<DecodedCallback>>();
71 65
72 public J2KDecoderModule() 66 public J2KDecoderModule()
73 { 67 {
@@ -75,630 +69,267 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender
75 69
76 public void Initialise(Scene scene, IConfigSource source) 70 public void Initialise(Scene scene, IConfigSource source)
77 { 71 {
78 if (m_Scene == null) 72 if (m_scene == null)
79 m_Scene = scene; 73 m_scene = scene;
80
81 IConfig j2kConfig = source.Configs["J2KDecoder"];
82 if (j2kConfig != null)
83 {
84 CacheFolder = j2kConfig.GetString("CacheDir", CacheFolder);
85 CacheTimeout = j2kConfig.GetInt("CacheTimeout", CacheTimeout);
86 }
87
88 if (fCache == null)
89 fCache = new J2KDecodeFileCache(CacheFolder, CacheTimeout);
90 74
91 scene.RegisterModuleInterface<IJ2KDecoder>(this); 75 scene.RegisterModuleInterface<IJ2KDecoder>(this);
92
93 if (CleanerThread == null && CacheTimeout != 0)
94 {
95 CleanerThread = new Thread(CleanCache);
96 CleanerThread.Name = "J2KCleanerThread";
97 CleanerThread.IsBackground = true;
98 CleanerThread.Start();
99 }
100 } 76 }
101 77
102 public void PostInitialise() 78 public void PostInitialise()
103 { 79 {
104 AssetService = m_Scene.AssetService; 80 m_cache = m_scene.RequestModuleInterface<IImprovedAssetCache>();
105 } 81 }
106 82
107 public void Close() 83 public void Close()
108 { 84 {
109
110 }
111
112 public string Name
113 {
114 get { return "J2KDecoderModule"; }
115 }
116
117 public bool IsSharedModule
118 {
119 get { return true; }
120 } 85 }
121 86
122 #endregion 87 #endregion IRegionModule
123
124 #region IJ2KDecoder Members
125 88
89 #region IJ2KDecoder
126 90
127 public void decode(UUID AssetId, byte[] assetData, DecodedCallback decodedReturn) 91 public void BeginDecode(UUID assetID, byte[] j2kData, DecodedCallback callback)
128 { 92 {
129 // Dummy for if decoding fails. 93 OpenJPEG.J2KLayerInfo[] result;
130 OpenJPEG.J2KLayerInfo[] result = new OpenJPEG.J2KLayerInfo[0];
131
132 // Check if it's cached
133 bool cached = false;
134 lock (m_cacheddecode)
135 {
136 if (m_cacheddecode.ContainsKey(AssetId))
137 {
138 cached = true;
139 result = m_cacheddecode[AssetId];
140 }
141 }
142 94
143 // If it's cached, return the cached results 95 // If it's cached, return the cached results
144 if (cached) 96 if (m_decodedCache.TryGetValue(assetID, out result))
145 { 97 {
146 decodedReturn(AssetId, result); 98 callback(assetID, result);
147 } 99 }
148 else 100 else
149 { 101 {
150 // not cached, so we need to decode it 102 // Not cached, we need to decode it.
151 // Add to notify list and start decoding. 103 // Add to notify list and start decoding.
152 // Next request for this asset while it's decoding will only be added to the notify list 104 // Next request for this asset while it's decoding will only be added to the notify list
153 // once this is decoded, requests will be served from the cache and all clients in the notifylist will be updated 105 // once this is decoded, requests will be served from the cache and all clients in the notifylist will be updated
154 bool decode = false; 106 bool decode = false;
155 lock (m_notifyList) 107 lock (m_notifyList)
156 { 108 {
157 if (m_notifyList.ContainsKey(AssetId)) 109 if (m_notifyList.ContainsKey(assetID))
158 { 110 {
159 m_notifyList[AssetId].Add(decodedReturn); 111 m_notifyList[assetID].Add(callback);
160 } 112 }
161 else 113 else
162 { 114 {
163 List<DecodedCallback> notifylist = new List<DecodedCallback>(); 115 List<DecodedCallback> notifylist = new List<DecodedCallback>();
164 notifylist.Add(decodedReturn); 116 notifylist.Add(callback);
165 m_notifyList.Add(AssetId, notifylist); 117 m_notifyList.Add(assetID, notifylist);
166 decode = true; 118 decode = true;
167 } 119 }
168 } 120 }
121
169 // Do Decode! 122 // Do Decode!
170 if (decode) 123 if (decode)
171 { 124 DoJ2KDecode(assetID, j2kData);
172 doJ2kDecode(AssetId, assetData);
173 }
174 } 125 }
175 } 126 }
176 127
177 /// <summary> 128 /// <summary>
178 /// Provides a synchronous decode so that caller can be assured that this executes before the next line 129 /// Provides a synchronous decode so that caller can be assured that this executes before the next line
179 /// </summary> 130 /// </summary>
180 /// <param name="AssetId"></param> 131 /// <param name="assetID"></param>
181 /// <param name="j2kdata"></param> 132 /// <param name="j2kData"></param>
182 public void syncdecode(UUID AssetId, byte[] j2kdata) 133 public void Decode(UUID assetID, byte[] j2kData)
183 { 134 {
184 doJ2kDecode(AssetId, j2kdata); 135 DoJ2KDecode(assetID, j2kData);
185 } 136 }
186 137
187 #endregion 138 #endregion IJ2KDecoder
188 139
189 /// <summary> 140 /// <summary>
190 /// Decode Jpeg2000 Asset Data 141 /// Decode Jpeg2000 Asset Data
191 /// </summary> 142 /// </summary>
192 /// <param name="AssetId">UUID of Asset</param> 143 /// <param name="assetID">UUID of Asset</param>
193 /// <param name="j2kdata">Byte Array Asset Data </param> 144 /// <param name="j2kData">JPEG2000 data</param>
194 private void doJ2kDecode(UUID AssetId, byte[] j2kdata) 145 private void DoJ2KDecode(UUID assetID, byte[] j2kData)
195 { 146 {
196 int DecodeTime = 0; 147 int DecodeTime = 0;
197 DecodeTime = Environment.TickCount; 148 DecodeTime = Environment.TickCount;
198 OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[0]; // Dummy result for if it fails. Informs that there's only full quality 149 OpenJPEG.J2KLayerInfo[] layers;
199 150
200 if (!OpenJpegFail) 151 if (!TryLoadCacheForAsset(assetID, out layers))
201 { 152 {
202 if (!fCache.TryLoadCacheForAsset(AssetId, out layers)) 153 try
203 { 154 {
204 try 155 List<int> layerStarts = CSJ2K.J2kImage.GetLayerBoundaries(new MemoryStream(j2kData));
156
157 if (layerStarts != null && layerStarts.Count > 0)
205 { 158 {
159 layers = new OpenJPEG.J2KLayerInfo[layerStarts.Count];
206 160
207 AssetTexture texture = new AssetTexture(AssetId, j2kdata); 161 for (int i = 0; i < layerStarts.Count; i++)
208 if (texture.DecodeLayerBoundaries())
209 { 162 {
210 bool sane = true; 163 OpenJPEG.J2KLayerInfo layer = new OpenJPEG.J2KLayerInfo();
211 164
212 // Sanity check all of the layers 165 if (i == 0)
213 for (int i = 0; i < texture.LayerInfo.Length; i++) 166 layer.Start = 0;
214 {
215 if (texture.LayerInfo[i].End > texture.AssetData.Length)
216 {
217 sane = false;
218 break;
219 }
220 }
221
222 if (sane)
223 {
224 layers = texture.LayerInfo;
225 fCache.SaveFileCacheForAsset(AssetId, layers);
226
227
228 // Write out decode time
229 m_log.InfoFormat("[J2KDecoderModule]: {0} Decode Time: {1}", Environment.TickCount - DecodeTime,
230 AssetId);
231
232 }
233 else 167 else
234 { 168 layer.Start = layerStarts[i];
235 m_log.WarnFormat(
236 "[J2KDecoderModule]: JPEG2000 texture decoding succeeded, but sanity check failed for {0}",
237 AssetId);
238 }
239 }
240
241 else
242 {
243 /*
244 Random rnd = new Random();
245 // scramble ends for test
246 for (int i = 0; i < texture.LayerInfo.Length; i++)
247 {
248 texture.LayerInfo[i].End = rnd.Next(999999);
249 }
250 */
251
252 // Try to do some heuristics error correction! Yeah.
253 bool sane2Heuristics = true;
254
255
256 if (texture.Image == null)
257 sane2Heuristics = false;
258
259 if (texture.LayerInfo == null)
260 sane2Heuristics = false;
261
262 if (sane2Heuristics)
263 {
264
265 169
266 if (texture.LayerInfo.Length == 0) 170 if (i == layerStarts.Count - 1)
267 sane2Heuristics = false; 171 layer.End = j2kData.Length;
268 }
269
270 if (sane2Heuristics)
271 {
272 // Last layer start is less then the end of the file and last layer start is greater then 0
273 if (texture.LayerInfo[texture.LayerInfo.Length - 1].Start < texture.AssetData.Length && texture.LayerInfo[texture.LayerInfo.Length - 1].Start > 0)
274 {
275 }
276 else
277 {
278 sane2Heuristics = false;
279 }
280
281 }
282
283 if (sane2Heuristics)
284 {
285 int start = 0;
286
287 // try to fix it by using consistant data in the start field
288 for (int i = 0; i < texture.LayerInfo.Length; i++)
289 {
290 if (i == 0)
291 start = 0;
292
293 if (i == texture.LayerInfo.Length - 1)
294 texture.LayerInfo[i].End = texture.AssetData.Length;
295 else
296 texture.LayerInfo[i].End = texture.LayerInfo[i + 1].Start - 1;
297
298 // in this case, the end of the next packet is less then the start of the last packet
299 // after we've attempted to fix it which means the start of the last packet is borked
300 // there's no recovery from this
301 if (texture.LayerInfo[i].End < start)
302 {
303 sane2Heuristics = false;
304 break;
305 }
306
307 if (texture.LayerInfo[i].End < 0 || texture.LayerInfo[i].End > texture.AssetData.Length)
308 {
309 sane2Heuristics = false;
310 break;
311 }
312
313 if (texture.LayerInfo[i].Start < 0 || texture.LayerInfo[i].Start > texture.AssetData.Length)
314 {
315 sane2Heuristics = false;
316 break;
317 }
318
319 start = texture.LayerInfo[i].Start;
320 }
321 }
322
323 if (sane2Heuristics)
324 {
325 layers = texture.LayerInfo;
326 fCache.SaveFileCacheForAsset(AssetId, layers);
327
328
329 // Write out decode time
330 m_log.InfoFormat("[J2KDecoderModule]: HEURISTICS SUCCEEDED {0} Decode Time: {1}", Environment.TickCount - DecodeTime,
331 AssetId);
332
333 }
334 else 172 else
335 { 173 layer.End = layerStarts[i + 1] - 1;
336 m_log.WarnFormat("[J2KDecoderModule]: JPEG2000 texture decoding failed for {0}. Is this a texture? is it J2K?", AssetId); 174
337 } 175 layers[i] = layer;
338 } 176 }
339 texture = null; // dereference and dispose of ManagedImage
340 }
341 catch (DllNotFoundException)
342 {
343 m_log.Error(
344 "[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!");
345 OpenJpegFail = true;
346 }
347 catch (Exception ex)
348 {
349 m_log.WarnFormat(
350 "[J2KDecoderModule]: JPEG2000 texture decoding threw an exception for {0}, {1}",
351 AssetId, ex);
352 } 177 }
353 } 178 }
354 179 catch (Exception ex)
355 } 180 {
181 m_log.Warn("[J2KDecoderModule]: CSJ2K threw an exception decoding texture " + assetID + ": " + ex.Message);
182 }
356 183
357 // Cache Decoded layers 184 if (layers == null || layers.Length == 0)
358 lock (m_cacheddecode) 185 {
359 { 186 m_log.Warn("[J2KDecoderModule]: Failed to decode layer data for texture " + assetID + ", guessing sane defaults");
360 if (m_cacheddecode.ContainsKey(AssetId)) 187 // Layer decoding completely failed. Guess at sane defaults for the layer boundaries
361 m_cacheddecode.Remove(AssetId); 188 layers = CreateDefaultLayers(j2kData.Length);
362 m_cacheddecode.Add(AssetId, layers); 189 }
363 190
191 // Cache Decoded layers
192 SaveFileCacheForAsset(assetID, layers);
364 } 193 }
365 194
366 // Notify Interested Parties 195 // Notify Interested Parties
367 lock (m_notifyList) 196 lock (m_notifyList)
368 { 197 {
369 if (m_notifyList.ContainsKey(AssetId)) 198 if (m_notifyList.ContainsKey(assetID))
370 { 199 {
371 foreach (DecodedCallback d in m_notifyList[AssetId]) 200 foreach (DecodedCallback d in m_notifyList[assetID])
372 { 201 {
373 if (d != null) 202 if (d != null)
374 d.DynamicInvoke(AssetId, layers); 203 d.DynamicInvoke(assetID, layers);
375 } 204 }
376 m_notifyList.Remove(AssetId); 205 m_notifyList.Remove(assetID);
377 } 206 }
378 } 207 }
379 } 208 }
380
381 private void CleanCache()
382 {
383 m_log.Info("[J2KDecoderModule]: Cleaner thread started");
384
385 while (true)
386 {
387 if (AssetService != null)
388 fCache.ScanCacheFiles(RedecodeTexture);
389 209
390 System.Threading.Thread.Sleep(600000); 210 private OpenJPEG.J2KLayerInfo[] CreateDefaultLayers(int j2kLength)
391 }
392 }
393
394 private void RedecodeTexture(UUID assetID)
395 { 211 {
396 AssetBase texture = AssetService.Get(assetID.ToString()); 212 OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[5];
397 if (texture == null) 213
398 return; 214 for (int i = 0; i < layers.Length; i++)
399 215 layers[i] = new OpenJPEG.J2KLayerInfo();
400 doJ2kDecode(assetID, texture.Data); 216
217 // These default layer sizes are based on a small sampling of real-world texture data
218 // with extra padding thrown in for good measure. This is a worst case fallback plan
219 // and may not gracefully handle all real world data
220 layers[0].Start = 0;
221 layers[1].Start = (int)((float)j2kLength * 0.02f);
222 layers[2].Start = (int)((float)j2kLength * 0.05f);
223 layers[3].Start = (int)((float)j2kLength * 0.20f);
224 layers[4].Start = (int)((float)j2kLength * 0.50f);
225
226 layers[0].End = layers[1].Start - 1;
227 layers[1].End = layers[2].Start - 1;
228 layers[2].End = layers[3].Start - 1;
229 layers[3].End = layers[4].Start - 1;
230 layers[4].End = j2kLength;
231
232 return layers;
401 } 233 }
402 }
403 234
404 public class J2KDecodeFileCache 235 private void SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers)
405 {
406 private readonly string m_cacheDecodeFolder;
407 private readonly int m_cacheTimeout;
408 private bool enabled = true;
409
410 private static readonly ILog m_log
411 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
412
413 /// <summary>
414 /// Creates a new instance of a file cache
415 /// </summary>
416 /// <param name="pFolder">base folder for the cache. Will be created if it doesn't exist</param>
417 public J2KDecodeFileCache(string pFolder, int timeout)
418 { 236 {
419 m_cacheDecodeFolder = pFolder; 237 m_decodedCache.AddOrUpdate(AssetId, Layers, TimeSpan.FromMinutes(10));
420 m_cacheTimeout = timeout;
421 if (!Directory.Exists(pFolder))
422 {
423 Createj2KCacheFolder(pFolder);
424 }
425 }
426 238
427 /// <summary> 239 if (m_cache != null)
428 /// Save Layers to Disk Cache
429 /// </summary>
430 /// <param name="AssetId">Asset to Save the layers. Used int he file name by default</param>
431 /// <param name="Layers">The Layer Data from OpenJpeg</param>
432 /// <returns></returns>
433 public bool SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers)
434 {
435 if (Layers.Length > 0 && enabled)
436 { 240 {
437 FileStream fsCache = 241 AssetBase layerDecodeAsset = new AssetBase();
438 new FileStream(String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId)), 242 layerDecodeAsset.ID = "j2kCache_" + AssetId.ToString();
439 FileMode.Create); 243 layerDecodeAsset.Local = true;
440 StreamWriter fsSWCache = new StreamWriter(fsCache); 244 layerDecodeAsset.Name = layerDecodeAsset.ID;
245 layerDecodeAsset.Temporary = true;
246 layerDecodeAsset.Type = (sbyte)AssetType.Notecard;
247
248 #region Serialize Layer Data
249
441 StringBuilder stringResult = new StringBuilder(); 250 StringBuilder stringResult = new StringBuilder();
442 string strEnd = "\n"; 251 string strEnd = "\n";
443 for (int i = 0; i < Layers.Length; i++) 252 for (int i = 0; i < Layers.Length; i++)
444 { 253 {
445 if (i == (Layers.Length - 1)) 254 if (i == Layers.Length - 1)
446 strEnd = ""; 255 strEnd = String.Empty;
447 256
448 stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd); 257 stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd);
449 } 258 }
450 fsSWCache.Write(stringResult.ToString());
451 fsSWCache.Close();
452 fsSWCache.Dispose();
453 fsCache.Dispose();
454 return true;
455 }
456
457 259
458 return false; 260 layerDecodeAsset.Data = Encoding.UTF8.GetBytes(stringResult.ToString());
459 }
460 261
461 262 #endregion Serialize Layer Data
462 /// <summary>
463 /// Loads the Layer data from the disk cache
464 /// Returns true if load succeeded
465 /// </summary>
466 /// <param name="AssetId">AssetId that we're checking the cache for</param>
467 /// <param name="Layers">out layers to save to</param>
468 /// <returns>true if load succeeded</returns>
469 public bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers)
470 {
471 string filename = String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId));
472 Layers = new OpenJPEG.J2KLayerInfo[0];
473 263
474 if (!File.Exists(filename)) 264 m_cache.Cache(layerDecodeAsset);
475 return false;
476
477 if (!enabled)
478 {
479 return false;
480 } 265 }
266 }
481 267
482 string readResult = string.Empty; 268 bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers)
483 269 {
484 try 270 if (m_decodedCache.TryGetValue(AssetId, out Layers))
485 { 271 {
486 FileStream fsCachefile = 272 return true;
487 new FileStream(filename,
488 FileMode.Open);
489
490 StreamReader sr = new StreamReader(fsCachefile);
491 readResult = sr.ReadToEnd();
492
493 sr.Close();
494 sr.Dispose();
495 fsCachefile.Dispose();
496
497 } 273 }
498 catch (IOException ioe) 274 else if (m_cache != null)
499 { 275 {
500 if (ioe is PathTooLongException) 276 string assetName = "j2kCache_" + AssetId.ToString();
501 { 277 AssetBase layerDecodeAsset = m_cache.Get(assetName);
502 m_log.Error(
503 "[J2KDecodeCache]: Cache Read failed. Path is too long.");
504 }
505 else if (ioe is DirectoryNotFoundException)
506 {
507 m_log.Error(
508 "[J2KDecodeCache]: Cache Read failed. Cache Directory does not exist!");
509 enabled = false;
510 }
511 else
512 {
513 m_log.Error(
514 "[J2KDecodeCache]: Cache Read failed. IO Exception.");
515 }
516 return false;
517 278
518 } 279 if (layerDecodeAsset != null)
519 catch (UnauthorizedAccessException)
520 {
521 m_log.Error(
522 "[J2KDecodeCache]: Cache Read failed. UnauthorizedAccessException Exception. Do you have the proper permissions on this file?");
523 return false;
524 }
525 catch (ArgumentException ae)
526 {
527 if (ae is ArgumentNullException)
528 {
529 m_log.Error(
530 "[J2KDecodeCache]: Cache Read failed. No Filename provided");
531 }
532 else
533 { 280 {
534 m_log.Error( 281 #region Deserialize Layer Data
535 "[J2KDecodeCache]: Cache Read failed. Filname was invalid");
536 }
537 return false;
538 }
539 catch (NotSupportedException)
540 {
541 m_log.Error(
542 "[J2KDecodeCache]: Cache Read failed, not supported. Cache disabled!");
543 enabled = false;
544 282
545 return false; 283 string readResult = Encoding.UTF8.GetString(layerDecodeAsset.Data);
546 } 284 string[] lines = readResult.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
547 catch (Exception e)
548 {
549 m_log.ErrorFormat(
550 "[J2KDecodeCache]: Cache Read failed, unknown exception. Error: {0}",
551 e.ToString());
552 return false;
553 }
554
555 string[] lines = readResult.Split('\n');
556 285
557 if (lines.Length <= 0) 286 if (lines.Length == 0)
558 return false;
559
560 Layers = new OpenJPEG.J2KLayerInfo[lines.Length];
561
562 for (int i = 0; i < lines.Length; i++)
563 {
564 string[] elements = lines[i].Split('|');
565 if (elements.Length == 3)
566 {
567 int element1, element2;
568
569 try
570 {
571 element1 = Convert.ToInt32(elements[0]);
572 element2 = Convert.ToInt32(elements[1]);
573 }
574 catch (FormatException)
575 { 287 {
576 m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed with ErrorConvert for {0}", AssetId); 288 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (empty) " + assetName);
577 Layers = new OpenJPEG.J2KLayerInfo[0]; 289 m_cache.Expire(assetName);
578 return false; 290 return false;
579 } 291 }
580 292
581 Layers[i] = new OpenJPEG.J2KLayerInfo(); 293 Layers = new OpenJPEG.J2KLayerInfo[lines.Length];
582 Layers[i].Start = element1;
583 Layers[i].End = element2;
584
585 }
586 else
587 {
588 // reading failed
589 m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed for {0}", AssetId);
590 Layers = new OpenJPEG.J2KLayerInfo[0];
591 return false;
592 }
593 }
594
595
596
597
598 return true;
599 }
600 294
601 /// <summary> 295 for (int i = 0; i < lines.Length; i++)
602 /// Routine which converts assetid to file name 296 {
603 /// </summary> 297 string[] elements = lines[i].Split('|');
604 /// <param name="AssetId">asset id of the image</param> 298 if (elements.Length == 3)
605 /// <returns>string filename</returns> 299 {
606 public string FileNameFromAssetId(UUID AssetId) 300 int element1, element2;
607 {
608 return String.Format("j2kCache_{0}.cache", AssetId);
609 }
610 301
611 public UUID AssetIdFromFileName(string fileName) 302 try
612 { 303 {
613 string rawId = fileName.Replace("j2kCache_", "").Replace(".cache", ""); 304 element1 = Convert.ToInt32(elements[0]);
614 UUID asset; 305 element2 = Convert.ToInt32(elements[1]);
615 if (!UUID.TryParse(rawId, out asset)) 306 }
616 return UUID.Zero; 307 catch (FormatException)
308 {
309 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (format) " + assetName);
310 m_cache.Expire(assetName);
311 return false;
312 }
617 313
618 return asset; 314 Layers[i] = new OpenJPEG.J2KLayerInfo();
619 } 315 Layers[i].Start = element1;
316 Layers[i].End = element2;
317 }
318 else
319 {
320 m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (layout) " + assetName);
321 m_cache.Expire(assetName);
322 return false;
323 }
324 }
620 325
621 /// <summary> 326 #endregion Deserialize Layer Data
622 /// Creates the Cache Folder
623 /// </summary>
624 /// <param name="pFolder">Folder to Create</param>
625 public void Createj2KCacheFolder(string pFolder)
626 {
627 try
628 {
629 Directory.CreateDirectory(pFolder);
630 }
631 catch (IOException ioe)
632 {
633 if (ioe is PathTooLongException)
634 {
635 m_log.Error(
636 "[J2KDecodeCache]: Cache Directory does not exist and create failed because the path to the cache folder is too long. Cache disabled!");
637 }
638 else if (ioe is DirectoryNotFoundException)
639 {
640 m_log.Error(
641 "[J2KDecodeCache]: Cache Directory does not exist and create failed because the supplied base of the directory folder does not exist. Cache disabled!");
642 }
643 else
644 {
645 m_log.Error(
646 "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an IO Exception. Cache disabled!");
647 }
648 enabled = false;
649 327
650 } 328 return true;
651 catch (UnauthorizedAccessException)
652 {
653 m_log.Error(
654 "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an UnauthorizedAccessException Exception. Cache disabled!");
655 enabled = false;
656 }
657 catch (ArgumentException ae)
658 {
659 if (ae is ArgumentNullException)
660 {
661 m_log.Error(
662 "[J2KDecodeCache]: Cache Directory does not exist and create failed because the folder provided is invalid! Cache disabled!");
663 } 329 }
664 else
665 {
666 m_log.Error(
667 "[J2KDecodeCache]: Cache Directory does not exist and create failed because no cache folder was provided! Cache disabled!");
668 }
669 enabled = false;
670 } 330 }
671 catch (NotSupportedException)
672 {
673 m_log.Error(
674 "[J2KDecodeCache]: Cache Directory does not exist and create failed because it's not supported. Cache disabled!");
675 enabled = false;
676 }
677 catch (Exception e)
678 {
679 m_log.ErrorFormat(
680 "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an unknown exception. Cache disabled! Error: {0}",
681 e.ToString());
682 enabled = false;
683 }
684 }
685
686 public void ScanCacheFiles(J2KDecodeDelegate decode)
687 {
688 DirectoryInfo dir = new DirectoryInfo(m_cacheDecodeFolder);
689 FileInfo[] files = dir.GetFiles("j2kCache_*.cache");
690 331
691 foreach (FileInfo f in files) 332 return false;
692 {
693 TimeSpan fileAge = DateTime.Now - f.CreationTime;
694
695 if (m_cacheTimeout != 0 && fileAge >= TimeSpan.FromMinutes(m_cacheTimeout))
696 {
697 File.Delete(f.Name);
698 decode(AssetIdFromFileName(f.Name));
699 System.Threading.Thread.Sleep(5000);
700 }
701 }
702 } 333 }
703 } 334 }
704} 335}
diff --git a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
index 14eb9a2..9a6c49a 100644
--- a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
@@ -325,7 +325,7 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
325 IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>(); 325 IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>();
326 if (cacheLayerDecode != null) 326 if (cacheLayerDecode != null)
327 { 327 {
328 cacheLayerDecode.syncdecode(asset.FullID, asset.Data); 328 cacheLayerDecode.Decode(asset.FullID, asset.Data);
329 cacheLayerDecode = null; 329 cacheLayerDecode = null;
330 LastAssetID = asset.FullID; 330 LastAssetID = asset.FullID;
331 } 331 }
diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
index f4526ae..8ad4844 100644
--- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
+++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
@@ -1102,5 +1102,9 @@ namespace OpenSim.Region.Examples.SimpleModule
1102 { 1102 {
1103 } 1103 }
1104 #endregion 1104 #endregion
1105
1106 public void SendRebakeAvatarTextures(UUID textureID)
1107 {
1108 }
1105 } 1109 }
1106} 1110}
diff --git a/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs b/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs
index b153997..856eb11 100644
--- a/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs
+++ b/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs
@@ -30,12 +30,11 @@ using OpenMetaverse.Imaging;
30 30
31namespace OpenSim.Region.Framework.Interfaces 31namespace OpenSim.Region.Framework.Interfaces
32{ 32{
33
34 public delegate void DecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers); 33 public delegate void DecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers);
35 34
36 public interface IJ2KDecoder 35 public interface IJ2KDecoder
37 { 36 {
38 void decode(UUID AssetId, byte[] assetData, DecodedCallback decodedReturn); 37 void BeginDecode(UUID assetID, byte[] j2kData, DecodedCallback callback);
39 void syncdecode(UUID AssetId, byte[] j2kdata); 38 void Decode(UUID assetID, byte[] j2kData);
40 } 39 }
41} 40}
diff --git a/OpenSim/Region/Framework/Scenes/SceneManager.cs b/OpenSim/Region/Framework/Scenes/SceneManager.cs
index 1d4efd0..3097929 100644
--- a/OpenSim/Region/Framework/Scenes/SceneManager.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneManager.cs
@@ -540,127 +540,5 @@ namespace OpenSim.Region.Framework.Scenes
540 { 540 {
541 m_localScenes.ForEach(action); 541 m_localScenes.ForEach(action);
542 } 542 }
543
544 public void CacheJ2kDecode(int threads)
545 {
546 if (threads < 1) threads = 1;
547
548 IJ2KDecoder m_decoder = m_localScenes[0].RequestModuleInterface<IJ2KDecoder>();
549
550 List<UUID> assetRequestList = new List<UUID>();
551
552 #region AssetGathering!
553 foreach (Scene scene in m_localScenes)
554 {
555 List<EntityBase> entitles = scene.GetEntities();
556 foreach (EntityBase entity in entitles)
557 {
558 if (entity is SceneObjectGroup)
559 {
560 SceneObjectGroup sog = (SceneObjectGroup) entity;
561 foreach (SceneObjectPart part in sog.Children.Values)
562 {
563 if (part.Shape != null)
564 {
565 if (part.Shape.TextureEntry.Length > 0)
566 {
567 OpenMetaverse.Primitive.TextureEntry te =
568 new Primitive.TextureEntry(part.Shape.TextureEntry, 0,
569 part.Shape.TextureEntry.Length);
570 if (te.DefaultTexture != null) // this has been null for some reason...
571 {
572 if (te.DefaultTexture.TextureID != UUID.Zero)
573 assetRequestList.Add(te.DefaultTexture.TextureID);
574 }
575 for (int i=0; i<te.FaceTextures.Length; i++)
576 {
577 if (te.FaceTextures[i] != null)
578 {
579 if (te.FaceTextures[i].TextureID != UUID.Zero)
580 {
581 assetRequestList.Add(te.FaceTextures[i].TextureID);
582 }
583 }
584 }
585 }
586 if (part.Shape.SculptTexture != UUID.Zero)
587 {
588 assetRequestList.Add(part.Shape.SculptTexture);
589 }
590
591 }
592 }
593 }
594 }
595 }
596 #endregion
597
598 int entries_per_thread = (assetRequestList.Count / threads) + 1;
599
600 UUID[] arrAssetRequestList = assetRequestList.ToArray();
601
602 List<UUID[]> arrvalus = new List<UUID[]>();
603
604 //split into separate arrays
605 for (int j = 0; j < threads; j++)
606 {
607 List<UUID> val = new List<UUID>();
608
609 for (int k = j * entries_per_thread; k < ((j + 1) * entries_per_thread); k++)
610 {
611 if (k < arrAssetRequestList.Length)
612 {
613 val.Add(arrAssetRequestList[k]);
614 }
615
616 }
617 arrvalus.Add(val.ToArray());
618 }
619
620 for (int l = 0; l < arrvalus.Count; l++)
621 {
622 DecodeThreadContents threadworkItem = new DecodeThreadContents();
623 threadworkItem.sn = m_localScenes[0];
624 threadworkItem.j2kdecode = m_decoder;
625 threadworkItem.arrassets = arrvalus[l];
626
627 System.Threading.Thread decodethread =
628 new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(threadworkItem.run));
629
630 threadworkItem.SetThread(decodethread);
631
632 decodethread.Priority = System.Threading.ThreadPriority.Lowest;
633 decodethread.Name = "J2kCacheDecodeThread_" + l + 1;
634 ThreadTracker.Add(decodethread);
635 decodethread.Start();
636
637 }
638 }
639 }
640
641 public class DecodeThreadContents
642 {
643 public Scene sn;
644 public UUID[] arrassets;
645 public IJ2KDecoder j2kdecode;
646 private System.Threading.Thread thisthread;
647
648 public void run(object o)
649 {
650 for (int i=0;i<arrassets.Length;i++)
651 {
652 AssetBase ab = sn.AssetService.Get(arrassets[i].ToString());
653 if (ab != null && ab.Data != null)
654 {
655 j2kdecode.syncdecode(arrassets[i], ab.Data);
656 }
657 }
658 ThreadTracker.Remove(thisthread);
659 }
660
661 public void SetThread(System.Threading.Thread thr)
662 {
663 thisthread = thr;
664 }
665 } 543 }
666} 544}
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
index 66fefa3..3996cdc 100644
--- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs
+++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
@@ -74,6 +74,8 @@ namespace OpenSim.Region.Framework.Scenes
74 74
75 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 75 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
76 76
77 private static readonly byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 };
78
77 public static byte[] DefaultTexture; 79 public static byte[] DefaultTexture;
78 80
79 public UUID currentParcelUUID = UUID.Zero; 81 public UUID currentParcelUUID = UUID.Zero;
@@ -2686,7 +2688,7 @@ namespace OpenSim.Region.Framework.Scenes
2686 /// </summary> 2688 /// </summary>
2687 /// <param name="texture"></param> 2689 /// <param name="texture"></param>
2688 /// <param name="visualParam"></param> 2690 /// <param name="visualParam"></param>
2689 public void SetAppearance(byte[] texture, List<byte> visualParam) 2691 public void SetAppearance(Primitive.TextureEntry textureEntry, byte[] visualParams)
2690 { 2692 {
2691 if (m_physicsActor != null) 2693 if (m_physicsActor != null)
2692 { 2694 {
@@ -2704,7 +2706,30 @@ namespace OpenSim.Region.Framework.Scenes
2704 AddToPhysicalScene(flyingTemp); 2706 AddToPhysicalScene(flyingTemp);
2705 } 2707 }
2706 } 2708 }
2707 m_appearance.SetAppearance(texture, visualParam); 2709
2710 #region Bake Cache Check
2711
2712 if (textureEntry != null)
2713 {
2714 for (int i = 0; i < BAKE_INDICES.Length; i++)
2715 {
2716 int j = BAKE_INDICES[i];
2717 Primitive.TextureEntryFace face = textureEntry.FaceTextures[j];
2718
2719 if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE)
2720 {
2721 if (m_scene.AssetService.Get(face.TextureID.ToString()) == null)
2722 {
2723 m_log.Warn("[APPEARANCE]: Missing baked texture " + face.TextureID + " (" + (AppearanceManager.TextureIndex)j + ") for avatar " + this.Name);
2724 this.ControllingClient.SendRebakeAvatarTextures(face.TextureID);
2725 }
2726 }
2727 }
2728 }
2729
2730 #endregion Bake Cache Check
2731
2732 m_appearance.SetAppearance(textureEntry, visualParams);
2708 if (m_appearance.AvatarHeight > 0) 2733 if (m_appearance.AvatarHeight > 0)
2709 SetHeight(m_appearance.AvatarHeight); 2734 SetHeight(m_appearance.AvatarHeight);
2710 m_scene.CommsManager.AvatarService.UpdateUserAppearance(m_controllingClient.AgentId, m_appearance); 2735 m_scene.CommsManager.AvatarService.UpdateUserAppearance(m_controllingClient.AgentId, m_appearance);
@@ -3255,14 +3280,14 @@ namespace OpenSim.Region.Framework.Scenes
3255 wears[i++] = new AvatarWearable(itemId, assetId); 3280 wears[i++] = new AvatarWearable(itemId, assetId);
3256 } 3281 }
3257 m_appearance.Wearables = wears; 3282 m_appearance.Wearables = wears;
3258 byte[] te = null; 3283 Primitive.TextureEntry te;
3259 if (cAgent.AgentTextures != null) 3284 if (cAgent.AgentTextures != null && cAgent.AgentTextures.Length > 1)
3260 te = cAgent.AgentTextures; 3285 te = new Primitive.TextureEntry(cAgent.AgentTextures, 0, cAgent.AgentTextures.Length);
3261 else 3286 else
3262 te = AvatarAppearance.GetDefaultTexture().GetBytes(); 3287 te = AvatarAppearance.GetDefaultTexture();
3263 if ((cAgent.VisualParams == null) || (cAgent.VisualParams.Length < AvatarAppearance.VISUALPARAM_COUNT)) 3288 if ((cAgent.VisualParams == null) || (cAgent.VisualParams.Length < AvatarAppearance.VISUALPARAM_COUNT))
3264 cAgent.VisualParams = AvatarAppearance.GetDefaultVisualParams(); 3289 cAgent.VisualParams = AvatarAppearance.GetDefaultVisualParams();
3265 m_appearance.SetAppearance(te, new List<byte>(cAgent.VisualParams)); 3290 m_appearance.SetAppearance(te, (byte[])cAgent.VisualParams.Clone());
3266 } 3291 }
3267 catch (Exception e) 3292 catch (Exception e)
3268 { 3293 {
diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs
index 605645b..ee2d2db 100644
--- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs
+++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs
@@ -861,12 +861,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
861 Scene scene = (Scene)Scene; 861 Scene scene = (Scene)Scene;
862 AvatarAppearance appearance; 862 AvatarAppearance appearance;
863 scene.GetAvatarAppearance(this, out appearance); 863 scene.GetAvatarAppearance(this, out appearance);
864 List<byte> visualParams = new List<byte>(); 864 OnSetAppearance(appearance.Texture, (byte[])appearance.VisualParams.Clone());
865 foreach (byte visualParam in appearance.VisualParams)
866 {
867 visualParams.Add(visualParam);
868 }
869 OnSetAppearance(appearance.Texture.GetBytes(), visualParams);
870 } 865 }
871 866
872 public void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args) 867 public void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args)
@@ -1609,5 +1604,9 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
1609 } 1604 }
1610 1605
1611 #endregion 1606 #endregion
1607
1608 public void SendRebakeAvatarTextures(UUID textureID)
1609 {
1610 }
1612 } 1611 }
1613} 1612}
diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs
index f0bdf3b..ac8b98c 100644
--- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs
+++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs
@@ -1101,5 +1101,9 @@ namespace OpenSim.Region.OptionalModules.World.NPC
1101 { 1101 {
1102 } 1102 }
1103 #endregion 1103 #endregion
1104
1105 public void SendRebakeAvatarTextures(UUID textureID)
1106 {
1107 }
1104 } 1108 }
1105} 1109}
diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
index b3bfe07..30a2675 100644
--- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
+++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
@@ -163,13 +163,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
163 { 163 {
164 AvatarAppearance x = GetAppearance(p_cloneAppearanceFrom, p_scene); 164 AvatarAppearance x = GetAppearance(p_cloneAppearanceFrom, p_scene);
165 165
166 List<byte> wearbyte = new List<byte>(); 166 sp.SetAppearance(x.Texture, (byte[])x.VisualParams.Clone());
167 for (int i = 0; i < x.VisualParams.Length; i++)
168 {
169 wearbyte.Add(x.VisualParams[i]);
170 }
171
172 sp.SetAppearance(x.Texture.GetBytes(), wearbyte);
173 } 167 }
174 168
175 m_avatars.Add(npcAvatar.AgentId, npcAvatar); 169 m_avatars.Add(npcAvatar.AgentId, npcAvatar);
diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
index 56eb359..d56ddc8 100644
--- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
+++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
@@ -78,6 +78,20 @@ namespace OpenSim.Region.Physics.Meshing
78 78
79 private Dictionary<ulong, Mesh> m_uniqueMeshes = new Dictionary<ulong, Mesh>(); 79 private Dictionary<ulong, Mesh> m_uniqueMeshes = new Dictionary<ulong, Mesh>();
80 80
81 public Meshmerizer()
82 {
83 try
84 {
85 if (!Directory.Exists(decodedScultMapPath))
86 Directory.CreateDirectory(decodedScultMapPath);
87 }
88 catch (Exception e)
89 {
90 m_log.WarnFormat("[SCULPT]: Unable to create {0} directory: ", decodedScultMapPath, e.Message);
91 }
92
93 }
94
81 /// <summary> 95 /// <summary>
82 /// creates a simple box mesh of the specified size. This mesh is of very low vertex count and may 96 /// creates a simple box mesh of the specified size. This mesh is of very low vertex count and may
83 /// be useful as a backup proxy when level of detail is not needed or when more complex meshes fail 97 /// be useful as a backup proxy when level of detail is not needed or when more complex meshes fail
diff --git a/OpenSim/Server/Handlers/Tests/Asset/AssetServerGetHandlerTests.cs b/OpenSim/Server/Handlers/Tests/Asset/AssetServerGetHandlerTests.cs
deleted file mode 100644
index 04a73e4..0000000
--- a/OpenSim/Server/Handlers/Tests/Asset/AssetServerGetHandlerTests.cs
+++ /dev/null
@@ -1,80 +0,0 @@
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 OpenSimulator 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 NUnit.Framework;
29using OpenSim.Framework;
30using OpenSim.Framework.Servers.HttpServer;
31using OpenSim.Server.Handlers.Asset;
32using OpenSim.Services.Interfaces;
33using OpenSim.Tests.Common;
34using OpenSim.Tests.Common.Mock;
35using OpenSim.Tests.Common.Setup;
36
37namespace OpenSim.Server.Handlers.Tests.Asset
38{
39 [TestFixture]
40 public class AssetServerGetHandlerTests
41 {
42 private const string ASSETS_PATH = "/assets";
43
44 [Test]
45 public void TestConstructor()
46 {
47 TestHelper.InMethod();
48
49 new AssetServerGetHandler(null);
50 }
51
52 [Test]
53 public void TestGetParams()
54 {
55 TestHelper.InMethod();
56
57 AssetServerGetHandler handler = new AssetServerGetHandler(null);
58 BaseRequestHandlerTestHelper.BaseTestGetParams(handler, ASSETS_PATH);
59 }
60
61 [Test]
62 public void TestSplitParams()
63 {
64 TestHelper.InMethod();
65
66 AssetServerGetHandler handler = new AssetServerGetHandler(null);
67 BaseRequestHandlerTestHelper.BaseTestSplitParams(handler, ASSETS_PATH);
68 }
69
70 // TODO: unused
71 // private static AssetBase CreateTestEnvironment(out AssetServerGetHandler handler, out OSHttpResponse response)
72 // {
73 // AssetBase asset = GetAssetStreamHandlerTestHelpers.CreateCommonTestResources(out response);
74 // IAssetService assetDataPlugin = new TestAssetService();
75 // handler = new AssetServerGetHandler(assetDataPlugin);
76 // assetDataPlugin.Store(asset);
77 // return asset;
78 // }
79 }
80}
diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs
index fe31729..2b54890 100644
--- a/OpenSim/Tests/Common/Mock/TestClient.cs
+++ b/OpenSim/Tests/Common/Mock/TestClient.cs
@@ -1163,5 +1163,9 @@ namespace OpenSim.Tests.Common.Mock
1163 { 1163 {
1164 } 1164 }
1165 1165
1166 public void SendRebakeAvatarTextures(UUID textureID)
1167 {
1168 }
1169
1166 } 1170 }
1167} 1171}