diff --git a/libsecondlife-cs/AvatarManager.cs b/libsecondlife-cs/AvatarManager.cs index cd74da6b..4d040f09 100644 --- a/libsecondlife-cs/AvatarManager.cs +++ b/libsecondlife-cs/AvatarManager.cs @@ -54,14 +54,12 @@ namespace libsecondlife private SecondLife Client; private Dictionary Avatars; - private Mutex AvatarsMutex; private AgentNamesCallback OnAgentNames; public AvatarManager(SecondLife client) { Client = client; Avatars = new Dictionary(); - AvatarsMutex = new Mutex(false, "AvatarsMutex"); // Friend notification callback PacketCallback callback = new PacketCallback(FriendNotificationHandler); @@ -76,26 +74,15 @@ namespace libsecondlife /// Filled-out Avatar class to insert public void AddAvatar(Avatar avatar) { - #region AvatarsMutex - AvatarsMutex.WaitOne(); - - Avatars[avatar.ID] = avatar; - - AvatarsMutex.ReleaseMutex(); - #endregion AvatarsMutex + lock (Avatars) + { + Avatars[avatar.ID] = avatar; + } } public bool Contains(LLUUID id) { - #region AvatarsMutex - AvatarsMutex.WaitOne(); - - bool contains = Avatars.ContainsKey(id); - - #endregion AvatarsMutex - AvatarsMutex.ReleaseMutex(); - - return contains; + return Avatars.ContainsKey(id); } /// @@ -105,22 +92,15 @@ namespace libsecondlife /// The avatar name, or an empty string if it's not found public string LocalAvatarNameLookup(LLUUID id) { - string name; + string name = ""; - #region AvatarsMutex - AvatarsMutex.WaitOne(); - - if (Avatars.ContainsKey(id)) + lock (Avatars) { - name = Avatars[id].Name; + if (Avatars.ContainsKey(id)) + { + name = Avatars[id].Name; + } } - else - { - name = ""; - } - - #endregion AvatarsMutex - AvatarsMutex.ReleaseMutex(); return name; } @@ -197,26 +177,23 @@ namespace libsecondlife Dictionary names = new Dictionary(); UUIDNameReplyPacket reply = (UUIDNameReplyPacket)packet; - #region AvatarsMutex - AvatarsMutex.WaitOne(); - - foreach (UUIDNameReplyPacket.UUIDNameBlockBlock block in reply.UUIDNameBlock) + lock (Avatars) { - if (!Avatars.ContainsKey(block.ID)) + foreach (UUIDNameReplyPacket.UUIDNameBlockBlock block in reply.UUIDNameBlock) { - Avatars[block.ID] = new Avatar(); - Avatars[block.ID].ID = block.ID; + if (!Avatars.ContainsKey(block.ID)) + { + Avatars[block.ID] = new Avatar(); + Avatars[block.ID].ID = block.ID; + } + + Avatars[block.ID].Name = Helpers.FieldToString(block.FirstName) + + " " + Helpers.FieldToString(block.LastName); + + names[block.ID] = Avatars[block.ID].Name; } - - Avatars[block.ID].Name = Helpers.FieldToString(block.FirstName) + - " " + Helpers.FieldToString(block.LastName); - - names[block.ID] = Avatars[block.ID].Name; } - AvatarsMutex.ReleaseMutex(); - #endregion AvatarsMutex - if (OnAgentNames != null) { OnAgentNames(names); @@ -232,23 +209,20 @@ namespace libsecondlife // If the agent is online... foreach (OnlineNotificationPacket.AgentBlockBlock block in ((OnlineNotificationPacket)packet).AgentBlock) { - #region AvatarsMutex - AvatarsMutex.WaitOne(); - - if (!Avatars.ContainsKey(block.AgentID)) + lock (Avatars) { - // Mark this avatar for a name request - requestids.Add(block.AgentID); + if (!Avatars.ContainsKey(block.AgentID)) + { + // Mark this avatar for a name request + requestids.Add(block.AgentID); - Avatars[block.AgentID] = new Avatar(); - Avatars[block.AgentID].ID = block.AgentID; + Avatars[block.AgentID] = new Avatar(); + Avatars[block.AgentID].ID = block.AgentID; + } + + Avatars[block.AgentID].Online = true; } - Avatars[block.AgentID].Online = true; - - AvatarsMutex.ReleaseMutex(); - #endregion AvatarsMutex - if (OnFriendNotification != null) { OnFriendNotification(block.AgentID, true); @@ -260,23 +234,20 @@ namespace libsecondlife // If the agent is Offline... foreach (OfflineNotificationPacket.AgentBlockBlock block in ((OfflineNotificationPacket)packet).AgentBlock) { - #region AvatarsMutex - AvatarsMutex.WaitOne(); - - if (!Avatars.ContainsKey(block.AgentID)) + lock (Avatars) { - // Mark this avatar for a name request - requestids.Add(block.AgentID); + if (!Avatars.ContainsKey(block.AgentID)) + { + // Mark this avatar for a name request + requestids.Add(block.AgentID); - Avatars[block.AgentID] = new Avatar(); - Avatars[block.AgentID].ID = block.AgentID; + Avatars[block.AgentID] = new Avatar(); + Avatars[block.AgentID].ID = block.AgentID; + } + + Avatars[block.AgentID].Online = false; } - Avatars[block.AgentID].Online = false; - - AvatarsMutex.ReleaseMutex(); - #endregion AvatarsMutex - if (OnFriendNotification != null) { OnFriendNotification(block.AgentID, true); diff --git a/libsecondlife-cs/GroupManager.cs b/libsecondlife-cs/GroupManager.cs index c174ee1c..ecbd6934 100644 --- a/libsecondlife-cs/GroupManager.cs +++ b/libsecondlife-cs/GroupManager.cs @@ -165,10 +165,8 @@ namespace libsecondlife private Dictionary GroupTitlesCallbacks; /// A list of all the lists of group members, indexed by the request ID private Dictionary> GroupMembersCaches; - private Mutex GroupMembersCachesMutex; /// A list of all the lists of group roles, indexed by the request ID private Dictionary> GroupRolesCaches; - private Mutex GroupRolesCachesMutex; /// /// @@ -184,9 +182,7 @@ namespace libsecondlife GroupTitlesCallbacks = new Dictionary(); GroupMembersCaches = new Dictionary>(); - GroupMembersCachesMutex = new Mutex(false, "GroupMembersCachesMutex"); GroupRolesCaches = new Dictionary>(); - GroupRolesCachesMutex = new Mutex(false, "GroupRolesCachesMutex"); Client.Network.RegisterCallback(PacketType.AgentGroupDataUpdate, new PacketCallback(GroupDataHandler)); Client.Network.RegisterCallback(PacketType.GroupTitlesReply, new PacketCallback(GroupTitlesHandler)); @@ -244,11 +240,10 @@ namespace libsecondlife { LLUUID requestID = LLUUID.GenerateUUID(); - #region GroupMembersCachesMutex - GroupMembersCachesMutex.WaitOne(); - GroupMembersCaches[requestID] = new Dictionary(); - GroupMembersCachesMutex.ReleaseMutex(); - #endregion GroupMembersCachesMutex + lock (GroupMembersCaches) + { + GroupMembersCaches[requestID] = new Dictionary(); + } GroupMembersCallbacks[group] = gmc; @@ -266,11 +261,10 @@ namespace libsecondlife { LLUUID requestID = LLUUID.GenerateUUID(); - #region GroupRolesCachesMutex - GroupRolesCachesMutex.WaitOne(); - GroupRolesCaches[requestID] = new Dictionary(); - GroupRolesCachesMutex.ReleaseMutex(); - #endregion GroupRolesCachesMutex + lock (GroupRolesCaches) + { + GroupRolesCaches[requestID] = new Dictionary(); + } GroupRolesCallbacks[group] = grc; @@ -321,7 +315,10 @@ namespace libsecondlife currentGroups[block.GroupID] = group; } - OnCurrentGroups(currentGroups); + if (OnCurrentGroups != null) + { + OnCurrentGroups(currentGroups); + } } } @@ -375,85 +372,68 @@ namespace libsecondlife private void GroupMembersHandler(Packet packet, Simulator simulator) { GroupMembersReplyPacket members = (GroupMembersReplyPacket)packet; + Dictionary groupMemberCache = null; - #region GroupMembersCachesMutex - GroupMembersCachesMutex.WaitOne(); - - // If nothing is registered to receive this RequestID drop the data - if (GroupMembersCaches.ContainsKey(members.GroupData.RequestID)) + lock (GroupMembersCaches) { - Dictionary groupMemberCache = GroupMembersCaches[members.GroupData.RequestID]; - - foreach (GroupMembersReplyPacket.MemberDataBlock block in members.MemberData) + // If nothing is registered to receive this RequestID drop the data + if (GroupMembersCaches.ContainsKey(members.GroupData.RequestID)) { - GroupMember groupMember = new GroupMember(block.AgentID); + groupMemberCache = GroupMembersCaches[members.GroupData.RequestID]; - groupMember.Contribution = block.Contribution; - groupMember.IsOwner = block.IsOwner; - groupMember.OnlineStatus = Helpers.FieldToString(block.OnlineStatus); - groupMember.Powers = block.AgentPowers; - groupMember.Title = Helpers.FieldToString(block.Title); + foreach (GroupMembersReplyPacket.MemberDataBlock block in members.MemberData) + { + GroupMember groupMember = new GroupMember(block.AgentID); - groupMemberCache[block.AgentID] = groupMember; - } + groupMember.Contribution = block.Contribution; + groupMember.IsOwner = block.IsOwner; + groupMember.OnlineStatus = Helpers.FieldToString(block.OnlineStatus); + groupMember.Powers = block.AgentPowers; + groupMember.Title = Helpers.FieldToString(block.Title); - // Release the mutex before the callback - GroupMembersCachesMutex.ReleaseMutex(); - - // Check if we've received all the group members that are showing up - if (groupMemberCache.Count >= members.GroupData.MemberCount) - { - GroupMembersCallbacks[members.GroupData.GroupID](groupMemberCache); + groupMemberCache[block.AgentID] = groupMember; + } } } - else - { - GroupMembersCachesMutex.ReleaseMutex(); - } - #endregion GroupMembersCachesMutex + // Check if we've received all the group members that are showing up + if (groupMemberCache != null && groupMemberCache.Count >= members.GroupData.MemberCount) + { + GroupMembersCallbacks[members.GroupData.GroupID](groupMemberCache); + } } private void GroupRoleDataHandler(Packet packet, Simulator simulator) { GroupRoleDataReplyPacket roles = (GroupRoleDataReplyPacket)packet; + Dictionary groupRoleCache = null; - #region GroupRolesCachesMutex - GroupRolesCachesMutex.WaitOne(); - - // If nothing is registered to receive this RequestID drop the data - if (GroupRolesCaches.ContainsKey(roles.GroupData.RequestID)) + lock (GroupRolesCaches) { - Dictionary groupRoleCache = GroupRolesCaches[roles.GroupData.RequestID]; - - foreach (GroupRoleDataReplyPacket.RoleDataBlock block in roles.RoleData) + // If nothing is registered to receive this RequestID drop the data + if (GroupRolesCaches.ContainsKey(roles.GroupData.RequestID)) { - GroupRole groupRole = new GroupRole(block.RoleID); + groupRoleCache = GroupRolesCaches[roles.GroupData.RequestID]; - groupRole.Description = Helpers.FieldToString(block.Description); - groupRole.Name = Helpers.FieldToString(block.Name); - groupRole.Powers = block.Powers; - groupRole.Title = Helpers.FieldToString(block.Title); + foreach (GroupRoleDataReplyPacket.RoleDataBlock block in roles.RoleData) + { + GroupRole groupRole = new GroupRole(block.RoleID); - groupRoleCache[block.RoleID] = groupRole; - } + groupRole.Description = Helpers.FieldToString(block.Description); + groupRole.Name = Helpers.FieldToString(block.Name); + groupRole.Powers = block.Powers; + groupRole.Title = Helpers.FieldToString(block.Title); - // Release the mutex before the callback - GroupRolesCachesMutex.ReleaseMutex(); - - // Check if we've received all the group members that are showing up - if (groupRoleCache.Count >= roles.GroupData.RoleCount) - { - GroupRolesCallbacks[roles.GroupData.GroupID](groupRoleCache); + groupRoleCache[block.RoleID] = groupRole; + } } } - else - { - GroupRolesCachesMutex.ReleaseMutex(); - } - GroupRolesCachesMutex.ReleaseMutex(); - #endregion GroupRolesCachesMutex + // Check if we've received all the group members that are showing up + if (groupRoleCache != null && groupRoleCache.Count >= roles.GroupData.RoleCount) + { + GroupRolesCallbacks[roles.GroupData.GroupID](groupRoleCache); + } } private void GroupRoleMembersHandler(Packet packet, Simulator simulator) diff --git a/libsecondlife-cs/InventorySystem/InventoryManager.cs b/libsecondlife-cs/InventorySystem/InventoryManager.cs index eebe9a13..78ef3a74 100644 --- a/libsecondlife-cs/InventorySystem/InventoryManager.cs +++ b/libsecondlife-cs/InventorySystem/InventoryManager.cs @@ -58,10 +58,10 @@ namespace libsecondlife.InventorySystem // UUID of Root Inventory Folder private LLUUID uuidRootFolder; - // Setup a hashtable to easily lookup folders by UUID + // Setup a dictionary to easily lookup folders by UUID private Dictionary htFoldersByUUID = new Dictionary(); - // Setup a Hashtable to track download progress + // Setup a dictionary to track download progress private Dictionary htFolderDownloadStatus; private List alFolderRequestQueue; diff --git a/libsecondlife-cs/JSON/JSONFacade.cs b/libsecondlife-cs/JSON/JSONFacade.cs index 916d8d98..f59819a6 100644 --- a/libsecondlife-cs/JSON/JSONFacade.cs +++ b/libsecondlife-cs/JSON/JSONFacade.cs @@ -12,7 +12,7 @@ namespace Nii.JSON public sealed class JsonFacade { /// - /// Parse JSON formatted string and return a Hashtable + /// Parse JSON formatted string and return a dictionary /// /// /// diff --git a/libsecondlife-cs/JSON/JSONObject.cs b/libsecondlife-cs/JSON/JSONObject.cs index 21d76aa0..56e9e33d 100644 --- a/libsecondlife-cs/JSON/JSONObject.cs +++ b/libsecondlife-cs/JSON/JSONObject.cs @@ -224,7 +224,7 @@ namespace Nii.JSON /// /// C# convenience method /// - /// The Hashtable + /// The Dictionary public Dictionary getDictionary() { return myHashMap; diff --git a/libsecondlife-cs/NetworkManager.cs b/libsecondlife-cs/NetworkManager.cs index 2f414d4c..e9d9d29c 100644 --- a/libsecondlife-cs/NetworkManager.cs +++ b/libsecondlife-cs/NetworkManager.cs @@ -126,14 +126,11 @@ namespace libsecondlife private Dictionary> Callbacks; private ushort Sequence; private byte[] RecvBuffer; - private Mutex RecvBufferMutex = new Mutex(false, "RecvBufferMutex"); private Socket Connection; private AsyncCallback ReceivedData; private Dictionary NeedAck; - private Mutex NeedAckMutex; private SortedList Inbox; private List PendingAcks; - private Mutex InboxMutex; private bool connected; private uint circuitCode; private IPEndPoint ipEndPoint; @@ -164,16 +161,13 @@ namespace libsecondlife AckTimer = new System.Timers.Timer(500); AckTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed); - // Initialize the hashtable for reliable packets waiting on ACKs from the server + // Initialize the dictionary for reliable packets waiting on ACKs from the server NeedAck = new Dictionary(); // Initialize the lists of sequence numbers we've received so far Inbox = new SortedList(); PendingAcks = new List(); - NeedAckMutex = new Mutex(false, "NeedAckMutex"); - InboxMutex = new Mutex(false, "InboxMutex"); - Client.Log("Connecting to " + ip.ToString() + ":" + port, Helpers.LogLevel.Info); try @@ -280,25 +274,22 @@ namespace libsecondlife // Keep track of when this packet was sent out packet.TickCount = Environment.TickCount; - #region NeedAckMutex - NeedAckMutex.WaitOne(); - if (!NeedAck.ContainsKey(packet.Header.Sequence)) + lock (NeedAck) { - NeedAck.Add(packet.Header.Sequence, packet); + if (!NeedAck.ContainsKey(packet.Header.Sequence)) + { + NeedAck.Add(packet.Header.Sequence, packet); + } + else + { + Client.Log("Attempted to add a duplicate sequence number (" + + packet.Header.Sequence + ") to the NeedAck dictionary for packet type " + + packet.Type.ToString(), Helpers.LogLevel.Warning); + } } - else - { - Client.Log("Attempted to add a duplicate sequence number (" + - packet.Header.Sequence + ") to the NeedAck hashtable for packet type " + - packet.Type.ToString(), Helpers.LogLevel.Warning); - } - NeedAckMutex.ReleaseMutex(); - #endregion NeedAckMutex // Append any ACKs that need to be sent out to this packet - #region InboxMutex - InboxMutex.WaitOne(); - try + lock (PendingAcks) { if (PendingAcks.Count > 0 && packet.Type != PacketType.PacketAck && packet.Type != PacketType.LogoutRequest) @@ -317,15 +308,6 @@ namespace libsecondlife packet.Header.AppendedAcks = true; } } - catch (Exception e) - { - Client.Log(e.ToString(), Helpers.LogLevel.Error); - } - finally - { - InboxMutex.ReleaseMutex(); - } - #endregion InboxMutex } } @@ -381,56 +363,36 @@ namespace libsecondlife // If we're receiving data the sim connection is open connected = true; - #region RecvBufferMutex - try + // Update the disconnect flag so this sim doesn't time out + DisconnectCandidate = false; + + lock (RecvBuffer) { - RecvBufferMutex.WaitOne(); - - // Update the disconnect flag so this sim doesn't time out - DisconnectCandidate = false; - // Retrieve the incoming packet try { numBytes = Connection.EndReceiveFrom(result, ref endPoint); + + int packetEnd = numBytes - 1; + packet = Packet.BuildPacket(RecvBuffer, ref packetEnd); + + Connection.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref endPoint, ReceivedData, null); } catch (SocketException) { - Client.Log("Socket error, shutting down " + this.Region.Name + - " (" + endPoint.ToString() + ")", Helpers.LogLevel.Warning); + Client.Log("Socket error, shutting down " + this.Region.Name + + " (" + endPoint.ToString() + ")", Helpers.LogLevel.Error); connected = false; - RecvBufferMutex.ReleaseMutex(); Network.DisconnectSim(this); - return; } - - int packetEnd = numBytes - 1; - packet = Packet.BuildPacket(RecvBuffer, ref packetEnd); } - catch (Exception e) - { - Client.Log(e.ToString(), Helpers.LogLevel.Error); - } - finally - { - RecvBufferMutex.ReleaseMutex(); - try - { - Connection.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref endPoint, ReceivedData, null); - } - catch (Exception) - { - } - } - #endregion RecvBufferMutex - - // Fail-safe checks + // Fail-safe check if (packet == null) { - Client.Log("OnReceivedData() fail-safe reached, exiting", Helpers.LogLevel.Warning); + Client.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning); return; } @@ -438,11 +400,8 @@ namespace libsecondlife if (packet.Header.Reliable) { // Check if we already received this packet - #region InboxMutex - try + lock (Inbox) { - InboxMutex.WaitOne(); - if (Inbox.ContainsKey(packet.Header.Sequence)) { Client.Log("Received a duplicate " + packet.Type.ToString() + ", sequence=" + @@ -458,61 +417,39 @@ namespace libsecondlife PendingAcks.Add((uint)packet.Header.Sequence); } } - catch (Exception e) - { - Client.Log(e.ToString(), Helpers.LogLevel.Error); - } - finally - { - InboxMutex.ReleaseMutex(); - } - #endregion InboxMutex } - // Handle ACK packets - if (packet.Header.AppendedAcks || packet.Type == PacketType.PacketAck) + // Handle appended ACKs + if (packet.Header.AppendedAcks) { - #region NeedAckMutex - try + lock (NeedAck) { - NeedAckMutex.WaitOne(); - - // Handle PacketAck packets - if (packet.Type == PacketType.PacketAck) + foreach (ushort ack in packet.Header.AckList) { - PacketAckPacket ackPacket = (PacketAckPacket)packet; - - foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets) + if (NeedAck.ContainsKey(ack)) { - NeedAck.Remove((ushort)block.ID); + NeedAck.Remove(ack); } - } - - // Handle appended ACKs - if (packet.Header.AppendedAcks) - { - foreach (ushort ack in packet.Header.AckList) + else { - if (NeedAck.ContainsKey(ack)) - { - NeedAck.Remove(ack); - } - else - { - Client.Log("Appended ACK for a packet we didn't send: " + ack, Helpers.LogLevel.Warning); - } + Client.Log("Appended ACK for a packet we didn't send: " + ack, Helpers.LogLevel.Warning); } } } - catch (Exception e) + } + + // Handle PacketAck packets + if (packet.Type == PacketType.PacketAck) + { + lock (NeedAck) { - Client.Log(e.ToString(), Helpers.LogLevel.Error); + PacketAckPacket ackPacket = (PacketAckPacket)packet; + + foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets) + { + NeedAck.Remove((ushort)block.ID); + } } - finally - { - NeedAckMutex.ReleaseMutex(); - } - #endregion NeedAckMutex } // Fire the registered packet events @@ -562,11 +499,8 @@ namespace libsecondlife return; } - #region InboxMutex - try + lock (PendingAcks) { - InboxMutex.WaitOne(); - if (PendingAcks.Count > 0) { int i = 0; @@ -586,15 +520,6 @@ namespace libsecondlife PendingAcks.Clear(); } } - catch (Exception e) - { - Client.Log(e.ToString(), Helpers.LogLevel.Error); - } - finally - { - InboxMutex.ReleaseMutex(); - } - #endregion InboxMutex } } @@ -625,7 +550,7 @@ namespace libsecondlife /// public Simulator CurrentSim; /// - /// The complete hashtable of all the login values returned by the + /// The complete dictionary of all the login values returned by the /// RPC login server, converted to native data types wherever possible /// public Dictionary LoginValues; @@ -650,7 +575,6 @@ namespace libsecondlife private Dictionary> Callbacks; private SecondLife Client; private List Simulators; - private Mutex SimulatorsMutex; private System.Timers.Timer DisconnectTimer; private bool connected; @@ -662,7 +586,6 @@ namespace libsecondlife { Client = client; Simulators = new List(); - SimulatorsMutex = new Mutex(false, "SimulatorsMutex"); Callbacks = new Dictionary>(); CurrentSim = null; LoginValues = null; @@ -975,7 +898,7 @@ namespace libsecondlife Client.Self.Position = posVector; Client.Self.LookAt = lookatVector; - // Create a hashtable to hold the home values + // Create a dictionary to hold the home values home = new Dictionary(); home["position"] = posVector; home["look_at"] = lookatVector; @@ -1040,21 +963,10 @@ namespace libsecondlife return null; } - #region SimulatorsMutex - try + lock (Simulators) { - SimulatorsMutex.WaitOne(); Simulators.Add(simulator); } - catch (Exception e) - { - Client.Log(e.ToString(), Helpers.LogLevel.Error); - } - finally - { - SimulatorsMutex.ReleaseMutex(); - } - #endregion SimulatorsMutex if (setDefault) { @@ -1112,21 +1024,10 @@ namespace libsecondlife OnSimDisconnected(sim, DisconnectType.NetworkTimeout); } - #region SimulatorsMutex - try + lock (Simulators) { - SimulatorsMutex.WaitOne(); Simulators.Remove(sim); } - catch (Exception e) - { - Client.Log(e.ToString(), Helpers.LogLevel.Error); - } - finally - { - SimulatorsMutex.ReleaseMutex(); - } - #endregion SimulatorsMutex } /// @@ -1135,11 +1036,8 @@ namespace libsecondlife /// private void Shutdown() { - #region SimulatorsMutex - try + lock (Simulators) { - SimulatorsMutex.WaitOne(); - // Disconnect all simulators except the current one foreach (Simulator simulator in Simulators) { @@ -1155,17 +1053,9 @@ namespace libsecondlife } } } - } - catch (Exception e) - { - Client.Log(e.ToString(), Helpers.LogLevel.Error); - } - finally - { + Simulators.Clear(); - SimulatorsMutex.ReleaseMutex(); } - #endregion SimulatorsMutex CurrentSim.Disconnect(); CurrentSim = null; @@ -1209,14 +1099,14 @@ namespace libsecondlife Client.Self.UpdateCamera(true); // TODO: What animation are we stopping here? - AgentAnimationPacket animation = new AgentAnimationPacket(); - animation.AgentData.AgentID = AgentID; - animation.AgentData.SessionID = SessionID; - animation.AnimationList = new AgentAnimationPacket.AnimationListBlock[1]; - animation.AnimationList[0] = new AgentAnimationPacket.AnimationListBlock(); - animation.AnimationList[0].AnimID = new LLUUID("efcf670c2d188128973a034ebc806b67"); - animation.AnimationList[0].StartAnim = false; - SendPacket(animation); + //AgentAnimationPacket animation = new AgentAnimationPacket(); + //animation.AgentData.AgentID = AgentID; + //animation.AgentData.SessionID = SessionID; + //animation.AnimationList = new AgentAnimationPacket.AnimationListBlock[1]; + //animation.AnimationList[0] = new AgentAnimationPacket.AnimationListBlock(); + //animation.AnimationList[0].AnimID = new LLUUID("efcf670c2d188128973a034ebc806b67"); + //animation.AnimationList[0].StartAnim = false; + //SendPacket(animation); // TODO: Do we ever want to set this to true? SetAlwaysRunPacket run = new SetAlwaysRunPacket(); @@ -1239,11 +1129,11 @@ namespace libsecondlife money.MoneyData.TransactionID = new LLUUID(); SendPacket(money); - // Request info about our avatar - AgentDataUpdateRequestPacket update = new AgentDataUpdateRequestPacket(); - update.AgentData.AgentID = AgentID; - update.AgentData.SessionID = SessionID; - SendPacket(update); + // FIXME: MainAvatar can request the info if it wants to use it + //AgentDataUpdateRequestPacket update = new AgentDataUpdateRequestPacket(); + //update.AgentData.AgentID = AgentID; + //update.AgentData.SessionID = SessionID; + //SendPacket(update); // TODO: What is the purpose of this? Information is currently unused RequestGrantedProxiesPacket proxies = new RequestGrantedProxiesPacket(); @@ -1254,50 +1144,42 @@ namespace libsecondlife private void DisconnectTimer_Elapsed(object sender, ElapsedEventArgs ev) { - #region SimulatorsMutex - try + // If the current simulator is disconnected, shutdown+callback+return + if (CurrentSim.DisconnectCandidate) { - SimulatorsMutex.WaitOne(); + Client.Log("Network timeout for the current simulator (" + + CurrentSim.Region.Name + "), logging out", Helpers.LogLevel.Warning); - Beginning: + DisconnectTimer.Stop(); + connected = false; + // Shutdown the network layer + Shutdown(); + + if (OnDisconnected != null) + { + OnDisconnected(DisconnectType.NetworkTimeout, ""); + } + + // We're completely logged out and shut down, leave this function + return; + } + + List disconnectedSims = null; + + // Check all of the connected sims for disconnects + lock (Simulators) + { foreach (Simulator sim in Simulators) { - if (sim.DisconnectCandidate == true) + if (sim.DisconnectCandidate) { - if (sim == CurrentSim) + if (disconnectedSims == null) { - Client.Log("Network timeout for the current simulator (" + - sim.Region.Name + "), logging out", Helpers.LogLevel.Warning); - - DisconnectTimer.Stop(); - connected = false; - - // Shutdown the network layer - Shutdown(); - - if (OnDisconnected != null) - { - OnDisconnected(DisconnectType.NetworkTimeout, ""); - } - - // We're completely logged out and shut down, leave this function - return; + disconnectedSims = new List(); } - else - { - // This sim hasn't received any network traffic since the - // timer last elapsed, consider it disconnected - Client.Log("Network timeout for simulator " + sim.Region.Name + - ", disconnecting", Helpers.LogLevel.Warning); - SimulatorsMutex.ReleaseMutex(); - DisconnectSim(sim); - SimulatorsMutex.WaitOne(); - - // Reset the iterator since we removed an element - goto Beginning; - } + disconnectedSims.Add(sim); } else { @@ -1305,15 +1187,20 @@ namespace libsecondlife } } } - catch (Exception e) + + // Actually disconnect each sim we detected as disconnected + if (disconnectedSims != null) { - Client.Log(e.ToString(), Helpers.LogLevel.Error); + foreach (Simulator sim in disconnectedSims) + { + // This sim hasn't received any network traffic since the + // timer last elapsed, consider it disconnected + Client.Log("Network timeout for simulator " + sim.Region.Name + + ", disconnecting", Helpers.LogLevel.Warning); + + DisconnectSim(sim); + } } - finally - { - SimulatorsMutex.ReleaseMutex(); - } - #endregion SimulatorsMutex } private void StartPingCheckHandler(Packet packet, Simulator simulator) diff --git a/libsecondlife-cs/Parcel.cs b/libsecondlife-cs/Parcel.cs index cf5803f4..28171384 100644 --- a/libsecondlife-cs/Parcel.cs +++ b/libsecondlife-cs/Parcel.cs @@ -346,9 +346,6 @@ namespace libsecondlife /// public class ParcelManager { - /// - public List ParcelsForSale; - private SecondLife Client; private bool ReservedNewbie; private bool ForSale; @@ -358,6 +355,7 @@ namespace libsecondlife private bool DirLandTimeout; private bool ParcelInfoTimeout; private DirectoryParcel ParcelInfoParcel; + private List ParcelsForSale; /// /// @@ -510,7 +508,7 @@ namespace libsecondlife // Fire off the next request, if we are downloading the whole sim bool hasTriggered = false; - if (simulator.Region.ParcelDownloading == true) + if (simulator.Region.ParcelDownloading) { for (x = 0; x < 64; x++) { @@ -540,69 +538,72 @@ namespace libsecondlife } // This map is complete, fire callback - if (hasTriggered == false) + if (!hasTriggered) { simulator.Region.FilledParcels(); } - // Save this parcels data - // TODO: Lots of values are not being stored, Parcel needs to be expanded to take all the data. - simulator.Region.ParcelsMutex.WaitOne(); + Parcel parcel = null; - if (!simulator.Region.Parcels.ContainsKey(LocalID)) + lock (simulator.Region.Parcels) { - simulator.Region.Parcels[LocalID] = new Parcel(simulator); + if (!simulator.Region.Parcels.ContainsKey(LocalID)) + { + simulator.Region.Parcels[LocalID] = new Parcel(simulator); + } + + parcel = simulator.Region.Parcels[LocalID]; } + // Save this parcels data + // TODO: Lots of values are not being stored, Parcel needs to be expanded to take all the data. // August2006: God help me should I have to type this out again... argh. // October2006: I really shouldnt have typed that. - ((Parcel)simulator.Region.Parcels[LocalID]).RequestResult = properties.ParcelData.RequestResult; - ((Parcel)simulator.Region.Parcels[LocalID]).SequenceID = properties.ParcelData.SequenceID; - ((Parcel)simulator.Region.Parcels[LocalID]).SnapSelection = properties.ParcelData.SnapSelection; - ((Parcel)simulator.Region.Parcels[LocalID]).SelfCount = properties.ParcelData.SelfCount; - ((Parcel)simulator.Region.Parcels[LocalID]).OtherCount = properties.ParcelData.OtherCount; - ((Parcel)simulator.Region.Parcels[LocalID]).PublicCount = properties.ParcelData.PublicCount; - ((Parcel)simulator.Region.Parcels[LocalID]).LocalID = LocalID; - ((Parcel)simulator.Region.Parcels[LocalID]).OwnerID = properties.ParcelData.OwnerID; - ((Parcel)simulator.Region.Parcels[LocalID]).IsGroupOwned = properties.ParcelData.IsGroupOwned; - ((Parcel)simulator.Region.Parcels[LocalID]).AuctionID = properties.ParcelData.AuctionID; - ((Parcel)simulator.Region.Parcels[LocalID]).ReservedNewbie = properties.ParcelData.ReservedNewbie; - ((Parcel)simulator.Region.Parcels[LocalID]).ClaimDate = properties.ParcelData.ClaimDate; - ((Parcel)simulator.Region.Parcels[LocalID]).ClaimPrice = properties.ParcelData.ClaimPrice; - ((Parcel)simulator.Region.Parcels[LocalID]).RentPrice = properties.ParcelData.RentPrice; - ((Parcel)simulator.Region.Parcels[LocalID]).AABBMin = properties.ParcelData.AABBMin; - ((Parcel)simulator.Region.Parcels[LocalID]).AABBMax = properties.ParcelData.AABBMax; - ((Parcel)simulator.Region.Parcels[LocalID]).Bitmap = properties.ParcelData.Bitmap; - ((Parcel)simulator.Region.Parcels[LocalID]).Area = properties.ParcelData.Area; - ((Parcel)simulator.Region.Parcels[LocalID]).Status = properties.ParcelData.Status; - ((Parcel)simulator.Region.Parcels[LocalID]).SimWideMaxObjects = properties.ParcelData.SimWideMaxPrims; - ((Parcel)simulator.Region.Parcels[LocalID]).SimWideTotalObjects = properties.ParcelData.SimWideTotalPrims; - ((Parcel)simulator.Region.Parcels[LocalID]).MaxObjects = properties.ParcelData.MaxPrims; - ((Parcel)simulator.Region.Parcels[LocalID]).TotalObjects = properties.ParcelData.TotalPrims; - ((Parcel)simulator.Region.Parcels[LocalID]).OwnerObjects = properties.ParcelData.OwnerPrims; - ((Parcel)simulator.Region.Parcels[LocalID]).GroupObjects = properties.ParcelData.GroupPrims; - ((Parcel)simulator.Region.Parcels[LocalID]).OtherObjects = properties.ParcelData.OtherPrims; - ((Parcel)simulator.Region.Parcels[LocalID]).ParcelObjectBonus = properties.ParcelData.ParcelPrimBonus; - ((Parcel)simulator.Region.Parcels[LocalID]).OtherCleanTime = properties.ParcelData.OtherCleanTime; - ((Parcel)simulator.Region.Parcels[LocalID]).ParcelFlags = properties.ParcelData.ParcelFlags; - ((Parcel)simulator.Region.Parcels[LocalID]).SalePrice = properties.ParcelData.SalePrice; - ((Parcel)simulator.Region.Parcels[LocalID]).Name = Helpers.FieldToString(properties.ParcelData.Name); - ((Parcel)simulator.Region.Parcels[LocalID]).Desc = Helpers.FieldToString(properties.ParcelData.Desc); - ((Parcel)simulator.Region.Parcels[LocalID]).MusicURL = Helpers.FieldToString(properties.ParcelData.MusicURL); - ((Parcel)simulator.Region.Parcels[LocalID]).MediaURL = Helpers.FieldToString(properties.ParcelData.MediaURL); - ((Parcel)simulator.Region.Parcels[LocalID]).MediaID = properties.ParcelData.MediaID; - ((Parcel)simulator.Region.Parcels[LocalID]).MediaAutoScale = properties.ParcelData.MediaAutoScale; - ((Parcel)simulator.Region.Parcels[LocalID]).GroupID = properties.ParcelData.GroupID; - ((Parcel)simulator.Region.Parcels[LocalID]).PassPrice = properties.ParcelData.PassPrice; - ((Parcel)simulator.Region.Parcels[LocalID]).PassHours = properties.ParcelData.PassHours; - ((Parcel)simulator.Region.Parcels[LocalID]).Category = properties.ParcelData.Category; - ((Parcel)simulator.Region.Parcels[LocalID]).AuthBuyerID = properties.ParcelData.AuthBuyerID; - ((Parcel)simulator.Region.Parcels[LocalID]).SnapshotID = properties.ParcelData.SnapshotID; - ((Parcel)simulator.Region.Parcels[LocalID]).UserLocation = properties.ParcelData.UserLocation; - ((Parcel)simulator.Region.Parcels[LocalID]).UserLookAt = properties.ParcelData.UserLookAt; - ((Parcel)simulator.Region.Parcels[LocalID]).LandingType = properties.ParcelData.LandingType; - - simulator.Region.ParcelsMutex.ReleaseMutex(); + parcel.RequestResult = properties.ParcelData.RequestResult; + parcel.SequenceID = properties.ParcelData.SequenceID; + parcel.SnapSelection = properties.ParcelData.SnapSelection; + parcel.SelfCount = properties.ParcelData.SelfCount; + parcel.OtherCount = properties.ParcelData.OtherCount; + parcel.PublicCount = properties.ParcelData.PublicCount; + parcel.LocalID = LocalID; + parcel.OwnerID = properties.ParcelData.OwnerID; + parcel.IsGroupOwned = properties.ParcelData.IsGroupOwned; + parcel.AuctionID = properties.ParcelData.AuctionID; + parcel.ReservedNewbie = properties.ParcelData.ReservedNewbie; + parcel.ClaimDate = properties.ParcelData.ClaimDate; + parcel.ClaimPrice = properties.ParcelData.ClaimPrice; + parcel.RentPrice = properties.ParcelData.RentPrice; + parcel.AABBMin = properties.ParcelData.AABBMin; + parcel.AABBMax = properties.ParcelData.AABBMax; + parcel.Bitmap = properties.ParcelData.Bitmap; + parcel.Area = properties.ParcelData.Area; + parcel.Status = properties.ParcelData.Status; + parcel.SimWideMaxObjects = properties.ParcelData.SimWideMaxPrims; + parcel.SimWideTotalObjects = properties.ParcelData.SimWideTotalPrims; + parcel.MaxObjects = properties.ParcelData.MaxPrims; + parcel.TotalObjects = properties.ParcelData.TotalPrims; + parcel.OwnerObjects = properties.ParcelData.OwnerPrims; + parcel.GroupObjects = properties.ParcelData.GroupPrims; + parcel.OtherObjects = properties.ParcelData.OtherPrims; + parcel.ParcelObjectBonus = properties.ParcelData.ParcelPrimBonus; + parcel.OtherCleanTime = properties.ParcelData.OtherCleanTime; + parcel.ParcelFlags = properties.ParcelData.ParcelFlags; + parcel.SalePrice = properties.ParcelData.SalePrice; + parcel.Name = Helpers.FieldToString(properties.ParcelData.Name); + parcel.Desc = Helpers.FieldToString(properties.ParcelData.Desc); + parcel.MusicURL = Helpers.FieldToString(properties.ParcelData.MusicURL); + parcel.MediaURL = Helpers.FieldToString(properties.ParcelData.MediaURL); + parcel.MediaID = properties.ParcelData.MediaID; + parcel.MediaAutoScale = properties.ParcelData.MediaAutoScale; + parcel.GroupID = properties.ParcelData.GroupID; + parcel.PassPrice = properties.ParcelData.PassPrice; + parcel.PassHours = properties.ParcelData.PassHours; + parcel.Category = properties.ParcelData.Category; + parcel.AuthBuyerID = properties.ParcelData.AuthBuyerID; + parcel.SnapshotID = properties.ParcelData.SnapshotID; + parcel.UserLocation = properties.ParcelData.UserLocation; + parcel.UserLookAt = properties.ParcelData.UserLookAt; + parcel.LandingType = properties.ParcelData.LandingType; } private void ParcelInfoReplyHandler(Packet packet, Simulator simulator) diff --git a/libsecondlife-cs/Region.cs b/libsecondlife-cs/Region.cs index 2314a50d..69a8ea47 100644 --- a/libsecondlife-cs/Region.cs +++ b/libsecondlife-cs/Region.cs @@ -25,6 +25,7 @@ */ using System; +using System.Collections.Generic; using libsecondlife.Packets; namespace libsecondlife @@ -40,6 +41,17 @@ namespace libsecondlife /// public class Region { + /// + public event ParcelCompleteCallback OnParcelCompletion; + + // FIXME: This whole setup is fscked in a really bad way. We can't be + // locking on a publically accessible container, and we shouldn't have + // publically accessible containers anyways because external programs + // might be iterating through them or modifying them when internally + // we are doing the opposite. The best way to fix this will be + // privatizing and adding helper functions to access the dictionary + public Dictionary Parcels; + /// public LLUUID ID; /// @@ -50,20 +62,13 @@ namespace libsecondlife public byte[] ParcelOverlay; /// public int ParcelOverlaysReceived; - - /// 64x64 Array of parcels which have been successfully downloaded. + /// 64x64 Array of parcels which have been successfully downloaded /// (and their LocalID's, 0 = Null) public int[,] ParcelMarked; - /// Flag to indicate whether we are downloading a sim's parcels. + /// Flag to indicate whether we are downloading a sim's parcels public bool ParcelDownloading; - /// Flag to indicate whether to get Dwell values automatically (NOT USED YET). Call Parcel.GetDwell() instead. + /// Flag to indicate whether to get Dwell values automatically (NOT USED YET). Call Parcel.GetDwell() instead public bool ParcelDwell; - - /// - public System.Collections.Hashtable Parcels; - /// - public System.Threading.Mutex ParcelsMutex; - /// public float TerrainHeightRange00; /// @@ -82,10 +87,8 @@ namespace libsecondlife public float TerrainStartHeight11; /// public float WaterHeight; - /// public LLUUID SimOwner; - /// public LLUUID TerrainBase0; /// @@ -102,7 +105,6 @@ namespace libsecondlife public LLUUID TerrainDetail2; /// public LLUUID TerrainDetail3; - /// public bool IsEstateManager; /// @@ -110,9 +112,6 @@ namespace libsecondlife private SecondLife Client; - /// - public event ParcelCompleteCallback OnParcelCompletion; - /// /// /// @@ -125,8 +124,7 @@ namespace libsecondlife ParcelOverlay = new byte[4096]; ParcelMarked = new int[64, 64]; - Parcels = new System.Collections.Hashtable(); - ParcelsMutex = new System.Threading.Mutex(false, "ParcelsMutex"); + Parcels = new Dictionary(); SimOwner = new LLUUID(); TerrainBase0 = new LLUUID(); @@ -273,7 +271,7 @@ namespace libsecondlife /// public void ResetParcelDownload() { - Parcels = new System.Collections.Hashtable(); + Parcels = new Dictionary(); ParcelMarked = new int[64, 64]; }