diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Framework/Communications/Cache/AssetCache.cs | 268 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/ClientStackManager.cs | 32 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 2 |
3 files changed, 99 insertions, 203 deletions
diff --git a/OpenSim/Framework/Communications/Cache/AssetCache.cs b/OpenSim/Framework/Communications/Cache/AssetCache.cs index dfdb0e7..0581cc6 100644 --- a/OpenSim/Framework/Communications/Cache/AssetCache.cs +++ b/OpenSim/Framework/Communications/Cache/AssetCache.cs | |||
@@ -33,6 +33,7 @@ using OpenMetaverse; | |||
33 | using OpenMetaverse.Packets; | 33 | using OpenMetaverse.Packets; |
34 | using log4net; | 34 | using log4net; |
35 | using OpenSim.Framework.Statistics; | 35 | using OpenSim.Framework.Statistics; |
36 | using GlynnTucker.Cache; | ||
36 | 37 | ||
37 | namespace OpenSim.Framework.Communications.Cache | 38 | namespace OpenSim.Framework.Communications.Cache |
38 | { | 39 | { |
@@ -52,18 +53,20 @@ namespace OpenSim.Framework.Communications.Cache | |||
52 | /// </summary> | 53 | /// </summary> |
53 | public class AssetCache : IAssetReceiver | 54 | public class AssetCache : IAssetReceiver |
54 | { | 55 | { |
56 | protected ICache m_memcache = new SimpleMemoryCache(); | ||
57 | |||
55 | private static readonly ILog m_log | 58 | private static readonly ILog m_log |
56 | = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 59 | = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
57 | 60 | ||
58 | /// <summary> | 61 | /// <summary> |
59 | /// The cache of assets. This does not include textures. | 62 | /// The cache of assets. This does not include textures. |
60 | /// </summary> | 63 | /// </summary> |
61 | private Dictionary<UUID, AssetInfo> Assets; | 64 | //private Dictionary<UUID, AssetInfo> Assets; |
62 | 65 | ||
63 | /// <summary> | 66 | /// <summary> |
64 | /// The cache of textures. | 67 | /// The cache of textures. |
65 | /// </summary> | 68 | /// </summary> |
66 | private Dictionary<UUID, TextureImage> Textures; | 69 | //private Dictionary<UUID, TextureImage> Textures; |
67 | 70 | ||
68 | /// <summary> | 71 | /// <summary> |
69 | /// Assets requests which are waiting for asset server data. This includes texture requests | 72 | /// Assets requests which are waiting for asset server data. This includes texture requests |
@@ -95,52 +98,11 @@ namespace OpenSim.Framework.Communications.Cache | |||
95 | /// </summary> | 98 | /// </summary> |
96 | public void ShowState() | 99 | public void ShowState() |
97 | { | 100 | { |
98 | m_log.InfoFormat("Assets:{0} Textures:{1} RequestLists:{2}", | 101 | m_log.InfoFormat("Memcache:{1} RequestLists:{2}", |
99 | Assets.Count, | 102 | m_memcache.Count, |
100 | Textures.Count, | ||
101 | // AssetRequests.Count, | 103 | // AssetRequests.Count, |
102 | // RequestedAssets.Count, | 104 | // RequestedAssets.Count, |
103 | RequestLists.Count); | 105 | RequestLists.Count); |
104 | |||
105 | int temporaryImages = 0; | ||
106 | int temporaryAssets = 0; | ||
107 | |||
108 | long imageBytes = 0; | ||
109 | long assetBytes = 0; | ||
110 | |||
111 | foreach (TextureImage texture in Textures.Values) | ||
112 | { | ||
113 | if (texture != null) | ||
114 | { | ||
115 | if (texture.Temporary) | ||
116 | { | ||
117 | temporaryImages++; | ||
118 | } | ||
119 | |||
120 | imageBytes += texture.Data.GetLongLength(0); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | foreach (AssetInfo asset in Assets.Values) | ||
125 | { | ||
126 | if (asset != null) | ||
127 | { | ||
128 | if (asset.Temporary) | ||
129 | { | ||
130 | temporaryAssets++; | ||
131 | } | ||
132 | |||
133 | assetBytes += asset.Data.GetLongLength(0); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | m_log.InfoFormat("Temporary Images: {0} Temporary Assets: {1}", | ||
138 | temporaryImages, | ||
139 | temporaryAssets); | ||
140 | |||
141 | m_log.InfoFormat("Image data: {0}kb Asset data: {1}kb", | ||
142 | imageBytes / 1024, | ||
143 | assetBytes / 1024); | ||
144 | } | 106 | } |
145 | 107 | ||
146 | /// <summary> | 108 | /// <summary> |
@@ -161,8 +123,6 @@ namespace OpenSim.Framework.Communications.Cache | |||
161 | /// </summary> | 123 | /// </summary> |
162 | private void Initialize() | 124 | private void Initialize() |
163 | { | 125 | { |
164 | Assets = new Dictionary<UUID, AssetInfo>(); | ||
165 | Textures = new Dictionary<UUID, TextureImage>(); | ||
166 | AssetRequests = new List<AssetRequest>(); | 126 | AssetRequests = new List<AssetRequest>(); |
167 | 127 | ||
168 | RequestedAssets = new Dictionary<UUID, AssetRequest>(); | 128 | RequestedAssets = new Dictionary<UUID, AssetRequest>(); |
@@ -181,7 +141,7 @@ namespace OpenSim.Framework.Communications.Cache | |||
181 | m_assetServer = assetServer; | 141 | m_assetServer = assetServer; |
182 | m_assetServer.SetReceiver(this); | 142 | m_assetServer.SetReceiver(this); |
183 | 143 | ||
184 | Thread assetCacheThread = new Thread(new ThreadStart(RunAssetManager)); | 144 | Thread assetCacheThread = new Thread(RunAssetManager); |
185 | assetCacheThread.Name = "AssetCacheThread"; | 145 | assetCacheThread.Name = "AssetCacheThread"; |
186 | assetCacheThread.IsBackground = true; | 146 | assetCacheThread.IsBackground = true; |
187 | assetCacheThread.Start(); | 147 | assetCacheThread.Start(); |
@@ -203,7 +163,7 @@ namespace OpenSim.Framework.Communications.Cache | |||
203 | } | 163 | } |
204 | catch (Exception e) | 164 | catch (Exception e) |
205 | { | 165 | { |
206 | m_log.Error("[ASSET CACHE]: " + e.ToString()); | 166 | m_log.Error("[ASSET CACHE]: " + e); |
207 | } | 167 | } |
208 | } | 168 | } |
209 | } | 169 | } |
@@ -216,14 +176,11 @@ namespace OpenSim.Framework.Communications.Cache | |||
216 | /// <returns>true if the asset was in the cache, false if it was not</returns> | 176 | /// <returns>true if the asset was in the cache, false if it was not</returns> |
217 | public bool TryGetCachedAsset(UUID assetId, out AssetBase asset) | 177 | public bool TryGetCachedAsset(UUID assetId, out AssetBase asset) |
218 | { | 178 | { |
219 | if (Textures.ContainsKey(assetId)) | 179 | Object tmp; |
220 | { | 180 | if(m_memcache.TryGet(assetId, out tmp)) |
221 | asset = Textures[assetId]; | ||
222 | return true; | ||
223 | } | ||
224 | else if (Assets.ContainsKey(assetId)) | ||
225 | { | 181 | { |
226 | asset = Assets[assetId]; | 182 | asset = (AssetBase)tmp; |
183 | //m_log.Info("Retrieved from cache " + assetId); | ||
227 | return true; | 184 | return true; |
228 | } | 185 | } |
229 | 186 | ||
@@ -312,25 +269,22 @@ namespace OpenSim.Framework.Communications.Cache | |||
312 | { | 269 | { |
313 | return asset; | 270 | return asset; |
314 | } | 271 | } |
315 | else | 272 | m_assetServer.RequestAsset(assetID, isTexture); |
273 | |||
274 | do | ||
316 | { | 275 | { |
317 | m_assetServer.RequestAsset(assetID, isTexture); | 276 | Thread.Sleep(pollPeriod); |
318 | 277 | ||
319 | do | 278 | if (TryGetCachedAsset(assetID, out asset)) |
320 | { | 279 | { |
321 | Thread.Sleep(pollPeriod); | 280 | return asset; |
322 | 281 | } | |
323 | if (TryGetCachedAsset(assetID, out asset)) | 282 | } while (--maxPolls > 0); |
324 | { | ||
325 | return asset; | ||
326 | } | ||
327 | } while (--maxPolls > 0); | ||
328 | 283 | ||
329 | m_log.WarnFormat("[ASSET CACHE]: {0} {1} was not received before the retrieval timeout was reached", | 284 | m_log.WarnFormat("[ASSET CACHE]: {0} {1} was not received before the retrieval timeout was reached", |
330 | isTexture ? "texture" : "asset", assetID.ToString()); | 285 | isTexture ? "texture" : "asset", assetID.ToString()); |
331 | 286 | ||
332 | return null; | 287 | return null; |
333 | } | ||
334 | } | 288 | } |
335 | 289 | ||
336 | /// <summary> | 290 | /// <summary> |
@@ -339,68 +293,34 @@ namespace OpenSim.Framework.Communications.Cache | |||
339 | /// <param name="asset"></param> | 293 | /// <param name="asset"></param> |
340 | public void AddAsset(AssetBase asset) | 294 | public void AddAsset(AssetBase asset) |
341 | { | 295 | { |
342 | // m_log.DebugFormat( | 296 | if (!m_memcache.Contains(asset.FullID)) |
343 | // "[ASSET CACHE]: Uploaded asset {0}, temporary {1}, store local {2}", | ||
344 | // asset.ID, asset.Temporary, asset.Local); | ||
345 | |||
346 | if (asset.Type == (int)AssetType.Texture) | ||
347 | { | 297 | { |
348 | if (!Textures.ContainsKey(asset.FullID)) | 298 | m_log.Info("[CACHE] Caching " + asset.FullID + " for 24 hours from last access"); |
299 | // Use 24 hour rolling asset cache. | ||
300 | m_memcache.AddOrUpdate(asset.FullID, asset, TimeSpan.FromHours(24)); | ||
301 | |||
302 | // According to http://wiki.secondlife.com/wiki/AssetUploadRequest, Local signifies that the | ||
303 | // information is stored locally. It could disappear, in which case we could send the | ||
304 | // ImageNotInDatabase packet to tell the client this. | ||
305 | // | ||
306 | // However, this doesn't quite appear to work with local textures that are part of an avatar's | ||
307 | // appearance texture set. Whilst sending an ImageNotInDatabase does trigger an automatic rebake | ||
308 | // and reupload by the client, if those assets aren't pushed to the asset server anyway, then | ||
309 | // on crossing onto another region server, other avatars can no longer get the required textures. | ||
310 | // There doesn't appear to be any signal from the sim to the newly region border crossed client | ||
311 | // asking it to reupload its local texture assets to that region server. | ||
312 | // | ||
313 | // One can think of other cunning ways around this. For instance, on a region crossing or teleport, | ||
314 | // the original sim could squirt local assets to the new sim. Or the new sim could have pointers | ||
315 | // to the original sim to fetch the 'local' assets (this is getting more complicated). | ||
316 | // | ||
317 | // But for now, we're going to take the easy way out and store local assets globally. | ||
318 | // | ||
319 | // TODO: Also, Temporary is now deprecated. We should start ignoring it and not passing it out from LLClientView. | ||
320 | if (!asset.Temporary || asset.Local) | ||
349 | { | 321 | { |
350 | TextureImage textur = new TextureImage(asset); | 322 | m_assetServer.StoreAsset(asset); |
351 | Textures.Add(textur.FullID, textur); | ||
352 | |||
353 | if (StatsManager.SimExtraStats != null) | ||
354 | StatsManager.SimExtraStats.AddTexture(textur); | ||
355 | |||
356 | // According to http://wiki.secondlife.com/wiki/AssetUploadRequest, Local signifies that the | ||
357 | // information is stored locally. It could disappear, in which case we could send the | ||
358 | // ImageNotInDatabase packet to tell the client this. | ||
359 | // | ||
360 | // However, this doesn't quite appear to work with local textures that are part of an avatar's | ||
361 | // appearance texture set. Whilst sending an ImageNotInDatabase does trigger an automatic rebake | ||
362 | // and reupload by the client, if those assets aren't pushed to the asset server anyway, then | ||
363 | // on crossing onto another region server, other avatars can no longer get the required textures. | ||
364 | // There doesn't appear to be any signal from the sim to the newly region border crossed client | ||
365 | // asking it to reupload its local texture assets to that region server. | ||
366 | // | ||
367 | // One can think of other cunning ways around this. For instance, on a region crossing or teleport, | ||
368 | // the original sim could squirt local assets to the new sim. Or the new sim could have pointers | ||
369 | // to the original sim to fetch the 'local' assets (this is getting more complicated). | ||
370 | // | ||
371 | // But for now, we're going to take the easy way out and store local assets globally. | ||
372 | // | ||
373 | // TODO: Also, Temporary is now deprecated. We should start ignoring it and not passing it out from LLClientView. | ||
374 | if (!asset.Temporary || asset.Local) | ||
375 | { | ||
376 | m_assetServer.StoreAsset(asset); | ||
377 | } | ||
378 | } | 323 | } |
379 | // else | ||
380 | // { | ||
381 | // m_log.DebugFormat("[ASSET CACHE]: Textures already contains {0}", asset.ID); | ||
382 | // } | ||
383 | } | ||
384 | else | ||
385 | { | ||
386 | if (!Assets.ContainsKey(asset.FullID)) | ||
387 | { | ||
388 | AssetInfo assetInf = new AssetInfo(asset); | ||
389 | Assets.Add(assetInf.FullID, assetInf); | ||
390 | |||
391 | if (StatsManager.SimExtraStats != null) | ||
392 | StatsManager.SimExtraStats.AddAsset(assetInf); | ||
393 | |||
394 | // See comment above. | ||
395 | if (!asset.Temporary || asset.Local) | ||
396 | { | ||
397 | m_assetServer.StoreAsset(asset); | ||
398 | } | ||
399 | } | ||
400 | // else | ||
401 | // { | ||
402 | // m_log.DebugFormat("[ASSET CACHE]: Assets already contains {0}", asset.ID); | ||
403 | // } | ||
404 | } | 324 | } |
405 | } | 325 | } |
406 | 326 | ||
@@ -417,71 +337,46 @@ namespace OpenSim.Framework.Communications.Cache | |||
417 | // in the 2 caches differently. Also, locks are probably | 337 | // in the 2 caches differently. Also, locks are probably |
418 | // needed in all of this, or move to synchronized non | 338 | // needed in all of this, or move to synchronized non |
419 | // generic forms for Dictionaries. | 339 | // generic forms for Dictionaries. |
420 | if (Textures.ContainsKey(uuid)) | 340 | if(m_memcache.Contains(uuid)) |
421 | { | ||
422 | Textures.Remove(uuid); | ||
423 | } | ||
424 | else if (Assets.ContainsKey(uuid)) | ||
425 | { | 341 | { |
426 | Assets.Remove(uuid); | 342 | m_memcache.Remove(uuid); |
427 | } | 343 | } |
428 | } | 344 | } |
429 | 345 | ||
430 | // See IAssetReceiver | 346 | // See IAssetReceiver |
431 | public void AssetReceived(AssetBase asset, bool IsTexture) | 347 | public void AssetReceived(AssetBase asset, bool IsTexture) |
432 | { | 348 | { |
433 | // m_log.DebugFormat("[ASSET CACHE]: Received asset {0}", asset.ID); | ||
434 | |||
435 | //check if it is a texture or not | ||
436 | //then add to the correct cache list | ||
437 | //then check for waiting requests for this asset/texture (in the Requested lists) | ||
438 | //and move those requests into the Requests list. | ||
439 | if (IsTexture) | ||
440 | { | ||
441 | TextureImage image = new TextureImage(asset); | ||
442 | if (!Textures.ContainsKey(image.FullID)) | ||
443 | { | ||
444 | Textures.Add(image.FullID, image); | ||
445 | 349 | ||
446 | if (StatsManager.SimExtraStats != null) | 350 | AssetInfo assetInf = new AssetInfo(asset); |
447 | { | 351 | if (!m_memcache.Contains(assetInf.FullID)) |
448 | StatsManager.SimExtraStats.AddTexture(image); | ||
449 | } | ||
450 | } | ||
451 | } | ||
452 | else | ||
453 | { | 352 | { |
454 | AssetInfo assetInf = new AssetInfo(asset); | 353 | m_memcache.AddOrUpdate(assetInf.FullID, assetInf, TimeSpan.FromHours(24)); |
455 | if (!Assets.ContainsKey(assetInf.FullID)) | ||
456 | { | ||
457 | Assets.Add(assetInf.FullID, assetInf); | ||
458 | 354 | ||
459 | if (StatsManager.SimExtraStats != null) | 355 | if (StatsManager.SimExtraStats != null) |
460 | { | 356 | { |
461 | StatsManager.SimExtraStats.AddAsset(assetInf); | 357 | StatsManager.SimExtraStats.AddAsset(assetInf); |
462 | } | 358 | } |
463 | 359 | ||
464 | if (RequestedAssets.ContainsKey(assetInf.FullID)) | 360 | if (RequestedAssets.ContainsKey(assetInf.FullID)) |
465 | { | 361 | { |
466 | AssetRequest req = RequestedAssets[assetInf.FullID]; | 362 | AssetRequest req = RequestedAssets[assetInf.FullID]; |
467 | req.AssetInf = assetInf; | 363 | req.AssetInf = assetInf; |
468 | req.NumPackets = CalculateNumPackets(assetInf.Data); | 364 | req.NumPackets = CalculateNumPackets(assetInf.Data); |
469 | 365 | ||
470 | RequestedAssets.Remove(assetInf.FullID); | 366 | RequestedAssets.Remove(assetInf.FullID); |
471 | // If it's a direct request for a script, drop it | 367 | // If it's a direct request for a script, drop it |
472 | // because it's a hacked client | 368 | // because it's a hacked client |
473 | if (req.AssetRequestSource != 2 || assetInf.Type != 10) | 369 | if (req.AssetRequestSource != 2 || assetInf.Type != 10) |
474 | AssetRequests.Add(req); | 370 | AssetRequests.Add(req); |
475 | } | ||
476 | } | 371 | } |
477 | } | 372 | } |
478 | 373 | ||
479 | // Notify requesters for this asset | 374 | // Notify requesters for this asset |
480 | AssetRequestsList reqList = null; | 375 | AssetRequestsList reqList; |
481 | 376 | ||
482 | lock (RequestLists) | 377 | lock (RequestLists) |
483 | { | 378 | { |
484 | if (RequestLists.TryGetValue(asset.FullID, out reqList)) | 379 | if (RequestLists.TryGetValue(asset.FullID, out reqList)) |
485 | RequestLists.Remove(asset.FullID); | 380 | RequestLists.Remove(asset.FullID); |
486 | } | 381 | } |
487 | 382 | ||
@@ -489,7 +384,7 @@ namespace OpenSim.Framework.Communications.Cache | |||
489 | { | 384 | { |
490 | if (StatsManager.SimExtraStats != null) | 385 | if (StatsManager.SimExtraStats != null) |
491 | StatsManager.SimExtraStats.AddAssetRequestTimeAfterCacheMiss(DateTime.Now - reqList.TimeRequested); | 386 | StatsManager.SimExtraStats.AddAssetRequestTimeAfterCacheMiss(DateTime.Now - reqList.TimeRequested); |
492 | 387 | ||
493 | foreach (NewAssetRequest req in reqList.Requests) | 388 | foreach (NewAssetRequest req in reqList.Requests) |
494 | { | 389 | { |
495 | // Xantor 20080526 are we really calling all the callbacks if multiple queued for 1 request? -- Yes, checked | 390 | // Xantor 20080526 are we really calling all the callbacks if multiple queued for 1 request? -- Yes, checked |
@@ -504,17 +399,8 @@ namespace OpenSim.Framework.Communications.Cache | |||
504 | { | 399 | { |
505 | // m_log.WarnFormat("[ASSET CACHE]: AssetNotFound for {0}", assetID); | 400 | // m_log.WarnFormat("[ASSET CACHE]: AssetNotFound for {0}", assetID); |
506 | 401 | ||
507 | if (IsTexture) | ||
508 | { | ||
509 | Textures[assetID] = null; | ||
510 | } | ||
511 | else | ||
512 | { | ||
513 | Assets[assetID] = null; | ||
514 | } | ||
515 | |||
516 | // Notify requesters for this asset | 402 | // Notify requesters for this asset |
517 | AssetRequestsList reqList = null; | 403 | AssetRequestsList reqList; |
518 | lock (RequestLists) | 404 | lock (RequestLists) |
519 | { | 405 | { |
520 | if (RequestLists.TryGetValue(assetID, out reqList)) | 406 | if (RequestLists.TryGetValue(assetID, out reqList)) |
@@ -578,7 +464,7 @@ namespace OpenSim.Framework.Communications.Cache | |||
578 | 464 | ||
579 | //check to see if asset is in local cache, if not we need to request it from asset server. | 465 | //check to see if asset is in local cache, if not we need to request it from asset server. |
580 | //Console.WriteLine("asset request " + requestID); | 466 | //Console.WriteLine("asset request " + requestID); |
581 | if (!Assets.ContainsKey(requestID)) | 467 | if (!m_memcache.Contains(requestID)) |
582 | { | 468 | { |
583 | //not found asset | 469 | //not found asset |
584 | // so request from asset server | 470 | // so request from asset server |
@@ -598,7 +484,7 @@ namespace OpenSim.Framework.Communications.Cache | |||
598 | } | 484 | } |
599 | 485 | ||
600 | // It has an entry in our cache | 486 | // It has an entry in our cache |
601 | AssetInfo asset = Assets[requestID]; | 487 | AssetInfo asset = (AssetInfo)m_memcache[requestID]; |
602 | 488 | ||
603 | // FIXME: We never tell the client about assets which do not exist when requested by this transfer mechanism, which can't be right. | 489 | // FIXME: We never tell the client about assets which do not exist when requested by this transfer mechanism, which can't be right. |
604 | if (null == asset) | 490 | if (null == asset) |
diff --git a/OpenSim/Region/ClientStack/ClientStackManager.cs b/OpenSim/Region/ClientStack/ClientStackManager.cs index 5723739..098466d 100644 --- a/OpenSim/Region/ClientStack/ClientStackManager.cs +++ b/OpenSim/Region/ClientStack/ClientStackManager.cs | |||
@@ -48,22 +48,32 @@ namespace OpenSim.Region.Environment | |||
48 | { | 48 | { |
49 | m_log.Info("[CLIENTSTACK]: Attempting to load " + dllName); | 49 | m_log.Info("[CLIENTSTACK]: Attempting to load " + dllName); |
50 | 50 | ||
51 | plugin = null; | 51 | try |
52 | pluginAssembly = Assembly.LoadFrom(dllName); | ||
53 | |||
54 | foreach (Type pluginType in pluginAssembly.GetTypes()) | ||
55 | { | 52 | { |
56 | if (pluginType.IsPublic) | 53 | plugin = null; |
57 | { | 54 | pluginAssembly = Assembly.LoadFrom(dllName); |
58 | Type typeInterface = pluginType.GetInterface("IClientNetworkServer", true); | ||
59 | 55 | ||
60 | if (typeInterface != null) | 56 | foreach (Type pluginType in pluginAssembly.GetTypes()) |
57 | { | ||
58 | if (pluginType.IsPublic) | ||
61 | { | 59 | { |
62 | m_log.Info("[CLIENTSTACK]: Added IClientNetworkServer Interface"); | 60 | Type typeInterface = pluginType.GetInterface("IClientNetworkServer", true); |
63 | plugin = pluginType; | 61 | |
64 | return; | 62 | if (typeInterface != null) |
63 | { | ||
64 | m_log.Info("[CLIENTSTACK]: Added IClientNetworkServer Interface"); | ||
65 | plugin = pluginType; | ||
66 | return; | ||
67 | } | ||
65 | } | 68 | } |
66 | } | 69 | } |
70 | } catch (ReflectionTypeLoadException e) | ||
71 | { | ||
72 | foreach(Exception e2 in e.LoaderExceptions) | ||
73 | { | ||
74 | m_log.Error(e2.ToString()); | ||
75 | } | ||
76 | throw e; | ||
67 | } | 77 | } |
68 | } | 78 | } |
69 | 79 | ||
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index c953228..6ad4c2d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | |||
@@ -664,7 +664,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
664 | m_log.DebugFormat( | 664 | m_log.DebugFormat( |
665 | "[CLIENT]: Entered main packet processing loop for {0} {1}", FirstName, LastName); | 665 | "[CLIENT]: Entered main packet processing loop for {0} {1}", FirstName, LastName); |
666 | 666 | ||
667 | while (true) | 667 | while (IsActive) |
668 | { | 668 | { |
669 | LLQueItem nextPacket = m_PacketHandler.PacketQueue.Dequeue(); | 669 | LLQueItem nextPacket = m_PacketHandler.PacketQueue.Dequeue(); |
670 | 670 | ||