Index: OpenMetaverse.Http/EventQueueClient.cs =================================================================== --- OpenMetaverse.Http/EventQueueClient.cs (revision 2945) +++ OpenMetaverse.Http/EventQueueClient.cs (working copy) @@ -143,13 +143,19 @@ if (error is WebException && ((WebException)error).Response != null) code = ((HttpWebResponse)((WebException)error).Response).StatusCode; - if (code == HttpStatusCode.NotFound || code == HttpStatusCode.Gone) + if ( code == HttpStatusCode.Gone) { Logger.Log.InfoFormat("Closing event queue at {0} due to missing caps URI", _Address); _Running = false; _Dead = true; } + else if (code == HttpStatusCode.NotFound) + { + Logger.Log.InfoFormat("event queue at {0} got {1} message {2}", _Address, code, error.Message); + _Running = false; + _Dead = true; + } else if (code == HttpStatusCode.BadGateway) { // This is not good (server) protocol design, but it's normal. Index: OpenMetaverse/AgentManager.cs =================================================================== --- OpenMetaverse/AgentManager.cs (revision 2945) +++ OpenMetaverse/AgentManager.cs (working copy) @@ -1049,16 +1049,15 @@ if (sittingOn != 0) { Primitive parent; - if (Client.Network.CurrentSim != null && Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(sittingOn, out parent)) + if (Client.Network.CurrentSim != null) { - return parent.Position + Vector3.Transform(relativePosition, Matrix4.CreateFromQuaternion(parent.Rotation)); + lock (Client.Network.CurrentSim.ObjectsPrimitives.Dictionary) + if (Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(sittingOn, out parent)) + return parent.Position + Vector3.Transform(relativePosition, Matrix4.CreateFromQuaternion(parent.Rotation)); } - else - { - Logger.Log("Currently sitting on object " + sittingOn + " which is not tracked, SimPosition will be inaccurate", - Helpers.LogLevel.Warning, Client); - return relativePosition; - } + Logger.Log("Currently sitting on object " + sittingOn + " which is not tracked, SimPosition will be inaccurate", + Helpers.LogLevel.Warning, Client); + return relativePosition; } else { @@ -1076,16 +1075,15 @@ if (sittingOn != 0) { Primitive parent; - if (Client.Network.CurrentSim != null && Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(sittingOn, out parent)) + if (Client.Network.CurrentSim != null) { - return relativeRotation * parent.Rotation; + lock (Client.Network.CurrentSim.ObjectsPrimitives.Dictionary) + if (Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(sittingOn, out parent)) + return relativeRotation*parent.Rotation; } - else - { - Logger.Log("Currently sitting on object " + sittingOn + " which is not tracked, SimRotation will be inaccurate", - Helpers.LogLevel.Warning, Client); - return relativeRotation; - } + Logger.Log("Currently sitting on object " + sittingOn + " which is not tracked, SimRotation will be inaccurate", + Helpers.LogLevel.Warning, Client); + return relativeRotation; } else { Index: OpenMetaverse/AgentManagerMovement.cs =================================================================== --- OpenMetaverse/AgentManagerMovement.cs (revision 2945) +++ OpenMetaverse/AgentManagerMovement.cs (working copy) @@ -671,7 +671,7 @@ else agentControls &= ~((uint)flag); } - private void ResetControlFlags() + public void ResetControlFlags() { // Reset all of the flags except for persistent settings like // away, fly, mouselook, and crouching Index: OpenMetaverse/AppearanceManager.cs =================================================================== --- OpenMetaverse/AppearanceManager.cs (revision 2945) +++ OpenMetaverse/AppearanceManager.cs (working copy) @@ -110,6 +110,10 @@ /// public event AppearanceUpdatedCallback OnAppearanceUpdated; + /// + /// Time that wearable events will wait (140 secs) + /// + public static int WEARABLE_TIMEOUT = 140 * 1000; /// Total number of wearables for each avatar public const int WEARABLE_COUNT = 13; /// Total number of baked textures on each avatar @@ -240,7 +244,7 @@ { WearableData wearable; - if (Wearables.TryGetValue(type, out wearable)) + if (Wearables.Dictionary.TryGetValue(type, out wearable)) return wearable.Item.AssetUUID; else return UUID.Zero; @@ -414,9 +418,19 @@ List attachments; if (!GetFolderWearables(wearOutfitParams.Param, out wearables, out attachments)) // get wearables in outfit folder - return; // TODO: this error condition should be passed back to the client somehow + // TODO: this error condition should be passed back to the client somehow} + { + Logger.Log( + "GetFolderWearables did not find wearables " + + wearOutfitParams.Param + " " + wearables + " " + + attachments, Helpers.LogLevel.Error, Client); + return; + } - WearablesRequestEvent.WaitOne(); // wait for current wearables + if (!WearablesRequestEvent.WaitOne(WEARABLE_TIMEOUT*2)) // wait for current wearables + { + Logger.Log("StartWearOutfitFolder timed out.", Helpers.LogLevel.Error, Client); + } ReplaceOutfitWearables(wearables); // replace current wearables with outfit folder UpdateAppearanceFromWearables(wearOutfitParams.Bake); AddAttachments(attachments, wearOutfitParams.RemoveExistingAttachments); @@ -433,7 +447,7 @@ string[] path = (string[])_folder; folder = Client.Inventory.FindObjectByPath( - Client.Inventory.Store.RootFolder.UUID, Client.Self.AgentID, String.Join("/", path), 1000 * 20); + Client.Inventory.Store.RootFolder.UUID, Client.Self.AgentID, String.Join("/", path), 1000 * 200); if (folder == UUID.Zero) { @@ -455,22 +469,22 @@ { if (ib is InventoryWearable) { - Logger.DebugLog("Adding wearable " + ib.Name, Client); + Logger.Log("Adding wearable " + ib.Name, Client); wearables.Add((InventoryWearable)ib); } else if (ib is InventoryAttachment) { - Logger.DebugLog("Adding attachment (attachment) " + ib.Name, Client); + Logger.Log("Adding attachment (attachment) " + ib.Name, Client); attachments.Add(ib); } else if (ib is InventoryObject) { - Logger.DebugLog("Adding attachment (object) " + ib.Name, Client); + Logger.Log("Adding attachment (object) " + ib.Name, Client); attachments.Add(ib); } else { - Logger.DebugLog("Ignoring inventory item " + ib.Name, Client); + Logger.Log("Ignoring inventory item " + ib.Name, Client); } } } @@ -669,13 +683,18 @@ SendAgentIsNowWearing(); // Wait for cached layer check to finish - if (bake) CachedResponseEvent.WaitOne(); + if (bake) if (!CachedResponseEvent.WaitOne(WEARABLE_TIMEOUT)) + { + Logger.Log("Giving up on CachedResponseEvent didnt complete (in time)", Client); + Assets.OnAssetUploaded -= uploadCallback; + return; + } // Unregister the image download and asset upload callbacks //Assets.OnImageReceived -= imageCallback; Assets.OnAssetUploaded -= uploadCallback; - Logger.DebugLog("CachedResponseEvent completed", Client); + Logger.Log("CachedResponseEvent completed", Client); #region Send Appearance @@ -705,7 +724,7 @@ } else if (face.TextureID != AgentTextures[i] && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) { - Logger.DebugLog("*** FACE is " + ((TextureIndex)i).ToString() + " " + face.TextureID.ToString() + " Agent Texture is " + AgentTextures[i].ToString()); + Logger.Log("*** FACE is " + ((TextureIndex)i).ToString() + " " + face.TextureID.ToString() + " Agent Texture is " + AgentTextures[i].ToString(),Client); match = false; //break; } @@ -730,7 +749,7 @@ SendAgentSetAppearance(); // Wait for the ObjectUpdate to come in for our avatar after changing appearance - if (UpdateEvent.WaitOne(1000 * 60, false)) + if (UpdateEvent.WaitOne(1000 * 130, false)) { if (OnAppearanceUpdated != null) { @@ -755,7 +774,7 @@ /// public void RequestCachedBakes() { - Logger.DebugLog("RequestCachedBakes()", Client); + Logger.Log("RequestCachedBakes()", Client); List> hashes = new List>(); @@ -768,6 +787,7 @@ for (int bakedIndex = 0; bakedIndex < BAKED_TEXTURE_COUNT; bakedIndex++) { // Don't do a cache request for a skirt bake if we're not wearing a skirt + lock (Wearables.Dictionary) if (bakedIndex == (int)BakeType.Skirt && (!Wearables.ContainsKey(WearableType.Skirt) || Wearables.Dictionary[WearableType.Skirt].Asset.AssetID == UUID.Zero)) continue; @@ -804,7 +824,7 @@ cache.WearableData[i].TextureIndex = (byte)hashes[i].Key; cache.WearableData[i].ID = hashes[i].Value; - Logger.DebugLog("Checking cache for index " + cache.WearableData[i].TextureIndex + + Logger.Log("Checking cache for index " + cache.WearableData[i].TextureIndex + ", ID: " + cache.WearableData[i].ID, Client); } @@ -825,6 +845,7 @@ for (int i = 0; i < BAKED_TEXTURE_COUNT; i++) { // Don't bake skirt if not wearing one + lock (Wearables.Dictionary) if (i == (int)BakeType.Skirt && (!Wearables.ContainsKey(WearableType.Skirt) || Wearables.Dictionary[WearableType.Skirt].Asset.AssetID == UUID.Zero)) { continue; @@ -884,11 +905,12 @@ private void SendAgentSetAppearance() { + const bool UseExtraParams = false; AgentSetAppearancePacket set = new AgentSetAppearancePacket(); set.AgentData.AgentID = Client.Self.AgentID; set.AgentData.SessionID = Client.Self.SessionID; set.AgentData.SerialNum = SetAppearanceSerialNum++; - set.VisualParam = new AgentSetAppearancePacket.VisualParamBlock[218]; + set.VisualParam = new AgentSetAppearancePacket.VisualParamBlock[UseExtraParams ? 258 : 218]; float AgentSizeVPHeight = 0.0f; float AgentSizeVPHeelHeight = 0.0f; @@ -952,6 +974,19 @@ ++vpIndex; } + else + { + if (UseExtraParams) + { + if ((vp.Wearable != null) && vp.Wearable.Equals("shape")) + { + set.VisualParam[vpIndex] = new AgentSetAppearancePacket.VisualParamBlock(); + set.VisualParam[vpIndex].ParamValue = Utils.FloatToByte(vp.DefaultValue, vp.MinValue, + vp.MaxValue); + ++vpIndex; + } + } + } } // Build the texture entry for our agent @@ -979,7 +1014,7 @@ Primitive.TextureEntryFace face = te.CreateFace((uint)texture.Key); face.TextureID = texture.Value; - Logger.DebugLog("Setting agent texture " + ((TextureIndex)texture.Key).ToString() + " to " + + Logger.Log("Setting agent texture " + ((TextureIndex)texture.Key).ToString() + " to " + texture.Value.ToString(), Client); } } @@ -1031,7 +1066,7 @@ set.WearableData[bakedIndex] = new AgentSetAppearancePacket.WearableDataBlock(); set.WearableData[bakedIndex].TextureIndex = (byte)bakedIndex; set.WearableData[bakedIndex].CacheID = hash; - Logger.DebugLog("Setting baked agent texture hash " + ((BakeType)bakedIndex).ToString() + " to " + hash, Client); + Logger.Log("Setting baked agent texture hash " + ((BakeType)bakedIndex).ToString() + " to " + hash, Client); } @@ -1042,7 +1077,7 @@ private void SendAgentIsNowWearing() { - Logger.DebugLog("SendAgentIsNowWearing()", Client); + Logger.Log("SendAgentIsNowWearing()", Client); AgentIsNowWearingPacket wearing = new AgentIsNowWearingPacket(); wearing.AgentData.AgentID = Client.Self.AgentID; @@ -1055,6 +1090,7 @@ wearing.WearableData[i] = new AgentIsNowWearingPacket.WearableDataBlock(); wearing.WearableData[i].WearableType = (byte)i; + lock (Wearables.Dictionary) if (Wearables.ContainsKey(type)) wearing.WearableData[i].ItemID = Wearables.Dictionary[type].Item.UUID; else @@ -1091,7 +1127,7 @@ { foreach (KeyValuePair kvp in Wearables.Dictionary) { - Logger.DebugLog("Requesting asset for wearable item " + kvp.Value.Item.WearableType + " (" + kvp.Value.Item.AssetUUID + ")", Client); + Logger.Log("Requesting asset for wearable item " + kvp.Value.Item.WearableType + " (" + kvp.Value.Item.AssetUUID + ")", Client); AssetDownloads.Enqueue(new PendingAssetDownload(kvp.Value.Item.AssetUUID, kvp.Value.Item.AssetType)); } } @@ -1144,7 +1180,7 @@ PendingUploads.Add(UUID.Combine(id, Client.Self.SecureSessionID), BakeTypeToAgentTextureIndex(bake.BakeType)); } - Logger.DebugLog(String.Format("Bake {0} completed. Uploading asset {1}", bake.BakeType, + Logger.Log(String.Format("Bake {0} completed. Uploading asset {1}", bake.BakeType, bake.BakedTexture.AssetID.ToString()), Client); } @@ -1157,7 +1193,7 @@ { if (!ImageDownloads.ContainsKey(image)) { - Logger.DebugLog("Downloading layer " + index.ToString(), Client); + Logger.Log("Downloading layer " + index.ToString(), Client); ImageDownloads.Add(image, index); } @@ -1184,7 +1220,7 @@ // Try and find this value in our collection of downloaded wearables foreach (WearableData data in Wearables.Dictionary.Values) { - if (data.Asset.Params.ContainsKey(vp.ParamID)) + if (data.Asset!=null && data.Asset.Params.ContainsKey(vp.ParamID)) { paramValues.Add(vp.ParamID, data.Asset.Params[vp.ParamID]); found = true; @@ -1293,7 +1329,7 @@ private void AgentCachedTextureResponseHandler(Packet packet, Simulator simulator) { - Logger.DebugLog("AgentCachedTextureResponseHandler()", Client); + Logger.Log("AgentCachedTextureResponseHandler()", Client); AgentCachedTextureResponsePacket response = (AgentCachedTextureResponsePacket)packet; @@ -1307,12 +1343,12 @@ { //UUID hash=new UUID(); // For each missing element we need to bake our own texture - Logger.DebugLog("Cache response, index: " + block.TextureIndex + ", ID: " + + Logger.Log("Cache response, index: " + block.TextureIndex + ", ID: " + block.TextureID.ToString(), Client); // FIXME: Use this. Right now we treat baked images on other sims as if they were missing string host = Utils.BytesToString(block.HostName); - if (host.Length > 0) Logger.DebugLog("Cached bake exists on foreign host " + host, Client); + if (host.Length > 0) Logger.Log("Cached bake exists on foreign host " + host, Client); BakeType bakeType = (BakeType)block.TextureIndex; @@ -1332,7 +1368,7 @@ if (!PendingBakes.ContainsKey(bakeType)) { - Logger.DebugLog("Initializing " + bakeType.ToString() + " bake with " + imageCount + " textures", Client); + Logger.Log("Initializing " + bakeType.ToString() + " bake with " + imageCount + " textures", Client); Dictionary paramValues=MakeParamValues(); // Build a dictionary of appearance parameter indices and values from the wearables @@ -1393,7 +1429,7 @@ { kvp.Value.Asset = (AssetWearable)asset; - Logger.DebugLog("Downloaded wearable asset " + kvp.Value.Asset.Name, Client); + Logger.Log("Downloaded wearable asset " + kvp.Value.Asset.Name, Client); if (!kvp.Value.Asset.Decode()) { @@ -1407,7 +1443,7 @@ { if (texture.Value != DEFAULT_AVATAR_TEXTURE) // this texture is not meant to be displayed { - Logger.DebugLog("Setting " + texture.Key + " to " + texture.Value, Client); + Logger.Log("Setting " + texture.Key + " to " + texture.Value, Client); AgentTextures[(int)texture.Key] = texture.Value; } } @@ -1470,7 +1506,7 @@ { if (state == TextureRequestState.Finished) { - Logger.DebugLog("Finished downloading texture for " + index.ToString(), Client); + Logger.Log("Finished downloading texture for " + index.ToString(), Client); OpenJPEG.DecodeToImage(assetTexture.AssetData, out assetTexture.Image); baked = PendingBakes[type].AddTexture(index, assetTexture, false); } @@ -1492,12 +1528,12 @@ { // This is a failsafe catch, as the upload completed callback should normally // be triggering the event - Logger.DebugLog("No pending downloads or uploads detected in OnImageReceived", Client); + Logger.Log("No pending downloads or uploads detected in OnImageReceived", Client); CachedResponseEvent.Set(); } else { - Logger.DebugLog("Pending uploads: " + PendingUploads.Count + ", pending downloads: " + + Logger.Log("Pending uploads: " + PendingUploads.Count + ", pending downloads: " + ImageDownloads.Count, Client); } @@ -1522,10 +1558,19 @@ { // Setup the TextureEntry with the new baked upload TextureIndex index = PendingUploads[upload.AssetID]; - AgentTextures[(int)index] = upload.AssetID; + int u = (int) index; + if (u < 0) + { + Logger.Log("Invalid Index, AgentTextures " + index.ToString() + " set to " + + upload.AssetID.ToString(), Client); + } + else + { + AgentTextures[u] = upload.AssetID; - Logger.DebugLog("Upload complete, AgentTextures " + index.ToString() + " set to " + - upload.AssetID.ToString(), Client); + Logger.Log("Upload complete, AgentTextures " + index.ToString() + " set to " + + upload.AssetID.ToString(), Client); + } } else { @@ -1535,12 +1580,12 @@ PendingUploads.Remove(upload.AssetID); - Logger.DebugLog("Pending uploads: " + PendingUploads.Count + ", pending downloads: " + + Logger.Log("Pending uploads: " + PendingUploads.Count + ", pending downloads: " + ImageDownloads.Count, Client); if (PendingUploads.Count == 0 && ImageDownloads.Count == 0) { - Logger.DebugLog("All pending image downloads and uploads complete", Client); + Logger.Log("All pending image downloads and uploads complete", Client); CachedResponseEvent.Set(); } @@ -1548,7 +1593,7 @@ else { // TEMP - Logger.DebugLog("Upload " + upload.AssetID.ToString() + " was not found in PendingUploads", Client); + Logger.Log("Upload " + upload.AssetID.ToString() + " was not found in PendingUploads", Client); } } } Index: OpenMetaverse/Assets/AssetTypes/AssetWearable.cs =================================================================== --- OpenMetaverse/Assets/AssetTypes/AssetWearable.cs (revision 2945) +++ OpenMetaverse/Assets/AssetTypes/AssetWearable.cs (working copy) @@ -118,22 +118,29 @@ if (fields[0] == "parameters") { int count = Int32.Parse(fields[1]) + stri; - for (; stri < count; ) + for (; stri < count;) { stri++; + if (stri >= lines.Length) return false; line = lines[stri].Trim(); fields = line.Split(' '); - int id = Int32.Parse(fields[0]); - if (fields[1] == ",") - fields[1] = "0"; - else - fields[1] = fields[1].Replace(',', '.'); + int id; + if (int.TryParse(fields[0], out id)) + { + if (fields.Length > 1) + { + if (fields[1] == ",") + fields[1] = "0"; + else + fields[1] = fields[1].Replace(',', '.'); - float weight = float.Parse(fields[1], System.Globalization.NumberStyles.Float, - Utils.EnUsCulture.NumberFormat); + float weight = float.Parse(fields[1], System.Globalization.NumberStyles.Float, + Utils.EnUsCulture.NumberFormat); - Params[id] = weight; + Params[id] = weight; + } + } } } else if (fields[0] == "textures") Index: OpenMetaverse/AvatarManager.cs =================================================================== --- OpenMetaverse/AvatarManager.cs (revision 2945) +++ OpenMetaverse/AvatarManager.cs (working copy) @@ -492,7 +492,7 @@ InternalDictionary signaledAnims = new InternalDictionary(); for(int i=0; i < anims.AnimationList.Length; i++) - signaledAnims.Add(anims.AnimationList[i].AnimID, anims.AnimationList[i].AnimSequenceID); + signaledAnims.Dictionary.Add(anims.AnimationList[i].AnimID, anims.AnimationList[i].AnimSequenceID); try { OnAvatarAnimation(anims.Sender.ID, signaledAnims); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } Index: OpenMetaverse/BVHDecoder.cs =================================================================== --- OpenMetaverse/BVHDecoder.cs (revision 2945) +++ OpenMetaverse/BVHDecoder.cs (working copy) @@ -343,14 +343,148 @@ } return m_keys; } + + public bool Equals(BinBVHAnimationReader other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return other.Loop.Equals(Loop) && other.OutPoint == OutPoint && other.InPoint == InPoint && other.Length == Length && other.HandPose == HandPose && other.JointCount == JointCount && Equals(other.joints, joints) && other.EaseInTime == EaseInTime && other.EaseOutTime == EaseOutTime && other.Priority == Priority && other.unknown1 == unknown1 && other.unknown0 == unknown0 && other.positionkeys == positionkeys && other.rotationkeys == rotationkeys; + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// The to compare with the current . + /// The parameter is null. + /// 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (BinBVHAnimationReader)) return false; + return Equals((BinBVHAnimationReader) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + int result = Loop.GetHashCode(); + result = (result*397) ^ OutPoint.GetHashCode(); + result = (result*397) ^ InPoint.GetHashCode(); + result = (result*397) ^ Length.GetHashCode(); + result = (result*397) ^ HandPose.GetHashCode(); + result = (result*397) ^ JointCount.GetHashCode(); + result = (result*397) ^ (joints != null ? joints.GetHashCode() : 0); + result = (result*397) ^ EaseInTime.GetHashCode(); + result = (result*397) ^ EaseOutTime.GetHashCode(); + result = (result*397) ^ Priority; + result = (result*397) ^ unknown1.GetHashCode(); + result = (result*397) ^ unknown0.GetHashCode(); + result = (result*397) ^ positionkeys; + result = (result*397) ^ rotationkeys; + return result; + } + } + + public static bool Equals(binBVHJoint[] arr1, binBVHJoint[] arr2) + { + if (arr1.Length == arr2.Length) + { + for (int i = 0; i < arr1.Length; i++) + if (!arr1[i].Equals(arr2[i])) + return false; + /* not same*/ + return true; + } + return false; + } + } + /// /// A Joint and it's associated meta data and keyframes /// public struct binBVHJoint { + public static bool Equals(binBVHJointKey[] arr1, binBVHJointKey[] arr2) + { + if (arr1.Length == arr2.Length) + { + for (int i = 0; i < arr1.Length; i++) + if (!Equals(arr1[i], arr2[i])) + return false; + /* not same*/ + return true; + } + return false; + } + public static bool Equals(binBVHJointKey arr1, binBVHJointKey arr2) + { + return (arr1.time == arr2.time && arr1.key_element == arr2.key_element); + } + + public bool Equals(binBVHJoint other) + { + return other.Priority == Priority && Equals(other.rotationkeys, rotationkeys) && Equals(other.Name, Name) && Equals(other.positionkeys, positionkeys); + } + /// + /// Indicates whether this instance and a specified object are equal. + /// + /// + /// true if and this instance are the same type and represent the same value; otherwise, false. + /// + /// Another object to compare to. + /// 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != typeof (binBVHJoint)) return false; + return Equals((binBVHJoint) obj); + } + + /// + /// Returns the hash code for this instance. + /// + /// + /// A 32-bit signed integer that is the hash code for this instance. + /// + /// 2 + public override int GetHashCode() + { + unchecked + { + int result = Priority; + result = (result*397) ^ (rotationkeys != null ? rotationkeys.GetHashCode() : 0); + result = (result*397) ^ (Name != null ? Name.GetHashCode() : 0); + result = (result*397) ^ (positionkeys != null ? positionkeys.GetHashCode() : 0); + return result; + } + } + + public static bool operator ==(binBVHJoint left, binBVHJoint right) + { + return left.Equals(right); + } + + public static bool operator !=(binBVHJoint left, binBVHJoint right) + { + return !left.Equals(right); + } + + /// /// Name of the Joint. Matches the avatar_skeleton.xml in client distros /// public string Name; Index: OpenMetaverse/EventDictionary.cs =================================================================== --- OpenMetaverse/EventDictionary.cs (revision 2945) +++ OpenMetaverse/EventDictionary.cs (working copy) @@ -34,11 +34,14 @@ namespace OpenMetaverse { + public delegate bool SkipEventCallback(PacketType type, Simulator sim); /// /// Registers, unregisters, and fires events generated by incoming packets /// public class PacketEventDictionary { + public event SkipEventCallback SkipEvent; + /// /// Object that is passed to worker threads in the ThreadPool for /// firing packet callbacks @@ -112,6 +115,7 @@ { NetworkManager.PacketCallback callback; + if (SkipEvent != null && SkipEvent(packetType, simulator)) return; // Default handler first, if one exists if (_EventTable.TryGetValue(PacketType.Default, out callback)) { @@ -135,7 +139,7 @@ if (packetType != PacketType.Default && packetType != PacketType.PacketAck) { - Logger.DebugLog("No handler registered for packet event " + packetType, Client); + Logger.Log("No handler registered for packet event " + packetType, Client); } } @@ -147,6 +151,7 @@ /// Simulator this packet was received from internal void BeginRaiseEvent(PacketType packetType, Packet packet, Simulator simulator) { + if (SkipEvent != null && SkipEvent(packetType, simulator)) return; NetworkManager.PacketCallback callback; PacketCallbackWrapper wrapper; @@ -177,7 +182,7 @@ if (packetType != PacketType.Default && packetType != PacketType.PacketAck) { - Logger.DebugLog("No handler registered for packet event " + packetType, Client); + Logger.Log("No handler registered for packet event " + packetType, Client); } } Index: OpenMetaverse/GroupManager.cs =================================================================== --- OpenMetaverse/GroupManager.cs (revision 2945) +++ OpenMetaverse/GroupManager.cs (working copy) @@ -488,35 +488,37 @@ /// /// returned by RequestGroupMembers /// of the group + /// Total number of members in the group /// A dictionary containing the members of a group /// where key is member and value is struct - public delegate void GroupMembersCallback(UUID requestID, UUID groupID, Dictionary members); + public delegate void GroupMembersCallback(UUID requestID, UUID groupID, int memberCount, Dictionary members); /// /// Callback for retrieving group roles /// /// of the request returned from RequestGroupRoles /// of the group + /// Total number of roles defined in the group /// A dictionary containing role s as the key /// and structs as values - public delegate void GroupRolesCallback(UUID requestID, UUID groupID, Dictionary roles); + public delegate void GroupRolesCallback(UUID requestID, UUID groupID, int roleCount, Dictionary roles); /// /// Callback for a pairing of roles to members /// /// of the request returned from RequestGroupRolesMembers /// of the group + /// Total number of role/member mappings /// List containing role/member pairs - public delegate void GroupRolesMembersCallback(UUID requestID, UUID groupID, List> rolesMembers); + public delegate void GroupRolesMembersCallback(UUID requestID, UUID groupID, uint totalPairs, List> rolesMembers); /// /// Callback for the title list of a group /// - /// of the request returned from RequestGroupTitles /// Group /// A dictionary containing the titles of a group /// where the Key is the role , and the values are the title details - public delegate void GroupTitlesCallback(UUID requestID, UUID groupID, Dictionary titles); + public delegate void GroupTitlesCallback(UUID groupID, Dictionary titles); /// /// Callback fired when group account summary information is received @@ -1207,7 +1209,7 @@ group.Contribution = msg.GroupDataBlock[i].Contribution; group.AcceptNotices = msg.GroupDataBlock[i].AcceptNotices; group.Powers = msg.GroupDataBlock[i].GroupPowers; - group.ListInProfile = msg.NewGroupDataBlock[i].ListInProfile; + // group.ListInProfile = msg.NewGroupDataBlock[i].ListInProfile; currentGroups.Add(group.ID, group); @@ -1280,7 +1282,7 @@ groupTitleCache[block.RoleID] = groupTitle; } - try { OnGroupTitles(titles.AgentData.RequestID, titles.AgentData.GroupID, groupTitleCache); } + try { OnGroupTitles(titles.AgentData.GroupID, groupTitleCache); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } @@ -1326,9 +1328,9 @@ } } - if (OnGroupMembers != null && groupMemberCache != null && groupMemberCache.Count >= members.GroupData.MemberCount) + if (OnGroupMembers != null && groupMemberCache != null) { - try { OnGroupMembers(members.GroupData.RequestID, members.GroupData.GroupID, groupMemberCache); } + try { OnGroupMembers(members.GroupData.RequestID, members.GroupData.GroupID, members.GroupData.MemberCount, groupMemberCache); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } @@ -1376,9 +1378,9 @@ } } - if (OnGroupRoles != null && groupRoleCache != null && groupRoleCache.Count >= roles.GroupData.RoleCount) + if (OnGroupRoles != null && groupRoleCache != null) { - try { OnGroupRoles(roles.GroupData.RequestID, roles.GroupData.GroupID, groupRoleCache); } + try { OnGroupRoles(roles.GroupData.RequestID, roles.GroupData.GroupID, roles.GroupData.RoleCount, groupRoleCache); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } @@ -1425,9 +1427,9 @@ Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } - if (OnGroupRolesMembers != null && groupRoleMemberCache != null && groupRoleMemberCache.Count >= members.AgentData.TotalPairs) + if (OnGroupRolesMembers != null && groupRoleMemberCache != null) { - try { OnGroupRolesMembers(members.AgentData.RequestID, members.AgentData.GroupID, groupRoleMemberCache); } + try { OnGroupRolesMembers(members.AgentData.RequestID, members.AgentData.GroupID, members.AgentData.TotalPairs, groupRoleMemberCache); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } Index: OpenMetaverse/Imaging/BakeLayer.cs =================================================================== --- OpenMetaverse/Imaging/BakeLayer.cs (revision 2945) +++ OpenMetaverse/Imaging/BakeLayer.cs (working copy) @@ -125,7 +125,8 @@ } } - _textures.Add(index, texture); + //_textures.Add(index, texture); + _textures[index] = texture; Logger.DebugLog(String.Format("Added texture {0} (ID: {1}) to bake {2}", index, texture.AssetID, _bakeType), _client); } Index: OpenMetaverse/InternalDictionary.cs =================================================================== --- OpenMetaverse/InternalDictionary.cs (revision 2945) +++ OpenMetaverse/InternalDictionary.cs (working copy) @@ -26,6 +26,8 @@ using System; using System.Collections.Generic; +using System.Reflection; +using System.Threading; namespace OpenMetaverse { @@ -43,7 +45,7 @@ /// Internal dictionary that this class wraps around. Do not /// modify or enumerate the contents of this dictionary without locking /// on this member - internal Dictionary Dictionary; + public Dictionary Dictionary; /// /// Gets the number of Key/Value pairs contained in the @@ -125,6 +127,7 @@ /// public bool TryGetValue(TKey key, out TValue value) { + ConfirmOuterModifierIsLocking(); lock (Dictionary) { return Dictionary.TryGetValue(key, out value); @@ -274,6 +277,7 @@ /// if found, otherwise public bool ContainsKey(TKey key) { + ConfirmOuterModifierIsLocking(); return Dictionary.ContainsKey(key); } @@ -282,6 +286,7 @@ /// if found, otherwise public bool ContainsValue(TValue value) { + ConfirmOuterModifierIsLocking(); return Dictionary.ContainsValue(value); } @@ -293,6 +298,7 @@ /// The value internal void Add(TKey key, TValue value) { + ConfirmOuterModifierIsLocking(); lock (Dictionary) Dictionary.Add(key, value); } @@ -304,6 +310,7 @@ /// if successful, otherwise internal bool Remove(TKey key) { + ConfirmOuterModifierIsLocking(); lock (Dictionary) return Dictionary.Remove(key); } @@ -317,14 +324,44 @@ { get { + ConfirmOuterModifierIsLocking(); lock (Dictionary) return Dictionary[key]; } internal set { + ConfirmOuterModifierIsLocking(); lock (Dictionary) Dictionary[key] = value; } } + private void ConfirmOuterModifierIsLocking() + { + System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace(2); + System.Diagnostics.StackFrame sf = st.GetFrame(0); + MethodBase mb = sf.GetMethod(); + if (!mb.DeclaringType.Namespace.StartsWith("OpenMetaverse")) return; + + bool badGuy = false; + Thread t = new Thread(() => + { + if (!Monitor.TryEnter(Dictionary)) + { + //all is good! + } + else + { + Monitor.Exit(Dictionary); + //throw badGuy; + badGuy = true; + } + }); + t.Start(); + t.Join(); + if (!badGuy) return; + throw new Exception(string.Format("{0}.{1} forgot to lock!", mb.DeclaringType.FullName, mb.Name)); + } + + } } Index: OpenMetaverse/Logger.cs =================================================================== --- OpenMetaverse/Logger.cs (revision 2945) +++ OpenMetaverse/Logger.cs (working copy) @@ -82,6 +82,10 @@ Log(message, level, null, null); } + public static void Log(object message, GridClient client) + { + Log(message, Helpers.LogLevel.Info, client); + } /// /// Send a log message to the logging engine /// @@ -183,5 +187,10 @@ LogInstance.Debug(message); } } + + public static void Log(string message) + { + Log(message, Helpers.LogLevel.Info); + } } } Index: OpenMetaverse/Login.cs =================================================================== --- OpenMetaverse/Login.cs (revision 2945) +++ OpenMetaverse/Login.cs (working copy) @@ -1083,7 +1083,7 @@ InternalStatusCode = status; InternalLoginMessage = message; - Logger.DebugLog("Login status: " + status.ToString() + ": " + message, Client); + Logger.Log("Login status: " + status.ToString() + ": " + message, Client); // If we reached a login resolution trigger the event if (status == LoginStatus.Success || status == LoginStatus.Failed) Index: OpenMetaverse/Messages/LindenMessages.cs =================================================================== --- OpenMetaverse/Messages/LindenMessages.cs (revision 2945) +++ OpenMetaverse/Messages/LindenMessages.cs (working copy) @@ -1167,7 +1167,7 @@ AgentID = agentMap["AgentID"].AsUUID(); OSDArray groupArray = (OSDArray)map["GroupData"]; - OSDArray newGroupArray = (OSDArray)map["NewGroupData"]; + //OSDArray newGroupArray = (OSDArray)map["NewGroupData"]; GroupDataBlock = new GroupData[groupArray.Count]; @@ -1187,15 +1187,15 @@ GroupDataBlock[i] = groupData; } - NewGroupDataBlock = new NewGroupData[newGroupArray.Count]; + //NewGroupDataBlock = new NewGroupData[newGroupArray.Count]; - for (int i = 0; i < newGroupArray.Count; i++) - { - OSDMap newGroupMap = (OSDMap)newGroupArray[i]; - NewGroupData newGroupData = new NewGroupData(); - newGroupData.ListInProfile = newGroupMap["ListInProfile"].AsBoolean(); - NewGroupDataBlock[i] = newGroupData; - } + //for (int i = 0; i < newGroupArray.Count; i++) + //{ + // OSDMap newGroupMap = (OSDMap)newGroupArray[i]; + // NewGroupData newGroupData = new NewGroupData(); + // newGroupData.ListInProfile = newGroupMap["ListInProfile"].AsBoolean(); + // NewGroupDataBlock[i] = newGroupData; + //} } } Index: OpenMetaverse/NetworkManager.cs =================================================================== --- OpenMetaverse/NetworkManager.cs (revision 2945) +++ OpenMetaverse/NetworkManager.cs (working copy) @@ -64,6 +64,8 @@ ClientInitiated, /// The server notified us that it is disconnecting ServerInitiated, + /// The server notified us that it is disconnecting + TeleportInitiated, /// Either a socket was closed or network traffic timed out NetworkTimeout, /// The last active simulator shut down @@ -267,7 +269,7 @@ /// Handlers for incoming capability events internal CapsEventDictionary CapsEvents; /// Handlers for incoming packets - internal PacketEventDictionary PacketEvents; + public PacketEventDictionary PacketEvents; /// Incoming packets that are awaiting handling internal BlockingQueue PacketInbox = new BlockingQueue(Settings.PACKET_INBOX_SIZE); /// Outgoing packets that are awaiting handling @@ -292,6 +294,7 @@ // Register internal CAPS callbacks RegisterEventCallback("EnableSimulator", new Caps.EventQueueCallback(EnableSimulatorHandler)); + RegisterEventCallback("DisableSimulator", new Caps.EventQueueCallback(DisableSimulatorCapsHandler)); // Register the internal callbacks RegisterCallback(PacketType.RegionHandshake, new PacketCallback(RegionHandshakeHandler)); @@ -414,7 +417,7 @@ if (simulator == null) { // We're not tracking this sim, create a new Simulator object - simulator = new Simulator(Client, endPoint, handle); + simulator = Simulator.Create(Client, endPoint, handle); // Immediately add this simulator to the list of current sims. It will be removed if the // connection fails @@ -482,9 +485,14 @@ } else { + //if (OnSimDisconnected != null) + //{ + // try { OnSimDisconnected(simulator,DisconnectType.NetworkTimeout); } + // catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } + //} // Connection failed, remove this simulator from our list and destroy it - lock (Simulators) Simulators.Remove(simulator); - return null; + // lock (Simulators) Simulators.Remove(simulator); + return simulator; } } else if (setDefault) @@ -564,22 +572,23 @@ /// /// /// - public void DisconnectSim(Simulator sim, bool sendCloseCircuit) + public void DisconnectSim(Simulator sim, bool sendCloseCircuit,DisconnectType disconnectType) { if (sim != null) { - sim.Disconnect(sendCloseCircuit); + sim.Disconnect(sendCloseCircuit, disconnectType); // Fire the SimDisconnected event if a handler is registered if (OnSimDisconnected != null) { - try { OnSimDisconnected(sim, DisconnectType.NetworkTimeout); } + try { OnSimDisconnected(sim, disconnectType); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } - lock (Simulators) Simulators.Remove(sim); + //if (disconnectType == DisconnectType.ClientInitiated) + lock (Simulators) Simulators.Remove(sim); - if (Simulators.Count == 0) Shutdown(DisconnectType.SimShutdown); + if (Simulators.Count == 0) Shutdown(disconnectType/* | DisconnectType.SimShutdown*/); } else { @@ -623,7 +632,7 @@ if (CurrentSim != null) { // Kill the connection to the curent simulator - CurrentSim.Disconnect(sendCloseCircuit); + CurrentSim.Disconnect(sendCloseCircuit,type); // Fire the SimDisconnected event if a handler is registered if (OnSimDisconnected != null) @@ -780,6 +789,7 @@ Timer timer = DisconnectTimer; if (timer != null) timer.Dispose(); + timer = null; connected = false; } else if (CurrentSim.DisconnectCandidate) @@ -1034,11 +1044,18 @@ } } + private void DisableSimulatorCapsHandler(string capsKey, IMessage message, Simulator simulator) + { + if (!Client.Settings.MULTIPLE_SIMS) return; + Logger.Log("Received a CAPS Based DisableSimulator packet from " + simulator + ", shutting it down", Client); + DisconnectSim(simulator, false, DisconnectType.ServerInitiated); + } + private void DisableSimulatorHandler(Packet packet, Simulator simulator) { Logger.DebugLog("Received a DisableSimulator packet from " + simulator + ", shutting it down", Client); - DisconnectSim(simulator, false); + DisconnectSim(simulator, false, DisconnectType.ServerInitiated); } private void KickUserHandler(Packet packet, Simulator simulator) Index: OpenMetaverse/ObjectManager.cs =================================================================== --- OpenMetaverse/ObjectManager.cs (revision 2945) +++ OpenMetaverse/ObjectManager.cs (working copy) @@ -2863,6 +2863,10 @@ protected void InterpolationTimer_Elapsed(object obj) { int elapsed = 0; + if (!Client.Settings.USE_INTERPOLATION_TIMER) + { + return; + } if (Client.Network.Connected) { Index: OpenMetaverse/ParcelManager.cs =================================================================== --- OpenMetaverse/ParcelManager.cs (revision 2945) +++ OpenMetaverse/ParcelManager.cs (working copy) @@ -411,6 +411,73 @@ #endregion Structs + /// + /// Parcel Media Command used in ParcelMediaCommandMessage + /// + public enum ParcelMediaCommand: uint + { + /// + /// 0 - stop the media stream and go back to the first frame + /// + Stop = 0, + /// + /// 1 - pause the media stream (stop playing but stay on current frame) + /// + Pause, + /// + /// 2 - start the current media stream playing and stop when the end is reached + /// + Play, + /// + /// 3 - start the current media stream playing, + /// loop to the beginning when the end is reached and continue to play + /// + Loop, + /// + /// 4 - [key texture] specifies the texture to replace with video + /// Note: If passing the key of a texture, it must be explicitly typecast as a key, + /// not just passed within double quotes. + /// + Texture, + /// + /// 5 - [string url] specifies the movie URL (254 characters) + /// + URL, + /// + /// 6 - [float time] specifies the time index at which to begin playing + /// + Time, + /// + /// 7 - [key agent] specifies a single agent to apply the media command to + /// + Agent, + /// + /// 8 - unloads the stream. While the stop command sets the texture to the first frame of the movie, + /// unload resets it to the real texture that the movie was replacing. + /// + Unload, + /// + /// 9 - [integer enable] + /// turns on/off the auto align feature, similar to the auto align checkbox in the parcel media properties + /// (NOT to be confused with the "align" function in the textures view of the editor!) Takes TRUE or FALSE as parameter. + /// + AutoAlign, + /// + /// 10 - [string] Allows a Web page or image to be placed on a prim (1.19.1 RC0 and later only). + /// Use "text/html" for HTML. + /// + Type, + /// + /// 11 - [integer x, integer y] Resizes a Web page to fit on x, y pixels (1.19.1 RC0 and later only). + /// Note: this might still not be working + /// + Size, + /// + /// 12 - [string] Sets a description for the media being displayed (1.19.1 RC0 and later only). + /// + Desc + } + #region Parcel Class /// @@ -839,6 +906,16 @@ /// A struct containing updated media information public delegate void ParcelMediaUpdateReplyCallback(Simulator simulator, ParcelMedia media); + /// + /// Fired when a ParcelMediaCommandMessage packet is received, this occurs when the media on the parcel sends a specialized event + /// + /// A reference to the simulator object + /// The sequence the parcel command belongs to + /// Updated parcel information + /// The command executed on the Parcel + /// The time operand for some parcel commands + public delegate void ParcelMediaCommandMessageCallback(Simulator simulator, uint sequence, ParcelFlags flags, ParcelMediaCommand command, float time); + #endregion Delegates #region Events @@ -867,6 +944,10 @@ /// Fired when the Agent receives a which /// occurs when the parcel media information is changed for the current parcel the Agent is over public event ParcelMediaUpdateReplyCallback OnParcelMediaUpdate; + /// Fired when the Agent receives a which + /// occurs when the parcel media has a specialized event like starting and looping command on the media is raised + /// for the current parcel the Agent is over + public event ParcelMediaCommandMessageCallback OnParcelMediaCommandMessage; #endregion Events @@ -891,6 +972,7 @@ Client.Network.RegisterCallback(PacketType.ParcelAccessListReply, new NetworkManager.PacketCallback(ParcelAccessListReplyHandler)); Client.Network.RegisterCallback(PacketType.ForceObjectSelect, new NetworkManager.PacketCallback(SelectParcelObjectsReplyHandler)); Client.Network.RegisterCallback(PacketType.ParcelMediaUpdate, new NetworkManager.PacketCallback(ParcelMediaUpdateHandler)); + Client.Network.RegisterCallback(PacketType.ParcelMediaCommandMessage, new NetworkManager.PacketCallback(ParcelMediaCommandMessageHandler)); Client.Network.RegisterCallback(PacketType.ParcelOverlay, new NetworkManager.PacketCallback(ParcelOverlayHandler)); } @@ -1792,6 +1874,17 @@ } } + private void ParcelMediaCommandMessageHandler(Packet packet, Simulator simulator) + { + if (OnParcelMediaCommandMessage != null) + { + ParcelMediaCommandMessagePacket pmc = (ParcelMediaCommandMessagePacket)packet; + ParcelMediaCommandMessagePacket.CommandBlockBlock block = pmc.CommandBlock; + try { OnParcelMediaCommandMessage(simulator, pmc.Header.Sequence, (ParcelFlags)block.Flags, (ParcelMediaCommand)block.Command, block.Time); } + catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } + } + } + private void ParcelOverlayHandler(Packet packet, Simulator simulator) { const int OVERLAY_COUNT = 4; Index: OpenMetaverse/Simulator.cs =================================================================== --- OpenMetaverse/Simulator.cs (revision 2945) +++ OpenMetaverse/Simulator.cs (working copy) @@ -448,18 +448,46 @@ /// Reference to the GridClient object /// IPEndPoint of the simulator /// handle of the simulator - public Simulator(GridClient client, IPEndPoint address, ulong handle) + private Simulator(GridClient client, IPEndPoint address, ulong handle) : base(address) { Client = client; Handle = handle; + ReInit(); + } + + private void ReInit() + { Network = Client.Network; PacketArchive = new IncomingPacketIDCollection(Settings.PACKET_ARCHIVE_SIZE); InBytes = new Queue(Client.Settings.STATS_QUEUE_SIZE); OutBytes = new Queue(Client.Settings.STATS_QUEUE_SIZE); } + + readonly static Dictionary> savedSims = new Dictionary>(); + public static Simulator Create(GridClient client, IPEndPoint point, ulong handle) + { + Dictionary dict; + lock (savedSims) if (!savedSims.TryGetValue(client, out dict)) + { + dict = new Dictionary(); + savedSims.Add(client, dict); + } + Simulator sim; + lock (dict) if (!dict.TryGetValue(handle, out sim)) + { + sim = new Simulator(client, point, handle); + dict.Add(handle,sim); + } else + { + sim.ReInit(); + } + return sim; + + } + /// /// Called when this Simulator object is being destroyed /// @@ -529,7 +557,7 @@ if (Client.Settings.SEND_AGENT_UPDATES) Client.Self.Movement.SendUpdate(true, this); - if (!ConnectedEvent.WaitOne(Client.Settings.SIMULATOR_TIMEOUT, false)) + if (!ConnectedEvent.WaitOne(Client.Settings.LOGIN_TIMEOUT, false)) { Logger.Log("Giving up on waiting for RegionHandshake for " + this.ToString(), Helpers.LogLevel.Warning, Client); @@ -571,22 +599,36 @@ /// Disconnect from this simulator /// public void Disconnect(bool sendCloseCircuit) + { + Disconnect(sendCloseCircuit,NetworkManager.DisconnectType.ClientInitiated); + } + public void Disconnect(bool sendCloseCircuit, NetworkManager.DisconnectType disconnectType) { + //lets wait until the server makes a desision + if (disconnectType == NetworkManager.DisconnectType.TeleportInitiated + || disconnectType == NetworkManager.DisconnectType.NetworkTimeout) return; if (connected) { connected = false; + if (disconnectType != NetworkManager.DisconnectType.ServerInitiated) + { + // Destroy the timers if (AckTimer != null) AckTimer.Dispose(); + AckTimer = null; if (StatsTimer != null) StatsTimer.Dispose(); + StatsTimer = null; if (PingTimer != null) PingTimer.Dispose(); + PingTimer = null; // Kill the current CAPS system - if (Caps != null) + if (Caps != null ) { Caps.Disconnect(true); Caps = null; } + } if (sendCloseCircuit) { Index: OpenMetaverse/TexturePipeline.cs =================================================================== --- OpenMetaverse/TexturePipeline.cs (revision 2945) +++ OpenMetaverse/TexturePipeline.cs (working copy) @@ -130,13 +130,13 @@ /// An array of worker slots which shows the availablity status of the slot private readonly int[] threadpoolSlots; /// The primary thread which manages the requests. - private readonly Thread downloadMaster; + private Thread downloadMaster; /// true if the TexturePipeline is currently running bool _Running; /// A synchronization object used by the primary thread - private object lockerObject = new object(); + readonly private object lockerObject = new object(); /// A refresh timer used to increase the priority of stalled requests - private readonly System.Timers.Timer RefreshDownloadsTimer = + private System.Timers.Timer RefreshDownloadsTimer = new System.Timers.Timer(Settings.PIPELINE_REFRESH_INTERVAL); /// Current number of pending and in-process transfers @@ -168,12 +168,6 @@ client.Network.OnConnected += delegate { Startup(); }; client.Network.OnDisconnected += delegate { Shutdown(); }; - // Instantiate master thread that manages the request pool - downloadMaster = new Thread(DownloadThread); - downloadMaster.Name = "TexturePipeline"; - downloadMaster.IsBackground = true; - - RefreshDownloadsTimer.Elapsed += RefreshDownloadsTimer_Elapsed; } /// @@ -186,6 +180,21 @@ _Running = true; + if (downloadMaster==null) + { + // Instantiate master thread that manages the request pool + downloadMaster = new Thread(DownloadThread); + downloadMaster.Name = "TexturePipeline"; + downloadMaster.IsBackground = true; + } + if (RefreshDownloadsTimer==null) + { + RefreshDownloadsTimer = + new System.Timers.Timer(Settings.PIPELINE_REFRESH_INTERVAL); + RefreshDownloadsTimer.Elapsed += RefreshDownloadsTimer_Elapsed; + } + + _Client.Network.RegisterCallback(PacketType.ImageData, ImageDataHandler); _Client.Network.RegisterCallback(PacketType.ImagePacket, ImagePacketHandler); _Client.Network.RegisterCallback(PacketType.ImageNotInDatabase, ImageNotInDatabaseHandler); @@ -205,6 +214,8 @@ TotalTime, NetworkTime, Math.Round(TotalBytes / NetworkTime.TotalSeconds / 60, 2), TotalBytes), Helpers.LogLevel.Debug); #endif RefreshDownloadsTimer.Stop(); + RefreshDownloadsTimer = null; + downloadMaster = null; _Client.Network.UnregisterCallback(PacketType.ImageNotInDatabase, ImageNotInDatabaseHandler); _Client.Network.UnregisterCallback(PacketType.ImageData, ImageDataHandler); @@ -657,7 +668,10 @@ task.Transfer.ID, Helpers.LogLevel.Warning, _Client); _Transfers.Remove(task.Transfer.ID); - resetEvents[task.RequestSlot].Set(); // free up request slot + if (task.RequestSlot >= 0) resetEvents[task.RequestSlot].Set(); // free up request slot + else + Logger.Log("Negative slot in download for " + + task.Transfer.ID, Helpers.LogLevel.Warning, _Client); foreach (TextureDownloadCallback callback in task.Callbacks) callback(TextureRequestState.Timeout, new AssetTexture(task.RequestID, task.Transfer.AssetData)); Index: OpenMetaverseTypes/DoubleDictionary.cs =================================================================== --- OpenMetaverseTypes/DoubleDictionary.cs (revision 2945) +++ OpenMetaverseTypes/DoubleDictionary.cs (working copy) @@ -28,11 +28,11 @@ using System.Threading; using System.Collections.Generic; -#if VISUAL_STUDIO +//#if VISUAL_STUDIO using ReaderWriterLockImpl = System.Threading.ReaderWriterLockSlim; -#else -using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; -#endif +//#else +//using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; +//#endif namespace OpenMetaverse {