diff --git a/libsecondlife-cs/Avatar.cs b/libsecondlife-cs/Avatar.cs index 127531ff..2dfb3481 100644 --- a/libsecondlife-cs/Avatar.cs +++ b/libsecondlife-cs/Avatar.cs @@ -31,606 +31,603 @@ using System.Collections; namespace libsecondlife { - public delegate void ChatCallback(string message, byte audible, byte type, byte sourcetype, - string name, LLUUID id, byte command, LLUUID commandID); - - public delegate void InstantMessageCallback(LLUUID FromAgentID, LLUUID ToAgentID, - uint ParentEstateID, LLUUID RegionID, LLVector3 Position, byte Offline, byte Dialog, - LLUUID ID, uint Timestamp, string AgentName, string Message, string Bucket); - - public delegate void FriendNotificationCallback(LLUUID AgentID, bool Online); - - public delegate void TeleportCallback(string message); - - public class Avatar - { - public LLUUID ID; - public string Name; - public bool Online; - } - - public class MainAvatar - { - public LLUUID ID; - public string FirstName; - public string LastName; - public string TeleportMessage; - public LLVector3d Position; - public LLVector3d LookAt; - public LLVector3d HomePosition; - public LLVector3d HomeLookAt; - - private SecondLife Client; - private int TeleportStatus; - private Timer TeleportTimer; - private bool TeleportTimeout; - - public event ChatCallback OnChat; - public event InstantMessageCallback OnInstantMessage; - public event FriendNotificationCallback OnFriendNotification; - public event TeleportCallback OnTeleport; - - public MainAvatar(SecondLife client) - { - Client = client; - TeleportMessage = ""; - - // Create emtpy vectors for now - HomeLookAt = HomePosition = LookAt = Position = new LLVector3d(); - - // Location callback - PacketCallback callback = new PacketCallback(LocationHandler); - Client.Network.RegisterCallback("CoarseLocationUpdate", callback); - - // Teleport callbacks - callback = new PacketCallback(TeleportHandler); - Client.Network.RegisterCallback("TeleportStart", callback); - Client.Network.RegisterCallback("TeleportProgress", callback); - Client.Network.RegisterCallback("TeleportFailed", callback); - Client.Network.RegisterCallback("TeleportFinish", callback); - - // Instant Message callback - callback = new PacketCallback(InstantMessageHandler); - Client.Network.RegisterCallback("ImprovedInstantMessage", callback); - - // Chat callback - callback = new PacketCallback(ChatHandler); - Client.Network.RegisterCallback("ChatFromSimulator", callback); - - // Friend notification callback - callback = new PacketCallback(FriendNotificationHandler); - Client.Network.RegisterCallback("OnlineNotification", callback); - Client.Network.RegisterCallback("OfflineNotification", callback); - - TeleportTimer = new Timer(8000); - TeleportTimer.Elapsed += new ElapsedEventHandler(TeleportTimerEvent); - TeleportTimeout = false; - } - - private void FriendNotificationHandler(Packet packet, Simulator simulator) - { - // If the agent is online... - if (packet.Layout.Name == "OnlineNotification") - { - LLUUID AgentID = new LLUUID(); - - ArrayList blocks; - - blocks = packet.Blocks(); - - foreach (Block block in blocks) - { - foreach (Field field in block.Fields) - { - if(field.Layout.Name == "AgentID") - { - AgentID = (LLUUID)field.Data; - - Client.AddAvatar(AgentID); - Client.AvatarsMutex.WaitOne(); - ((Avatar)Client.Avatars[AgentID]).Online = true; - Client.AvatarsMutex.ReleaseMutex(); - - if (OnFriendNotification != null) - { - OnFriendNotification(AgentID, true); - } - } - } - } - - return; - } - - // If the agent is Offline... - if (packet.Layout.Name == "OfflineNotification") - { - LLUUID AgentID = new LLUUID(); - - ArrayList blocks; - - blocks = packet.Blocks(); - - foreach (Block block in blocks) - { - foreach (Field field in block.Fields) - { - if(field.Layout.Name == "AgentID") - { - AgentID = (LLUUID)field.Data; - - Client.AddAvatar(AgentID); - Client.AvatarsMutex.WaitOne(); - ((Avatar)Client.Avatars[AgentID]).Online = false; - Client.AvatarsMutex.ReleaseMutex(); - - if (OnFriendNotification != null) - { - OnFriendNotification(AgentID, false); - } - } - } - } - - return; - } - } - - private void LocationHandler(Packet packet, Simulator simulator) - { - foreach (Block block in packet.Blocks()) - { - foreach (Field field in block.Fields) - { - if (field.Layout.Name == "X") - { - Position.X = Convert.ToDouble((byte)field.Data); - } - else if (field.Layout.Name == "Y") - { - Position.Y = Convert.ToDouble((byte)field.Data); - } - else if (field.Layout.Name == "Z") - { - Position.Z = Convert.ToDouble((byte)field.Data); - } - } - } - - // Send an AgentUpdate packet with the new camera location - packet = Packets.Sim.AgentUpdate(Client.Protocol, Client.Network.AgentID, 56.0F, - new LLVector3((float)Position.X, (float)Position.Y, (float)Position.Z)); - Client.Network.SendPacket(packet); - } - - private void InstantMessageHandler(Packet packet, Simulator simulator) - { - if (packet.Layout.Name == "ImprovedInstantMessage") - { - LLUUID FromAgentID = new LLUUID(); - LLUUID ToAgentID = new LLUUID(); - uint ParentEstateID = 0; - LLUUID RegionID = new LLUUID(); - LLVector3 Position = new LLVector3(); - byte Offline = 0; - byte Dialog = 0; - LLUUID ID = new LLUUID(); - uint Timestamp = 0; - string AgentName = ""; - string Message = ""; - string Bucket = ""; - - ArrayList blocks; - - blocks = packet.Blocks(); - - foreach (Block block in blocks) - { - foreach (Field field in block.Fields) - { - if(field.Layout.Name == "FromAgentID") - { - FromAgentID = (LLUUID)field.Data; - } - else if(field.Layout.Name == "ToAgentID") - { - ToAgentID = (LLUUID)field.Data; - } - else if(field.Layout.Name == "ParentEstateID") - { - ParentEstateID = (uint)field.Data; - } - else if(field.Layout.Name == "RegionID") - { - RegionID = (LLUUID)field.Data; - } - else if(field.Layout.Name == "Position") - { - Position = (LLVector3)field.Data; - } - else if(field.Layout.Name == "Offline") - { - Offline = (byte)field.Data; - } - else if(field.Layout.Name == "Dialog") - { - Dialog = (byte)field.Data; - } - else if(field.Layout.Name == "ID") - { - ID = (LLUUID)field.Data; - } - else if(field.Layout.Name == "Timestamp") - { - Timestamp = (uint)field.Data; - } - else if(field.Layout.Name == "FromAgentName") - { - AgentName = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); - } - else if(field.Layout.Name == "Message") - { - Message = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); - } - else if(field.Layout.Name == "BinaryBucket") - { - Bucket = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); - } - } - } - - if (OnInstantMessage != null) - { - OnInstantMessage(FromAgentID,ToAgentID,ParentEstateID,RegionID,Position, - Offline,Dialog,ID,Timestamp,AgentName,Message,Bucket); - } - } - } - - private void ChatHandler(Packet packet, Simulator simulator) - { - if (packet.Layout.Name == "ChatFromSimulator") - { - string message = ""; - byte audible = 0; - byte type = 0; - byte sourcetype = 0; - string name = ""; - LLUUID id = new LLUUID(); - byte command = 0; - LLUUID commandID = new LLUUID(); - - ArrayList blocks; - - blocks = packet.Blocks(); - - foreach (Block block in blocks) - { - foreach (Field field in block.Fields) - { - if (field.Layout.Name == "ID") - { - id = (LLUUID)field.Data; - } - else if(field.Layout.Name == "FromName") - { - name = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); - } - else if(field.Layout.Name == "SourceType") - { - sourcetype = (byte)field.Data; - } - else if(field.Layout.Name == "Type") - { - type = (byte)field.Data; - } - else if(field.Layout.Name == "Audible") - { - audible = (byte)field.Data; - } - else if(field.Layout.Name == "Message") - { - message = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); - } - else if(field.Layout.Name == "Command") - { - command = (byte)field.Data; - } - else if(field.Layout.Name == "CommandID") - { - commandID = (LLUUID)field.Data; - } - - } - } - - //DEBUG - //Helpers.Log("Chat: Message=" + message + ", Type=" + type, Helpers.LogLevel.Info); - - if (OnChat != null) - { - OnChat(message, audible, type, sourcetype, name, id, command, commandID); - } - } - } - - public void InstantMessage(LLUUID target, string message) - { - TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1)); - uint now = (uint)(t.TotalSeconds); - string name = FirstName + " " + LastName; - - InstantMessage(name, LLUUID.GenerateUUID(), target, message, null); - } - - public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, LLUUID[] conferenceIDs) - { - TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1)); - uint now = (uint)(t.TotalSeconds); - - byte[] binaryBucket; - - if (conferenceIDs != null && conferenceIDs.Length > 0) - { - binaryBucket = new byte[16 * conferenceIDs.Length]; - - for (int i = 0; i < conferenceIDs.Length; ++i) - { - Array.Copy(conferenceIDs[i].Data, 0, binaryBucket, 16 * i, 16); - } - } - else - { - binaryBucket = new byte[0]; - } - - // Build the packet - Packet packet = Packets.Communication.ImprovedInstantMessage(Client.Protocol, target, Client.Network.AgentID, 0, - Client.CurrentRegion.ID, new LLVector3((float)Position.X, (float)Position.Y, (float)Position.Z), - 0, 0, sessionID, now, fromName, message, binaryBucket); - - // Send the message - Client.Network.SendPacket(packet); - } - - public void Say(string message, int channel) - { - LLUUID CommandID = new LLUUID(); - LLVector3 Position = new LLVector3(0.0F,0.0F,0.0F); - - Packet packet = Packets.Communication.ChatFromViewer(Client.Protocol, Client.Avatar.ID, Client.Network.SessionID, - message, (byte)1, channel, 0, CommandID, 20, Position); - - Client.Network.SendPacket(packet); - } - - public void Shout(string message, int channel) - { - LLUUID CommandID = new LLUUID(); - LLVector3 Position = new LLVector3(0.0F,0.0F,0.0F); - - Packet packet = Packets.Communication.ChatFromViewer(Client.Protocol,Client.Avatar.ID,Client.Network.SessionID, - message, (byte)2, channel, 0, CommandID, 100, Position); - - Client.Network.SendPacket(packet); - } - - public void GiveMoney(LLUUID target, int amount, string description) - { - // 5001 - transaction type for av to av money transfers - GiveMoney(target, amount, description, 5001); - } - - public void GiveMoney(LLUUID target, int amount, string description, int transactiontype) - { - Hashtable blocks = new Hashtable(); - Hashtable fields = new Hashtable(); - - fields["AggregatePermInventory"] = (byte)0; - fields["AggregatePermNextOwner"] = (byte)0; - fields["DestID"] = target; - fields["Amount"] = amount; - fields["Description"] = description; - fields["Flags"] = (byte)0; - fields["SourceID"] = Client.Network.AgentID; - fields["TransactionType"] = transactiontype; - blocks[fields] = "MoneyData"; - - fields = new Hashtable(); - fields["AgentID"] = Client.Network.AgentID; - fields["SessionID"] = Client.Network.SessionID; - blocks[fields] = "AgentData"; - - Packet packet = PacketBuilder.BuildPacket("MoneyTransferRequest", Client.Protocol, blocks, - Helpers.MSG_RELIABLE); - - Client.Network.SendPacket(packet); - } - - public bool Teleport(U64 regionHandle, LLVector3 position) - { - return Teleport(regionHandle,position,new LLVector3(position.X + 1.0F, position.Y, position.Z)); - } - - public bool Teleport(U64 regionHandle, LLVector3 position, LLVector3 lookAt) - { - TeleportStatus = 0; -// LLVector3 lookAt = new LLVector3(position.X + 1.0F, position.Y, position.Z); - - Hashtable blocks = new Hashtable(); - Hashtable fields = new Hashtable(); - fields["RegionHandle"] = regionHandle; - fields["LookAt"] = lookAt; - fields["Position"] = position; - blocks[fields] = "Info"; - fields = new Hashtable(); - fields["AgentID"] = Client.Network.AgentID; - fields["SessionID"] = Client.Network.SessionID; - blocks[fields] = "AgentData"; - Packet packet = PacketBuilder.BuildPacket("TeleportLocationRequest", Client.Protocol, blocks, - Helpers.MSG_RELIABLE + Helpers.MSG_ZEROCODED); - - Helpers.Log("Teleporting to region " + regionHandle.ToString(), Helpers.LogLevel.Info); - - // Start the timeout check - TeleportTimeout = false; - TeleportTimer.Start(); - - Client.Network.SendPacket(packet); - - while (TeleportStatus == 0 && !TeleportTimeout) - { - Client.Tick(); - } - - TeleportTimer.Stop(); - - if (TeleportTimeout) - { - if (OnTeleport != null) - { - OnTeleport("Teleport timed out."); - } - } - else - { - if (OnTeleport != null) - { - OnTeleport(TeleportMessage); - } - } - - return (TeleportStatus == 1); - } - - public bool Teleport(string simName, LLVector3 position) - { - return Teleport(simName,position,new LLVector3(position.X + 1.0F, position.Y, position.Z)); - } - - public bool Teleport(string simName, LLVector3 position, LLVector3 lookAt) - { - Client.Grid.AddSim(simName); - int attempts = 0; - - while(attempts++ < 5) - { - if(Client.Grid.Regions.ContainsKey(simName)) - { - return Teleport(((GridRegion)Client.Grid.Regions[simName]).RegionHandle,position,lookAt); - } - else - { - System.Threading.Thread.Sleep(1000); - Client.Grid.AddSim(simName); - Client.Tick(); - } - } - if (OnTeleport != null) - { - OnTeleport("Unable to resolve name: " + simName); - } - return false; - } - - private void TeleportHandler(Packet packet, Simulator simulator) - { - ArrayList blocks; - - if (packet.Layout.Name == "TeleportStart") - { - TeleportMessage = "Teleport started"; - } - else if (packet.Layout.Name == "TeleportProgress") - { - blocks = packet.Blocks(); - - foreach (Block block in blocks) - { - foreach (Field field in block.Fields) - { - if (field.Layout.Name == "Message") - { - TeleportMessage = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); - return; - } - } - } - } - else if (packet.Layout.Name == "TeleportFailed") - { - blocks = packet.Blocks(); - - foreach (Block block in blocks) - { - foreach (Field field in block.Fields) - { - if (field.Layout.Name == "Reason") - { - TeleportMessage = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); - TeleportStatus = -1; - return; - } - } - } - } - else if (packet.Layout.Name == "TeleportFinish") - { - TeleportMessage = "Teleport finished"; - - ushort port = 0; - IPAddress ip = null; - U64 regionHandle; - - blocks = packet.Blocks(); - - foreach (Block block in blocks) - { - foreach (Field field in block.Fields) - { - if (field.Layout.Name == "SimPort") - { - port = (ushort)field.Data; - } - else if (field.Layout.Name == "SimIP") - { - ip = (IPAddress)field.Data; - } - else if (field.Layout.Name == "RegionHandle") - { - regionHandle = (U64)field.Data; - } - } - } - - if (Client.Network.Connect(ip, port, Client.Network.CurrentSim.CircuitCode, true)) - { - // Move the avatar in to this sim - Packet movePacket = Packets.Sim.CompleteAgentMovement(Client.Protocol, Client.Network.AgentID, - Client.Network.SessionID, Client.Network.CurrentSim.CircuitCode); - Client.Network.SendPacket(movePacket); - - Helpers.Log("Connected to new sim " + Client.Network.CurrentSim.IPEndPoint.ToString(), - Helpers.LogLevel.Info); - - // Sleep a little while so we can collect parcel information - System.Threading.Thread.Sleep(1000); - - Client.CurrentRegion = Client.Network.CurrentSim.Region; - TeleportStatus = 1; - return; - } - else - { - TeleportStatus = -1; - return; - } - } - } - - private void TeleportTimerEvent(object source, System.Timers.ElapsedEventArgs ea) - { - TeleportTimeout = true; - } - } + public delegate void ChatCallback(string message, byte audible, byte type, byte sourcetype, + string name, LLUUID id, byte command, LLUUID commandID); + + public delegate void InstantMessageCallback(LLUUID FromAgentID, LLUUID ToAgentID, + uint ParentEstateID, LLUUID RegionID, LLVector3 Position, byte Offline, byte Dialog, + LLUUID ID, uint Timestamp, string AgentName, string Message, string Bucket); + + public delegate void FriendNotificationCallback(LLUUID AgentID, bool Online); + + public delegate void TeleportCallback(string message); + + public class Avatar + { + public LLUUID ID; + public string Name; + public bool Online; + } + + public class MainAvatar + { + public LLUUID ID; + public string FirstName; + public string LastName; + public string TeleportMessage; + public LLVector3d Position; + public LLVector3d LookAt; + public LLVector3d HomePosition; + public LLVector3d HomeLookAt; + + private SecondLife Client; + private int TeleportStatus; + private Timer TeleportTimer; + private bool TeleportTimeout; + + public event ChatCallback OnChat; + public event InstantMessageCallback OnInstantMessage; + public event FriendNotificationCallback OnFriendNotification; + public event TeleportCallback OnTeleport; + + public MainAvatar(SecondLife client) + { + Client = client; + TeleportMessage = ""; + + // Create emtpy vectors for now + HomeLookAt = HomePosition = LookAt = Position = new LLVector3d(); + + // Location callback + PacketCallback callback = new PacketCallback(LocationHandler); + Client.Network.RegisterCallback("CoarseLocationUpdate", callback); + + // Teleport callbacks + callback = new PacketCallback(TeleportHandler); + Client.Network.RegisterCallback("TeleportStart", callback); + Client.Network.RegisterCallback("TeleportProgress", callback); + Client.Network.RegisterCallback("TeleportFailed", callback); + Client.Network.RegisterCallback("TeleportFinish", callback); + + // Instant Message callback + callback = new PacketCallback(InstantMessageHandler); + Client.Network.RegisterCallback("ImprovedInstantMessage", callback); + + // Chat callback + callback = new PacketCallback(ChatHandler); + Client.Network.RegisterCallback("ChatFromSimulator", callback); + + // Friend notification callback + callback = new PacketCallback(FriendNotificationHandler); + Client.Network.RegisterCallback("OnlineNotification", callback); + Client.Network.RegisterCallback("OfflineNotification", callback); + + TeleportTimer = new Timer(8000); + TeleportTimer.Elapsed += new ElapsedEventHandler(TeleportTimerEvent); + TeleportTimeout = false; + } + + private void FriendNotificationHandler(Packet packet, Simulator simulator) + { + // If the agent is online... + if (packet.Layout.Name == "OnlineNotification") + { + LLUUID AgentID = new LLUUID(); + + ArrayList blocks; + + blocks = packet.Blocks(); + + foreach (Block block in blocks) + { + foreach (Field field in block.Fields) + { + if (field.Layout.Name == "AgentID") + { + AgentID = (LLUUID)field.Data; + + Client.AddAvatar(AgentID); + Client.AvatarsMutex.WaitOne(); + ((Avatar)Client.Avatars[AgentID]).Online = true; + Client.AvatarsMutex.ReleaseMutex(); + + if (OnFriendNotification != null) + { + OnFriendNotification(AgentID, true); + } + } + } + } + + return; + } + + // If the agent is Offline... + if (packet.Layout.Name == "OfflineNotification") + { + LLUUID AgentID = new LLUUID(); + + ArrayList blocks; + + blocks = packet.Blocks(); + + foreach (Block block in blocks) + { + foreach (Field field in block.Fields) + { + if (field.Layout.Name == "AgentID") + { + AgentID = (LLUUID)field.Data; + + Client.AddAvatar(AgentID); + Client.AvatarsMutex.WaitOne(); + ((Avatar)Client.Avatars[AgentID]).Online = false; + Client.AvatarsMutex.ReleaseMutex(); + + if (OnFriendNotification != null) + { + OnFriendNotification(AgentID, false); + } + } + } + } + + return; + } + } + + private void LocationHandler(Packet packet, Simulator simulator) + { + foreach (Block block in packet.Blocks()) + { + foreach (Field field in block.Fields) + { + if (field.Layout.Name == "X") + { + Position.X = Convert.ToDouble((byte)field.Data); + } + else if (field.Layout.Name == "Y") + { + Position.Y = Convert.ToDouble((byte)field.Data); + } + else if (field.Layout.Name == "Z") + { + Position.Z = Convert.ToDouble((byte)field.Data); + } + } + } + + // Send an AgentUpdate packet with the new camera location + packet = Packets.Sim.AgentUpdate(Client.Protocol, Client.Network.AgentID, 56.0F, + new LLVector3((float)Position.X, (float)Position.Y, (float)Position.Z)); + Client.Network.SendPacket(packet); + } + + private void InstantMessageHandler(Packet packet, Simulator simulator) + { + if (packet.Layout.Name == "ImprovedInstantMessage") + { + LLUUID FromAgentID = new LLUUID(); + LLUUID ToAgentID = new LLUUID(); + uint ParentEstateID = 0; + LLUUID RegionID = new LLUUID(); + LLVector3 Position = new LLVector3(); + byte Offline = 0; + byte Dialog = 0; + LLUUID ID = new LLUUID(); + uint Timestamp = 0; + string AgentName = ""; + string Message = ""; + string Bucket = ""; + + ArrayList blocks; + + blocks = packet.Blocks(); + + foreach (Block block in blocks) + { + foreach (Field field in block.Fields) + { + if (field.Layout.Name == "FromAgentID") + { + FromAgentID = (LLUUID)field.Data; + } + else if (field.Layout.Name == "ToAgentID") + { + ToAgentID = (LLUUID)field.Data; + } + else if (field.Layout.Name == "ParentEstateID") + { + ParentEstateID = (uint)field.Data; + } + else if (field.Layout.Name == "RegionID") + { + RegionID = (LLUUID)field.Data; + } + else if (field.Layout.Name == "Position") + { + Position = (LLVector3)field.Data; + } + else if (field.Layout.Name == "Offline") + { + Offline = (byte)field.Data; + } + else if (field.Layout.Name == "Dialog") + { + Dialog = (byte)field.Data; + } + else if (field.Layout.Name == "ID") + { + ID = (LLUUID)field.Data; + } + else if (field.Layout.Name == "Timestamp") + { + Timestamp = (uint)field.Data; + } + else if (field.Layout.Name == "FromAgentName") + { + AgentName = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); + } + else if (field.Layout.Name == "Message") + { + Message = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); + } + else if (field.Layout.Name == "BinaryBucket") + { + Bucket = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); + } + } + } + + if (OnInstantMessage != null) + { + OnInstantMessage(FromAgentID, ToAgentID, ParentEstateID, RegionID, Position, + Offline, Dialog, ID, Timestamp, AgentName, Message, Bucket); + } + } + } + + private void ChatHandler(Packet packet, Simulator simulator) + { + if (packet.Layout.Name == "ChatFromSimulator") + { + string message = ""; + byte audible = 0; + byte type = 0; + byte sourcetype = 0; + string name = ""; + LLUUID id = new LLUUID(); + byte command = 0; + LLUUID commandID = new LLUUID(); + + ArrayList blocks; + + blocks = packet.Blocks(); + + foreach (Block block in blocks) + { + foreach (Field field in block.Fields) + { + if (field.Layout.Name == "ID") + { + id = (LLUUID)field.Data; + } + else if (field.Layout.Name == "FromName") + { + name = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); + } + else if (field.Layout.Name == "SourceType") + { + sourcetype = (byte)field.Data; + } + else if (field.Layout.Name == "Type") + { + type = (byte)field.Data; + } + else if (field.Layout.Name == "Audible") + { + audible = (byte)field.Data; + } + else if (field.Layout.Name == "Message") + { + message = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); + } + else if (field.Layout.Name == "Command") + { + command = (byte)field.Data; + } + else if (field.Layout.Name == "CommandID") + { + commandID = (LLUUID)field.Data; + } + + } + } + + if (OnChat != null) + { + OnChat(message, audible, type, sourcetype, name, id, command, commandID); + } + } + } + + public void InstantMessage(LLUUID target, string message) + { + TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1)); + uint now = (uint)(t.TotalSeconds); + string name = FirstName + " " + LastName; + + InstantMessage(name, LLUUID.GenerateUUID(), target, message, null); + } + + public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, LLUUID[] conferenceIDs) + { + TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1)); + uint now = (uint)(t.TotalSeconds); + + byte[] binaryBucket; + + if (conferenceIDs != null && conferenceIDs.Length > 0) + { + binaryBucket = new byte[16 * conferenceIDs.Length]; + + for (int i = 0; i < conferenceIDs.Length; ++i) + { + Array.Copy(conferenceIDs[i].Data, 0, binaryBucket, 16 * i, 16); + } + } + else + { + binaryBucket = new byte[0]; + } + + // Build the packet + Packet packet = Packets.Communication.ImprovedInstantMessage(Client.Protocol, target, Client.Network.AgentID, 0, + Client.CurrentRegion.ID, new LLVector3((float)Position.X, (float)Position.Y, (float)Position.Z), + 0, 0, sessionID, now, fromName, message, binaryBucket); + + // Send the message + Client.Network.SendPacket(packet); + } + + public void Say(string message, int channel) + { + LLUUID CommandID = new LLUUID(); + LLVector3 Position = new LLVector3(0.0F, 0.0F, 0.0F); + + Packet packet = Packets.Communication.ChatFromViewer(Client.Protocol, Client.Avatar.ID, Client.Network.SessionID, + message, (byte)1, channel, 0, CommandID, 20, Position); + + Client.Network.SendPacket(packet); + } + + public void Shout(string message, int channel) + { + LLUUID CommandID = new LLUUID(); + LLVector3 Position = new LLVector3(0.0F, 0.0F, 0.0F); + + Packet packet = Packets.Communication.ChatFromViewer(Client.Protocol, Client.Avatar.ID, Client.Network.SessionID, + message, (byte)2, channel, 0, CommandID, 100, Position); + + Client.Network.SendPacket(packet); + } + + public void GiveMoney(LLUUID target, int amount, string description) + { + // 5001 - transaction type for av to av money transfers + GiveMoney(target, amount, description, 5001); + } + + public void GiveMoney(LLUUID target, int amount, string description, int transactiontype) + { + Hashtable blocks = new Hashtable(); + Hashtable fields = new Hashtable(); + + fields["AggregatePermInventory"] = (byte)0; + fields["AggregatePermNextOwner"] = (byte)0; + fields["DestID"] = target; + fields["Amount"] = amount; + fields["Description"] = description; + fields["Flags"] = (byte)0; + fields["SourceID"] = Client.Network.AgentID; + fields["TransactionType"] = transactiontype; + blocks[fields] = "MoneyData"; + + fields = new Hashtable(); + fields["AgentID"] = Client.Network.AgentID; + fields["SessionID"] = Client.Network.SessionID; + blocks[fields] = "AgentData"; + + Packet packet = PacketBuilder.BuildPacket("MoneyTransferRequest", Client.Protocol, blocks, + Helpers.MSG_RELIABLE); + + Client.Network.SendPacket(packet); + } + + public bool Teleport(U64 regionHandle, LLVector3 position) + { + return Teleport(regionHandle, position, new LLVector3(position.X + 1.0F, position.Y, position.Z)); + } + + public bool Teleport(U64 regionHandle, LLVector3 position, LLVector3 lookAt) + { + TeleportStatus = 0; + // LLVector3 lookAt = new LLVector3(position.X + 1.0F, position.Y, position.Z); + + Hashtable blocks = new Hashtable(); + Hashtable fields = new Hashtable(); + fields["RegionHandle"] = regionHandle; + fields["LookAt"] = lookAt; + fields["Position"] = position; + blocks[fields] = "Info"; + fields = new Hashtable(); + fields["AgentID"] = Client.Network.AgentID; + fields["SessionID"] = Client.Network.SessionID; + blocks[fields] = "AgentData"; + Packet packet = PacketBuilder.BuildPacket("TeleportLocationRequest", Client.Protocol, blocks, + Helpers.MSG_RELIABLE + Helpers.MSG_ZEROCODED); + + Client.Log("Teleporting to region " + regionHandle.ToString(), Helpers.LogLevel.Info); + + // Start the timeout check + TeleportTimeout = false; + TeleportTimer.Start(); + + Client.Network.SendPacket(packet); + + while (TeleportStatus == 0 && !TeleportTimeout) + { + Client.Tick(); + } + + TeleportTimer.Stop(); + + if (TeleportTimeout) + { + if (OnTeleport != null) + { + OnTeleport("Teleport timed out."); + } + } + else + { + if (OnTeleport != null) + { + OnTeleport(TeleportMessage); + } + } + + return (TeleportStatus == 1); + } + + public bool Teleport(string simName, LLVector3 position) + { + return Teleport(simName, position, new LLVector3(position.X + 1.0F, position.Y, position.Z)); + } + + public bool Teleport(string simName, LLVector3 position, LLVector3 lookAt) + { + Client.Grid.AddSim(simName); + int attempts = 0; + + while (attempts++ < 5) + { + if (Client.Grid.Regions.ContainsKey(simName)) + { + return Teleport(((GridRegion)Client.Grid.Regions[simName]).RegionHandle, position, lookAt); + } + else + { + System.Threading.Thread.Sleep(1000); + Client.Grid.AddSim(simName); + Client.Tick(); + } + } + if (OnTeleport != null) + { + OnTeleport("Unable to resolve name: " + simName); + } + return false; + } + + private void TeleportHandler(Packet packet, Simulator simulator) + { + ArrayList blocks; + + if (packet.Layout.Name == "TeleportStart") + { + TeleportMessage = "Teleport started"; + } + else if (packet.Layout.Name == "TeleportProgress") + { + blocks = packet.Blocks(); + + foreach (Block block in blocks) + { + foreach (Field field in block.Fields) + { + if (field.Layout.Name == "Message") + { + TeleportMessage = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); + return; + } + } + } + } + else if (packet.Layout.Name == "TeleportFailed") + { + blocks = packet.Blocks(); + + foreach (Block block in blocks) + { + foreach (Field field in block.Fields) + { + if (field.Layout.Name == "Reason") + { + TeleportMessage = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); + TeleportStatus = -1; + return; + } + } + } + } + else if (packet.Layout.Name == "TeleportFinish") + { + TeleportMessage = "Teleport finished"; + + ushort port = 0; + IPAddress ip = null; + U64 regionHandle; + + blocks = packet.Blocks(); + + foreach (Block block in blocks) + { + foreach (Field field in block.Fields) + { + if (field.Layout.Name == "SimPort") + { + port = (ushort)field.Data; + } + else if (field.Layout.Name == "SimIP") + { + ip = (IPAddress)field.Data; + } + else if (field.Layout.Name == "RegionHandle") + { + regionHandle = (U64)field.Data; + } + } + } + + if (Client.Network.Connect(ip, port, Client.Network.CurrentSim.CircuitCode, true)) + { + // Move the avatar in to this sim + Packet movePacket = Packets.Sim.CompleteAgentMovement(Client.Protocol, Client.Network.AgentID, + Client.Network.SessionID, Client.Network.CurrentSim.CircuitCode); + Client.Network.SendPacket(movePacket); + + Client.Log("Connected to new sim " + Client.Network.CurrentSim.IPEndPoint.ToString(), + Helpers.LogLevel.Info); + + // Sleep a little while so we can collect parcel information + System.Threading.Thread.Sleep(1000); + + Client.CurrentRegion = Client.Network.CurrentSim.Region; + TeleportStatus = 1; + return; + } + else + { + TeleportStatus = -1; + return; + } + } + } + + private void TeleportTimerEvent(object source, System.Timers.ElapsedEventArgs ea) + { + TeleportTimeout = true; + } + } } diff --git a/libsecondlife-cs/GridManager.cs b/libsecondlife-cs/GridManager.cs index cbec7d43..861f2472 100644 --- a/libsecondlife-cs/GridManager.cs +++ b/libsecondlife-cs/GridManager.cs @@ -97,7 +97,7 @@ namespace libsecondlife else { /* TODO: Put some better handling inplace here with some retry code */ - Helpers.Log("Error returned sim that didnt exist",Helpers.LogLevel.Warning); + Client.Log("Error returned sim that didnt exist",Helpers.LogLevel.Warning); return new GridRegion(); } } diff --git a/libsecondlife-cs/NetworkManager.cs b/libsecondlife-cs/NetworkManager.cs index 370841e6..a570bd19 100644 --- a/libsecondlife-cs/NetworkManager.cs +++ b/libsecondlife-cs/NetworkManager.cs @@ -58,6 +58,7 @@ namespace libsecondlife public class Simulator { + private SecondLife Client; private ProtocolManager Protocol; private NetworkManager Network; private Hashtable Callbacks; @@ -99,6 +100,7 @@ namespace libsecondlife private void Initialize(SecondLife client, Hashtable callbacks, uint circuit, IPAddress ip, int port) { + Client = client; Protocol = client.Protocol; Network = client.Network; Callbacks = callbacks; @@ -118,7 +120,7 @@ namespace libsecondlife NeedAckMutex = new Mutex(false, "NeedAckMutex"); InboxMutex = new Mutex(false, "InboxMutex"); - Helpers.Log("Connecting to " + ip.ToString() + ":" + port, Helpers.LogLevel.Info); + Client.Log("Connecting to " + ip.ToString() + ":" + port, Helpers.LogLevel.Info); try { @@ -155,7 +157,7 @@ namespace libsecondlife } catch (Exception e) { - Helpers.Log(e.ToString(), Helpers.LogLevel.Error); + Client.Log(e.ToString(), Helpers.LogLevel.Error); } } @@ -174,7 +176,7 @@ namespace libsecondlife } catch (Exception e) { - Helpers.Log(e.ToString(), Helpers.LogLevel.Error); + Client.Log(e.ToString(), Helpers.LogLevel.Error); } } @@ -185,7 +187,7 @@ namespace libsecondlife if (!connected && packet.Layout.Name != "UseCircuitCode") { - Helpers.Log("Trying to send a " + packet.Layout.Name + " packet when the socket is closed", + Client.Log("Trying to send a " + packet.Layout.Name + " packet when the socket is closed", Helpers.LogLevel.Warning); throw new NotConnectedException(); @@ -256,7 +258,7 @@ namespace libsecondlife } catch (Exception e) { - Helpers.Log(e.ToString(), Helpers.LogLevel.Error); + Client.Log(e.ToString(), Helpers.LogLevel.Error); } } @@ -276,7 +278,7 @@ namespace libsecondlife } catch (Exception e) { - Helpers.Log(e.ToString(), Helpers.LogLevel.Error); + Client.Log(e.ToString(), Helpers.LogLevel.Error); } } @@ -299,7 +301,7 @@ namespace libsecondlife // Grab the ACKs that are appended to this packet byte numAcks = RecvBuffer[numBytes - 1]; - Helpers.Log("Found " + numAcks + " appended acks", Helpers.LogLevel.Info); + Client.Log("Found " + numAcks + " appended acks", Helpers.LogLevel.Info); NeedAckMutex.WaitOne(); for (int i = 1; i <= numAcks; ++i) @@ -341,9 +343,15 @@ namespace libsecondlife // Start listening again since we're done with RecvBuffer Connection.BeginReceiveFrom(RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref endPoint, ReceivedData, null); - RecvBufferMutex.ReleaseMutex(); + if (packet.Layout.Name == "") + { + // TODO: Add a packet dump function to Packet and dump the raw data here + Client.Log("Received an unrecognized packet", Helpers.LogLevel.Warning); + return; + } + // Track the sequence number for this packet if it's marked as reliable if ((packet.Data[0] & Helpers.MSG_RELIABLE) != 0) { @@ -355,7 +363,7 @@ namespace libsecondlife InboxMutex.WaitOne(); if (Inbox.Contains(packet.Sequence)) { - Helpers.Log("Received a duplicate " + packet.Layout.Name + ", sequence=" + + Client.Log("Received a duplicate " + packet.Layout.Name + ", sequence=" + packet.Sequence + ", resent=" + (((packet.Data[0] & Helpers.MSG_RESENT) != 0) ? "Yes" : "No"), Helpers.LogLevel.Info); @@ -373,7 +381,7 @@ namespace libsecondlife if (packet.Layout.Name == null) { - Helpers.Log("Received an unrecognized packet", Helpers.LogLevel.Warning); + Client.Log("Received an unrecognized packet", Helpers.LogLevel.Warning); return; } else if (packet.Layout.Name == "PacketAck") @@ -432,7 +440,7 @@ namespace libsecondlife } catch (Exception e) { - Helpers.Log(e.ToString(), Helpers.LogLevel.Error); + Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } @@ -483,7 +491,7 @@ namespace libsecondlife { if (!Callbacks.ContainsKey(packet)) { - Helpers.Log("Trying to unregister a callback for packet " + packet + + Client.Log("Trying to unregister a callback for packet " + packet + " when no callbacks are setup for that packet", Helpers.LogLevel.Info); return; } @@ -496,7 +504,7 @@ namespace libsecondlife } else { - Helpers.Log("Trying to unregister a non-existant callback for packet " + packet, + Client.Log("Trying to unregister a non-existant callback for packet " + packet, Helpers.LogLevel.Info); } } @@ -509,7 +517,7 @@ namespace libsecondlife } else { - Helpers.Log("Trying to send a packet when there is no current simulator", Helpers.LogLevel.Error); + Client.Log("Trying to send a packet when there is no current simulator", Helpers.LogLevel.Error); } } @@ -578,7 +586,7 @@ namespace libsecondlife } catch (Exception e) { - Helpers.Log(e.ToString(), Helpers.LogLevel.Error); + Client.Log(e.ToString(), Helpers.LogLevel.Error); LoginError = e.Message; LoginValues = null; return false; @@ -586,7 +594,7 @@ namespace libsecondlife if (result.IsFault) { - Helpers.Log("Fault " + result.FaultCode + ": " + result.FaultString, Helpers.LogLevel.Error); + Client.Log("Fault " + result.FaultCode + ": " + result.FaultString, Helpers.LogLevel.Error); LoginError = "Fault " + result.FaultCode + ": " + result.FaultString; LoginValues = null; return false; @@ -722,7 +730,7 @@ namespace libsecondlife /* if (circuit == CurrentCircuit) { - Helpers.Log("Disconnecting simulator " + simulator.IPEndPoint.ToString(), Helpers.LogLevel.Info); + Client.Log("Disconnecting simulator " + simulator.IPEndPoint.ToString(), Helpers.LogLevel.Info); circuit.CloseCircuit(); @@ -731,12 +739,12 @@ namespace libsecondlife if (Circuits.Count > 0) { CurrentCircuit = (Circuit)Circuits[0]; - Helpers.Log("Switched current simulator to " + CurrentCircuit.ipEndPoint.ToString(), + Client.Log("Switched current simulator to " + CurrentCircuit.ipEndPoint.ToString(), Helpers.LogLevel.Info); } else { - Helpers.Log("Last circuit disconnected, no open connections left", Helpers.LogLevel.Info); + Client.Log("Last circuit disconnected, no open connections left", Helpers.LogLevel.Info); CurrentCircuit = null; } CircuitsMutex.ReleaseMutex(); @@ -745,7 +753,7 @@ namespace libsecondlife } else { - Helpers.Log("Disconnecting circuit " + circuit.ipEndPoint.ToString(), Helpers.LogLevel.Info); + Client.Log("Disconnecting circuit " + circuit.ipEndPoint.ToString(), Helpers.LogLevel.Info); circuit.CloseCircuit(); CircuitsMutex.WaitOne(); @@ -754,7 +762,7 @@ namespace libsecondlife return; } - //Helpers.Log("Disconnect called with invalid circuit code " + circuitCode, Helpers.LogLevel.Warning); + //Client.Log("Disconnect called with invalid circuit code " + circuitCode, Helpers.LogLevel.Warning); */ } @@ -790,7 +798,7 @@ namespace libsecondlife } catch (Exception e) { - Helpers.Log("Logout error: " + e.ToString(), Helpers.LogLevel.Error); + Client.Log("Logout error: " + e.ToString(), Helpers.LogLevel.Error); } } @@ -897,7 +905,7 @@ namespace libsecondlife } } - Helpers.Log("Received a region handshake for " + simulator.Region.Name, Helpers.LogLevel.Info); + Client.Log("Received a region handshake for " + simulator.Region.Name, Helpers.LogLevel.Info); // Send a RegionHandshakeReply Packet replyPacket = new Packet("RegionHandshakeReply", Protocol, 12); @@ -905,7 +913,7 @@ namespace libsecondlife } catch (Exception e) { - Helpers.Log(e.ToString(), Helpers.LogLevel.Warning); + Client.Log(e.ToString(), Helpers.LogLevel.Warning); } } @@ -927,7 +935,7 @@ namespace libsecondlife byteArray = (byte[])field.Data; if (byteArray.Length != 1024) { - Helpers.Log("Received a parcel overlay packet with " + byteArray.Length + " bytes", + Client.Log("Received a parcel overlay packet with " + byteArray.Length + " bytes", Helpers.LogLevel.Error); } } @@ -942,7 +950,7 @@ namespace libsecondlife if (simulator.Region.ParcelOverlaysReceived > 3) { simulator.Region.ParcelOverlaysReceived = 0; - Helpers.Log("Finished building the " + simulator.Region.Name + " parcel overlay", + Client.Log("Finished building the " + simulator.Region.Name + " parcel overlay", Helpers.LogLevel.Info); // The int i = 0; is just there so I could break on it and check the @@ -953,7 +961,7 @@ namespace libsecondlife } else { - Helpers.Log("Parcel overlay with sequence ID of " + sequenceID + " received from " + + Client.Log("Parcel overlay with sequence ID of " + sequenceID + " received from " + simulator.Region.Name, Helpers.LogLevel.Error); } } diff --git a/libsecondlife-cs/Packet.cs b/libsecondlife-cs/Packet.cs index 361164c6..02242857 100644 --- a/libsecondlife-cs/Packet.cs +++ b/libsecondlife-cs/Packet.cs @@ -138,8 +138,13 @@ namespace libsecondlife if (Layout == null) { - Helpers.Log("Attempting to build a packet with invalid command \"" + command + "\"", - Helpers.LogLevel.Error); + //Client.Log("Attempting to build a packet with invalid command \"" + command + "\"", + // Helpers.LogLevel.Error); + + // Create an empty Layout + Layout = new MapPacket(); + Layout.Blocks = new ArrayList(); + return; } switch (Layout.Frequency) @@ -181,8 +186,6 @@ namespace libsecondlife if (Layout == null) { - Helpers.Log("Received a packet with an unknown command ID:\n" + libsecondlife.Utils.ByteArrayStuff.ByteArrayToString((byte[])data), Helpers.LogLevel.Warning); - // Create an empty MapPacket Layout = new MapPacket(); Layout.Blocks = new ArrayList(); @@ -229,9 +232,10 @@ namespace libsecondlife pos++; } else - { - Helpers.Log("Blocks(): end of packet in 1-byte variable block count for " + Layout.Name + "." + blockMap.Name + " (" + pos + "/" + length + ")", Helpers.LogLevel.Warning); + // FIXME: Figure out a sane way to log from this function + Console.WriteLine("WARNING: Blocks(): end of packet in 1-byte variable block count for " + + Layout.Name + "." + blockMap.Name + " (" + pos + "/" + length + ")"); goto Done; } } @@ -261,7 +265,8 @@ namespace libsecondlife } else { - Helpers.Log("Blocks(): end of packet in 1-byte variable field count for " + Layout.Name + "." + blockMap.Name + "." + fieldMap.Name + " (" + pos + "/" + length + ")", Helpers.LogLevel.Warning); + Console.WriteLine("WARNING: Blocks(): end of packet in 1-byte variable field count for " + + Layout.Name + "." + blockMap.Name + "." + fieldMap.Name + " (" + pos + "/" + length + ")"); goto BlockDone; } } @@ -275,7 +280,8 @@ namespace libsecondlife } else { - Helpers.Log("Blocks(): end of packet in 2-byte variable field count for " + Layout.Name + "." + blockMap.Name + "." + fieldMap.Name + " (" + pos + "/" + length + ")", Helpers.LogLevel.Warning); + Console.WriteLine("WARNING: Blocks(): end of packet in 2-byte variable field count for " + + Layout.Name + "." + blockMap.Name + "." + fieldMap.Name + " (" + pos + "/" + length + ")"); goto BlockDone; } } @@ -295,7 +301,9 @@ namespace libsecondlife } else { - Helpers.Log("Blocks(): end of packet in " + fieldSize + "-byte variable field " + Layout.Name + "." + blockMap.Name + "." + fieldMap.Name + " (" + pos + "/" + length + ")", Helpers.LogLevel.Warning); + Console.WriteLine("WARNING: Blocks(): end of packet in " + fieldSize + + "-byte variable field " + Layout.Name + "." + blockMap.Name + "." + fieldMap.Name + + " (" + pos + "/" + length + ")"); goto BlockDone; } } @@ -317,7 +325,8 @@ namespace libsecondlife } else { - Helpers.Log("Blocks(): end of packet in " + fieldSize + "-byte fixed field " + Layout.Name + "." + blockMap.Name + "." + fieldMap.Name + " (" + pos + "/" + length + ")", Helpers.LogLevel.Warning); + Console.WriteLine("WARNING: Blocks(): end of packet in " + fieldSize + "-byte fixed field " + + Layout.Name + "." + blockMap.Name + "." + fieldMap.Name + " (" + pos + "/" + length + ")"); goto BlockDone; } } @@ -340,7 +349,9 @@ namespace libsecondlife } else { - Helpers.Log("Blocks(): end of packet in " + fieldSize + "-byte " + fieldMap.Type + " field " + Layout.Name + "." + blockMap.Name + "." + fieldMap.Name + " (" + pos + "/" + length + ")", Helpers.LogLevel.Warning); + Console.WriteLine("WARNING: Blocks(): end of packet in " + fieldSize + "-byte " + + fieldMap.Type + " field " + Layout.Name + "." + blockMap.Name + "." + fieldMap.Name + + " (" + pos + "/" + length + ")"); goto BlockDone; } } @@ -523,8 +534,7 @@ namespace libsecondlife if (blockCount > 255) { - Helpers.Log("Trying to put more than 255 blocks in a variable block position, " + - "this will not end well", Helpers.LogLevel.Error); + throw new Exception("Trying to put more than 255 blocks in a variable block"); } // Prepend the blocks with a count @@ -545,7 +555,7 @@ namespace libsecondlife if ((string)blocksEnum.Value == blockMap.Name) { // Found a match of this block - if (blockMap.Count == -1 || blockCount < blockMap.Count) + if ((blockMap.Count == -1 && blockCount < 255) || blockCount < blockMap.Count) { blockCount++; @@ -554,11 +564,9 @@ namespace libsecondlife { fields = (Hashtable)blocksEnum.Key; } - catch (Exception) + catch (Exception e) { - Helpers.Log("Something other than a field Hashtable was passed to BuildPacket " + - "inside of the block Hashtable", Helpers.LogLevel.Warning); - continue; + throw new Exception("A block Hashtable did not contain a fields Hashtable", e); } #endregion TryBlockTypecast @@ -569,187 +577,152 @@ namespace libsecondlife object field = fields[fieldMap.Name]; #region AddField - try + switch (fieldMap.Type) { - switch (fieldMap.Type) - { - case FieldType.U8: - byteArray[length++] = (byte)field; - break; - case FieldType.U16: - ushort fieldUShort = (ushort)field; - byteArray[length++] = (byte)(fieldUShort % 256); - fieldUShort >>= 8; - byteArray[length++] = (byte)(fieldUShort % 256); - break; - case FieldType.U32: - uint fieldUInt = (uint)field; - byteArray[length++] = (byte)(fieldUInt % 256); - fieldUInt >>= 8; - byteArray[length++] = (byte)(fieldUInt % 256); - fieldUInt >>= 8; - byteArray[length++] = (byte)(fieldUInt % 256); - fieldUInt >>= 8; - byteArray[length++] = (byte)(fieldUInt % 256); - break; - case FieldType.U64: - // FIXME: Apply endianness patch - Array.Copy(((U64)field).GetBytes(), 0, byteArray, length, 8); - length += 8; - break; - case FieldType.S8: - byteArray[length++] = (byte)((sbyte)field); - break; - case FieldType.S16: - // FIXME: Apply endianness patch - Array.Copy(BitConverter.GetBytes((short)field), 0, byteArray, length, 2); - length += 2; - break; - case FieldType.S32: - // FIXME: Apply endianness patch - Array.Copy(BitConverter.GetBytes((int)field), 0, byteArray, length, 4); - length += 4; - break; - case FieldType.S64: - // FIXME: Apply endianness patch - Array.Copy(BitConverter.GetBytes((long)field), 0, byteArray, length, 8); - length += 8; - break; - case FieldType.F32: - Array.Copy(BitConverter.GetBytes((float)field), 0, byteArray, length, 4); - length += 4; - break; - case FieldType.F64: - Array.Copy(BitConverter.GetBytes((double)field), 0, byteArray, length, 8); - length += 8; - break; - case FieldType.LLUUID: - Array.Copy(((LLUUID)field).Data, 0, byteArray, length, 16); - length += 16; - break; - case FieldType.BOOL: - byteArray[length] = (byte)((bool)field == true ? 1 : 0); + case FieldType.U8: + byteArray[length++] = (byte)field; + break; + case FieldType.U16: + ushort fieldUShort = (ushort)field; + byteArray[length++] = (byte)(fieldUShort % 256); + fieldUShort >>= 8; + byteArray[length++] = (byte)(fieldUShort % 256); + break; + case FieldType.U32: + uint fieldUInt = (uint)field; + byteArray[length++] = (byte)(fieldUInt % 256); + fieldUInt >>= 8; + byteArray[length++] = (byte)(fieldUInt % 256); + fieldUInt >>= 8; + byteArray[length++] = (byte)(fieldUInt % 256); + fieldUInt >>= 8; + byteArray[length++] = (byte)(fieldUInt % 256); + break; + case FieldType.U64: + // FIXME: Apply endianness patch + Array.Copy(((U64)field).GetBytes(), 0, byteArray, length, 8); + length += 8; + break; + case FieldType.S8: + byteArray[length++] = (byte)((sbyte)field); + break; + case FieldType.S16: + // FIXME: Apply endianness patch + Array.Copy(BitConverter.GetBytes((short)field), 0, byteArray, length, 2); + length += 2; + break; + case FieldType.S32: + // FIXME: Apply endianness patch + Array.Copy(BitConverter.GetBytes((int)field), 0, byteArray, length, 4); + length += 4; + break; + case FieldType.S64: + // FIXME: Apply endianness patch + Array.Copy(BitConverter.GetBytes((long)field), 0, byteArray, length, 8); + length += 8; + break; + case FieldType.F32: + Array.Copy(BitConverter.GetBytes((float)field), 0, byteArray, length, 4); + length += 4; + break; + case FieldType.F64: + Array.Copy(BitConverter.GetBytes((double)field), 0, byteArray, length, 8); + length += 8; + break; + case FieldType.LLUUID: + Array.Copy(((LLUUID)field).Data, 0, byteArray, length, 16); + length += 16; + break; + case FieldType.BOOL: + byteArray[length] = (byte)((bool)field == true ? 1 : 0); + length++; + break; + case FieldType.LLVector3: + Array.Copy(((LLVector3)field).GetBytes(), 0, byteArray, length, 12); + length += 12; + break; + case FieldType.LLVector3d: + Array.Copy(((LLVector3d)field).GetBytes(), 0, byteArray, length, 24); + length += 24; + break; + case FieldType.LLVector4: + Array.Copy(((LLVector4)field).GetBytes(), 0, byteArray, length, 16); + length += 16; + break; + case FieldType.LLQuaternion: + Array.Copy(((LLQuaternion)field).GetBytes(), 0, byteArray, length, 16); + length += 16; + break; + case FieldType.IPADDR: + Array.Copy(((IPAddress)field).GetAddressBytes(), 0, byteArray, length, 4); + length += 4; + break; + case FieldType.IPPORT: + ushort fieldIPPort = (ushort)field; + byteArray[length + 1] = (byte)(fieldIPPort % 256); + fieldIPPort >>= 8; + byteArray[length] = (byte)(fieldIPPort % 256); + length += 2; + break; + case FieldType.Variable: + if (field.GetType().IsArray) + { + // Assume this is a byte array + fieldLength = ((byte[])field).Length; + } + else + { + // Assume this is a string, add 1 for the null terminator + fieldLength = ((string)field).Length + 1; + } + + if (fieldMap.Count == 1) + { + if (fieldLength > 255) + { + throw new Exception("Variable byte field longer than 255 characters"); + } + + byteArray[length] = (byte)(fieldLength); length++; - break; - case FieldType.LLVector3: - Array.Copy(((LLVector3)field).GetBytes(), 0, byteArray, length, 12); - length += 12; - break; - case FieldType.LLVector3d: - Array.Copy(((LLVector3d)field).GetBytes(), 0, byteArray, length, 24); - length += 24; - break; - case FieldType.LLVector4: - Array.Copy(((LLVector4)field).GetBytes(), 0, byteArray, length, 16); - length += 16; - break; - case FieldType.LLQuaternion: - Array.Copy(((LLQuaternion)field).GetBytes(), 0, byteArray, length, 16); - length += 16; - break; - case FieldType.IPADDR: - Array.Copy(((IPAddress)field).GetAddressBytes(), 0, byteArray, length, 4); - length += 4; - break; - case FieldType.IPPORT: - ushort fieldIPPort = (ushort)field; - byteArray[length + 1] = (byte)(fieldIPPort % 256); - fieldIPPort >>= 8; - byteArray[length] = (byte)(fieldIPPort % 256); - length += 2; - break; - case FieldType.Variable: - if (fieldMap.Count == 1) + } + else if (fieldMap.Count == 2) + { + if (fieldLength > 1024) { - if (fieldLength > 255) - { - Helpers.Log("Truncating variable (byte) field to 255 " + - "characters. fieldLength=" + fieldLength + - ", fieldMap.Name=" + fieldMap.Name + ", packetMap.Name=" + packetMap.Name, - Helpers.LogLevel.Warning); - - fieldLength = 255; - } - } - else if (fieldMap.Count == 2) - { - if (fieldLength > 1024) - { - Helpers.Log("Truncating variable (byte) field to 1024 " + - "characters", Helpers.LogLevel.Warning); - - fieldLength = 1024; - } - } - else - { - Helpers.Log("Variable field with an unknown count", Helpers.LogLevel.Warning); + throw new Exception("Variable byte field longer than 1024 characters"); } - if (field.GetType().IsArray) - { - // Assume this is a byte array - fieldLength = ((byte[])field).Length; - } - else - { - // Assume this is a string, add 1 for the null terminator - fieldLength = ((string)field).Length + 1; - } + byteArray[length++] = (byte)(fieldLength % 256); + byteArray[length++] = (byte)(fieldLength / 256); + } + else + { + throw new Exception("Variable field with an unknown count, protocol map error"); + } + + if (field.GetType().IsArray) + { + // Assume this is a byte array + Array.Copy((byte[])field, 0, byteArray, length, fieldLength); + } + else + { + // Assume this is a string, add 1 for the null terminator + byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes((string)field); + Array.Copy(stringBytes, 0, byteArray, length, stringBytes.Length); + fieldLength = stringBytes.Length + 1; + } - if (fieldMap.Count == 2) - { - byteArray[length++] = (byte)(fieldLength % 256); - byteArray[length++] = (byte)(fieldLength / 256); - } - else - { - if (fieldMap.Count != 1) - { - Helpers.Log("Variable field " + fieldMap.Name + " has a count of " + - + fieldMap.Count + ", ignoring and assuming 1", - Helpers.LogLevel.Warning); - } + length += fieldLength; - byteArray[length] = (byte)(fieldLength); - length++; - } - - if (field.GetType().IsArray) - { - // Assume this is a byte array - Array.Copy((byte[])field, 0, byteArray, length, fieldLength); - } - else - { - // Assume this is a string, add 1 for the null terminator - byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes((string)field); - Array.Copy(stringBytes, 0, byteArray, length, stringBytes.Length); - fieldLength = stringBytes.Length + 1; - } - - length += fieldLength; - - break; - case FieldType.Fixed: - Array.Copy((byte[])field, 0, byteArray, length, fieldMap.Count); - length += fieldMap.Count; - break; - default: - Helpers.Log("Unhandled field type " + fieldMap.Type + " during " + - "packet construction", Helpers.LogLevel.Error); - break; - } - } - catch (Exception err) - { - Helpers.Log("PacketBuilder.BuildPacket(): " + err.ToString(), Helpers.LogLevel.Error); - //Data type " + field.GetType().ToString() + " for field " + - // fieldMap.Name + " doesn't match expected type " + fieldMap.Type.ToString(), - // ); - // This will fail for fixed or variable type packets, but it's a - // last ditch effort - length += (int)protocol.TypeSizes[fieldMap.Type]; + break; + case FieldType.Fixed: + Array.Copy((byte[])field, 0, byteArray, length, fieldMap.Count); + length += fieldMap.Count; + break; + default: + throw new Exception("Unhandled FieldType"); } #endregion AddField } @@ -768,9 +741,7 @@ namespace libsecondlife { if (fieldMap.Count != 1) { - Helpers.Log("Variable field " + fieldMap.Name + " has a count of " + - + fieldMap.Count + ", ignoring and assuming 1", - Helpers.LogLevel.Warning); + throw new Exception("Variable length field has an invalid Count"); } length++; @@ -790,8 +761,7 @@ namespace libsecondlife } else { - Helpers.Log("Trying to build a " + packetMap.Name + " packet with too many " + - blockMap.Name + " blocks", Helpers.LogLevel.Warning); + throw new Exception("Too many blocks"); } } } diff --git a/libsecondlife-cs/Parcel.cs b/libsecondlife-cs/Parcel.cs index 8bc130c4..0f5971b2 100644 --- a/libsecondlife-cs/Parcel.cs +++ b/libsecondlife-cs/Parcel.cs @@ -456,7 +456,7 @@ namespace libsecondlife else if(field.Layout.Name == "LandingType") LandingType = (byte)field.Data; // else - // Helpers.Log("Unknown field type '" + field.Layout.Name + "' in ParcelProperties",Helpers.LogLevel.Warning); + // Client.Log("Unknown field type '" + field.Layout.Name + "' in ParcelProperties",Helpers.LogLevel.Warning); } } @@ -597,7 +597,7 @@ namespace libsecondlife if (!parcelID.Equals(ParcelInfoParcel.ID)) { - Helpers.Log("Received a ParcelInfoReply for " + parcelID.ToString() + + Client.Log("Received a ParcelInfoReply for " + parcelID.ToString() + ", looking for " + ParcelInfoParcel.ID.ToString(), Helpers.LogLevel.Warning); // Build and resend the ParcelInfoRequest packet @@ -682,7 +682,7 @@ namespace libsecondlife } catch (Exception e) { - Helpers.Log(e.ToString(), Helpers.LogLevel.Error); + Client.Log(e.ToString(), Helpers.LogLevel.Error); } Finished = true; @@ -786,7 +786,7 @@ namespace libsecondlife } else { - Helpers.Log("Parcel with no ID found in DirLandReply, skipping", Helpers.LogLevel.Warning); + Client.Log("Parcel with no ID found in DirLandReply, skipping", Helpers.LogLevel.Warning); } } diff --git a/libsecondlife-cs/ProtocolManager.cs b/libsecondlife-cs/ProtocolManager.cs index 43dd25ec..a9de3f0f 100644 --- a/libsecondlife-cs/ProtocolManager.cs +++ b/libsecondlife-cs/ProtocolManager.cs @@ -39,9 +39,9 @@ namespace libsecondlife public class MapField : IComparable { - public int KeywordPosition; - public string Name; - public FieldType Type; + public int KeywordPosition; + public string Name; + public FieldType Type; public int Count; public int CompareTo(object obj) @@ -68,9 +68,9 @@ namespace libsecondlife public class MapBlock : IComparable { - public int KeywordPosition; - public string Name; - public int Count; + public int KeywordPosition; + public string Name; + public int Count; public ArrayList Fields; public int CompareTo(object obj) @@ -112,10 +112,14 @@ namespace libsecondlife public MapPacket[] LowMaps; public MapPacket[] MediumMaps; public MapPacket[] HighMaps; - int i = 0; - public ProtocolManager(string keywordFile, string mapFile) + private SecondLife Client; + private int i = 0; + + public ProtocolManager(string keywordFile, string mapFile, SecondLife client) { + Client = client; + // Initialize the map arrays LowMaps = new MapPacket[65536]; MediumMaps = new MapPacket[256]; @@ -194,13 +198,13 @@ namespace libsecondlife throw new Exception("Cannot find map for command \"" + command + "\""); } - public MapPacket Command(byte[] data) - { + public MapPacket Command(byte[] data) + { ushort command; if (data.Length < 5) { - return null; + return null; } if (data[4] == 0xFF) @@ -209,22 +213,22 @@ namespace libsecondlife { // Low frequency command = (ushort)(data[6] * 256 + data[7]); - return Command(command, PacketFrequency.Low); + return Command(command, PacketFrequency.Low); } else { // Medium frequency command = (ushort)data[5]; - return Command(command, PacketFrequency.Medium); + return Command(command, PacketFrequency.Medium); } } else { // High frequency command = (ushort)data[4]; - return Command(command, PacketFrequency.High); - } - } + return Command(command, PacketFrequency.High); + } + } public MapPacket Command(ushort command, PacketFrequency frequency) { @@ -294,7 +298,7 @@ namespace libsecondlife } catch(Exception e) { - Helpers.Log("Error opening \"" + keywordFile + "\": " + e.Message, Helpers.LogLevel.Error); + Client.Log("Error opening \"" + keywordFile + "\": " + e.Message, Helpers.LogLevel.Error); throw new Exception("Keyword file error", e); } @@ -320,7 +324,6 @@ namespace libsecondlife } catch(Exception e) { - Helpers.Log("Error opening \"" + mapFile + "\": " + e.Message, Helpers.LogLevel.Error); throw new Exception("Map file error", e); } @@ -330,7 +333,6 @@ namespace libsecondlife } catch(Exception e) { - Helpers.Log("Error opening \"" + outputFile + "\": " + e.Message, Helpers.LogLevel.Error); throw new Exception("Map file error", e); } @@ -363,7 +365,6 @@ namespace libsecondlife } catch(Exception e) { - Helpers.Log("Error opening \"" + mapFile + "\": " + e.Message, Helpers.LogLevel.Error); throw new Exception("Map file error", e); } @@ -490,7 +491,7 @@ namespace libsecondlife } else { - Helpers.Log("!!!", Helpers.LogLevel.Error); + Client.Log("Unknown packet frequency", Helpers.LogLevel.Error); } } @@ -561,7 +562,7 @@ namespace libsecondlife } else { - Helpers.Log("Unknown block frequency!", Helpers.LogLevel.Error); + Client.Log("Unknown block frequency", Helpers.LogLevel.Error); } #endregion @@ -577,7 +578,7 @@ namespace libsecondlife } catch (Exception e) { - Helpers.Log(e.ToString(), Helpers.LogLevel.Error); + throw e; } } @@ -589,9 +590,9 @@ namespace libsecondlife } else { - Helpers.Log("Couldn't find keyword: " + keyword, Helpers.LogLevel.Warning); + Client.Log("Couldn't find keyword: " + keyword, Helpers.LogLevel.Warning); return -1; } } } -} +} diff --git a/libsecondlife-cs/Region.cs b/libsecondlife-cs/Region.cs index e6d97144..31d76b30 100644 --- a/libsecondlife-cs/Region.cs +++ b/libsecondlife-cs/Region.cs @@ -28,227 +28,227 @@ using System; namespace libsecondlife { - public delegate void ParcelCompleteCallback(Region region); + public delegate void ParcelCompleteCallback(Region region); - /// - /// Represents a region (also known as a sim) in Second Life. - /// - public class Region - { - public LLUUID ID; - public U64 Handle; - public string Name; - public byte[] ParcelOverlay; - public int ParcelOverlaysReceived; + /// + /// Represents a region (also known as a sim) in Second Life. + /// + public class Region + { + public LLUUID ID; + public U64 Handle; + public string Name; + public byte[] ParcelOverlay; + public int ParcelOverlaysReceived; - public int[,] ParcelMarked; // 64x64 Array of parcels which have been successfully downloaded. (and their LocalID's, 0 = Null) - public bool ParcelDownloading; // Flag to indicate whether we are downloading a sim's parcels. - public bool ParcelDwell; // Flag to indicate whether to get Dwell values automatically (NOT USED YET). - // Call .GetDwell() instead. + public int[,] ParcelMarked; // 64x64 Array of parcels which have been successfully downloaded. (and their LocalID's, 0 = Null) + public bool ParcelDownloading; // Flag to indicate whether we are downloading a sim's parcels. + public bool ParcelDwell; // Flag to indicate whether to get Dwell values automatically (NOT USED YET). + // Call .GetDwell() instead. - public System.Collections.Hashtable Parcels; - public System.Threading.Mutex ParcelsMutex; + public System.Collections.Hashtable Parcels; + public System.Threading.Mutex ParcelsMutex; - public float TerrainHeightRange00; - public float TerrainHeightRange01; - public float TerrainHeightRange10; - public float TerrainHeightRange11; - public float TerrainStartHeight00; - public float TerrainStartHeight01; - public float TerrainStartHeight10; - public float TerrainStartHeight11; - public float WaterHeight; + public float TerrainHeightRange00; + public float TerrainHeightRange01; + public float TerrainHeightRange10; + public float TerrainHeightRange11; + public float TerrainStartHeight00; + public float TerrainStartHeight01; + public float TerrainStartHeight10; + public float TerrainStartHeight11; + public float WaterHeight; - public LLUUID SimOwner; + public LLUUID SimOwner; - public LLUUID TerrainBase0; - public LLUUID TerrainBase1; - public LLUUID TerrainBase2; - public LLUUID TerrainBase3; - public LLUUID TerrainDetail0; - public LLUUID TerrainDetail1; - public LLUUID TerrainDetail2; - public LLUUID TerrainDetail3; + public LLUUID TerrainBase0; + public LLUUID TerrainBase1; + public LLUUID TerrainBase2; + public LLUUID TerrainBase3; + public LLUUID TerrainDetail0; + public LLUUID TerrainDetail1; + public LLUUID TerrainDetail2; + public LLUUID TerrainDetail3; - public bool IsEstateManager; - public EstateTools Estate; + public bool IsEstateManager; + public EstateTools Estate; - private SecondLife Client; + private SecondLife Client; - public event ParcelCompleteCallback OnParcelCompletion; + public event ParcelCompleteCallback OnParcelCompletion; - public Region(SecondLife client) - { - Estate = new EstateTools(client); - Client = client; - ID = new LLUUID(); - Handle = new U64(); - Name = ""; - ParcelOverlay = new byte[4096]; - ParcelOverlaysReceived = 0; + public Region(SecondLife client) + { + Estate = new EstateTools(client); + Client = client; + ID = new LLUUID(); + Handle = new U64(); + Name = ""; + ParcelOverlay = new byte[4096]; + ParcelOverlaysReceived = 0; - ParcelMarked = new int[64,64]; - ParcelDownloading = false; - ParcelDwell = false; + ParcelMarked = new int[64, 64]; + ParcelDownloading = false; + ParcelDwell = false; - Parcels = new System.Collections.Hashtable(); - ParcelsMutex = new System.Threading.Mutex(false,"ParcelsMutex"); + Parcels = new System.Collections.Hashtable(); + ParcelsMutex = new System.Threading.Mutex(false, "ParcelsMutex"); - SimOwner = new LLUUID(); + SimOwner = new LLUUID(); - TerrainBase0 = new LLUUID(); - TerrainBase1 = new LLUUID(); - TerrainBase2 = new LLUUID(); - TerrainBase3 = new LLUUID(); - TerrainDetail0 = new LLUUID(); - TerrainDetail1 = new LLUUID(); - TerrainDetail2 = new LLUUID(); - TerrainDetail3 = new LLUUID(); + TerrainBase0 = new LLUUID(); + TerrainBase1 = new LLUUID(); + TerrainBase2 = new LLUUID(); + TerrainBase3 = new LLUUID(); + TerrainDetail0 = new LLUUID(); + TerrainDetail1 = new LLUUID(); + TerrainDetail2 = new LLUUID(); + TerrainDetail3 = new LLUUID(); - IsEstateManager = false; - } + IsEstateManager = false; + } - public Region(SecondLife client, LLUUID id, U64 handle, string name, float[] heightList, - LLUUID simOwner, LLUUID[] terrainImages, bool isEstateManager) - { - Client = client; - Estate = new EstateTools(client); - ID = id; - Handle = handle; - Name = name; - ParcelOverlay = new byte[4096]; - ParcelMarked = new int[64,64]; - ParcelDownloading = false; - ParcelDwell = false; + public Region(SecondLife client, LLUUID id, U64 handle, string name, float[] heightList, + LLUUID simOwner, LLUUID[] terrainImages, bool isEstateManager) + { + Client = client; + Estate = new EstateTools(client); + ID = id; + Handle = handle; + Name = name; + ParcelOverlay = new byte[4096]; + ParcelMarked = new int[64, 64]; + ParcelDownloading = false; + ParcelDwell = false; - TerrainHeightRange00 = heightList[0]; - TerrainHeightRange01 = heightList[1]; - TerrainHeightRange10 = heightList[2]; - TerrainHeightRange11 = heightList[3]; - TerrainStartHeight00 = heightList[4]; - TerrainStartHeight01 = heightList[5]; - TerrainStartHeight10 = heightList[6]; - TerrainStartHeight11 = heightList[7]; - WaterHeight = heightList[8]; + TerrainHeightRange00 = heightList[0]; + TerrainHeightRange01 = heightList[1]; + TerrainHeightRange10 = heightList[2]; + TerrainHeightRange11 = heightList[3]; + TerrainStartHeight00 = heightList[4]; + TerrainStartHeight01 = heightList[5]; + TerrainStartHeight10 = heightList[6]; + TerrainStartHeight11 = heightList[7]; + WaterHeight = heightList[8]; - SimOwner = simOwner; + SimOwner = simOwner; - TerrainBase0 = terrainImages[0]; - TerrainBase1 = terrainImages[1]; - TerrainBase2 = terrainImages[2]; - TerrainBase3 = terrainImages[3]; - TerrainDetail0 = terrainImages[4]; - TerrainDetail1 = terrainImages[5]; - TerrainDetail2 = terrainImages[6]; - TerrainDetail3 = terrainImages[7]; + TerrainBase0 = terrainImages[0]; + TerrainBase1 = terrainImages[1]; + TerrainBase2 = terrainImages[2]; + TerrainBase3 = terrainImages[3]; + TerrainDetail0 = terrainImages[4]; + TerrainDetail1 = terrainImages[5]; + TerrainDetail2 = terrainImages[6]; + TerrainDetail3 = terrainImages[7]; - IsEstateManager = isEstateManager; - } + IsEstateManager = isEstateManager; + } - public void ParcelSubdivide(float west, float south, float east, float north) - { - Client.Network.SendPacket( - Packets.Parcel.ParcelDivide(Client.Protocol,Client.Avatar.ID, - west,south,east,north)); - } + public void ParcelSubdivide(float west, float south, float east, float north) + { + Client.Network.SendPacket( + Packets.Parcel.ParcelDivide(Client.Protocol, Client.Avatar.ID, + west, south, east, north)); + } - public void ParcelJoin(float west, float south, float east, float north) - { - Client.Network.SendPacket( - Packets.Parcel.ParcelJoin(Client.Protocol,Client.Avatar.ID, - west,south,east,north)); - } + public void ParcelJoin(float west, float south, float east, float north) + { + Client.Network.SendPacket( + Packets.Parcel.ParcelJoin(Client.Protocol, Client.Avatar.ID, + west, south, east, north)); + } - public void RezObject(PrimObject prim, LLVector3 position, LLVector3 avatarPosition) - { - byte[] textureEntry = new byte[40]; - Array.Copy(prim.Texture.Data, textureEntry, 16); - textureEntry[35] = 0xe0; // No clue + public void RezObject(PrimObject prim, LLVector3 position, LLVector3 avatarPosition) + { + byte[] textureEntry = new byte[40]; + Array.Copy(prim.Texture.Data, textureEntry, 16); + textureEntry[35] = 0xe0; // No clue - Packet objectAdd = libsecondlife.Packets.Object.ObjectAdd(Client.Protocol, Client.Network.AgentID, - LLUUID.GenerateUUID(), avatarPosition, - position, prim, textureEntry); - Client.Network.SendPacket(objectAdd); - } + Packet objectAdd = libsecondlife.Packets.Object.ObjectAdd(Client.Protocol, Client.Network.AgentID, + LLUUID.GenerateUUID(), avatarPosition, + position, prim, textureEntry); + Client.Network.SendPacket(objectAdd); + } - public void FillParcels() - { - // Begins filling parcels - ParcelDownloading = true; + public void FillParcels() + { + // Begins filling parcels + ParcelDownloading = true; - // TODO: Replace Client.Network with Region.Simulator, or similar? - Client.Network.SendPacket(libsecondlife.Packets.Parcel.ParcelPropertiesRequest(Client.Protocol,Client.Avatar.ID, -10000, - 0.0f,0.0f,4.0f,4.0f, false)); - } + // TODO: Replace Client.Network with Region.Simulator, or similar? + Client.Network.SendPacket(libsecondlife.Packets.Parcel.ParcelPropertiesRequest(Client.Protocol, Client.Avatar.ID, -10000, + 0.0f, 0.0f, 4.0f, 4.0f, false)); + } - public void ResetParcelDownload() - { - Parcels = new System.Collections.Hashtable(); - ParcelMarked = new int[64,64]; - } + public void ResetParcelDownload() + { + Parcels = new System.Collections.Hashtable(); + ParcelMarked = new int[64, 64]; + } - public void FilledParcels() - { - if(OnParcelCompletion != null) - { - OnParcelCompletion(this); - } - } + public void FilledParcels() + { + if (OnParcelCompletion != null) + { + OnParcelCompletion(this); + } + } - public override int GetHashCode() - { - return ID.GetHashCode(); - } + public override int GetHashCode() + { + return ID.GetHashCode(); + } - public override bool Equals(object o) - { - if (!(o is Region)) - { - return false; - } + public override bool Equals(object o) + { + if (!(o is Region)) + { + return false; + } - Region region = (Region)o; + Region region = (Region)o; - return (region.ID == ID); - } + return (region.ID == ID); + } - public static bool operator==(Region lhs, Region rhs) - { - try - { - return (lhs.ID == rhs.ID); - } - catch (NullReferenceException) - { - byte test; - bool lhsnull = false; - bool rhsnull = false; + public static bool operator ==(Region lhs, Region rhs) + { + try + { + return (lhs.ID == rhs.ID); + } + catch (NullReferenceException) + { + byte test; + bool lhsnull = false; + bool rhsnull = false; - try - { - test = lhs.ID.Data[0]; - } - catch (NullReferenceException) - { - lhsnull = true; - } + try + { + test = lhs.ID.Data[0]; + } + catch (NullReferenceException) + { + lhsnull = true; + } - try - { - test = rhs.ID.Data[0]; - } - catch (NullReferenceException) - { - rhsnull = true; - } + try + { + test = rhs.ID.Data[0]; + } + catch (NullReferenceException) + { + rhsnull = true; + } - return (lhsnull == rhsnull); - } - } + return (lhsnull == rhsnull); + } + } - public static bool operator!=(Region lhs, Region rhs) - { - return !(lhs == rhs); - } - } + public static bool operator !=(Region lhs, Region rhs) + { + return !(lhs == rhs); + } + } } diff --git a/libsecondlife-cs/SecondLife.cs b/libsecondlife-cs/SecondLife.cs index aa966d92..d312f450 100644 --- a/libsecondlife-cs/SecondLife.cs +++ b/libsecondlife-cs/SecondLife.cs @@ -30,344 +30,358 @@ using System.Threading; namespace libsecondlife { - /// - /// Main class to expose Second Life functionality to clients. All of the - /// classes are accessible through this class. - /// - public class SecondLife - { - public ProtocolManager Protocol; - public NetworkManager Network; - public ParcelManager Parcels; - public MainAvatar Avatar; - public Hashtable Avatars; - public Mutex AvatarsMutex; - public Inventory Inventory; - public Region CurrentRegion; - public GridManager Grid; + /// + /// Main class to expose Second Life functionality to clients. All of the + /// classes are accessible through this class. + /// + public class SecondLife + { + public ProtocolManager Protocol; + public NetworkManager Network; + public ParcelManager Parcels; + public MainAvatar Avatar; + public Hashtable Avatars; + public Mutex AvatarsMutex; + public Inventory Inventory; + public Region CurrentRegion; + public GridManager Grid; + public bool Debug; - public SecondLife(string keywordFile, string mapFile) - { - Protocol = new ProtocolManager(keywordFile, mapFile); - Network = new NetworkManager(this, Protocol); - Parcels = new ParcelManager(this); - Avatar = new MainAvatar(this); - Avatars = new Hashtable(); - AvatarsMutex = new Mutex(false, "AvatarsMutex"); - Inventory = new Inventory(this); - Grid = new GridManager(this); - CurrentRegion = null; - } + public SecondLife(string keywordFile, string mapFile) + { + Protocol = new ProtocolManager(keywordFile, mapFile, this); + Network = new NetworkManager(this, Protocol); + Parcels = new ParcelManager(this); + Avatar = new MainAvatar(this); + Avatars = new Hashtable(); + AvatarsMutex = new Mutex(false, "AvatarsMutex"); + Inventory = new Inventory(this); + Grid = new GridManager(this); + CurrentRegion = null; + Debug = true; - public override string ToString() - { - return Avatar.FirstName + " " + Avatar.LastName; - } + Network.RegisterCallback("UUIDNameReply", new PacketCallback(GetAgentNameHandler)); + } - public void Tick() - { - System.Threading.Thread.Sleep(0); - } + public override string ToString() + { + return Avatar.FirstName + " " + Avatar.LastName; + } - public void AddAvatar(LLUUID AgentID) - { - // Quick sanity check - if(Avatars.ContainsKey(AgentID)) - { - return; - } + /// + /// A simple sleep function that will allow pending threads to run + /// + public void Tick() + { + System.Threading.Thread.Sleep(0); + } - GetAgentDetails(AgentID); + /// + /// Send a log message to the debugging output system + /// + /// The log message + /// From the LogLevel enum, either Info, Warning, or Error + public void Log(string message, Helpers.LogLevel level) + { + if (Debug) + { + Console.WriteLine(level.ToString() + ": " + message); + } + } - AvatarsMutex.WaitOne(); - Avatars[AgentID] = new Avatar(); - AvatarsMutex.ReleaseMutex(); + /// + /// + /// + /// + public void AddAvatar(LLUUID AgentID) + { + // Quick sanity check + if (Avatars.ContainsKey(AgentID)) + { + return; + } - return; - } + GetAgentDetails(AgentID); - private void GetAgentDetails(LLUUID AgentID) - { - PacketCallback callback = new PacketCallback(GetAgentNameHandler); - Network.RegisterCallback("UUIDNameReply", callback); + AvatarsMutex.WaitOne(); + Avatars[AgentID] = new Avatar(); + AvatarsMutex.ReleaseMutex(); - Packet packet = Packets.Communication.UUIDNameRequest(Protocol, AgentID); - Network.SendPacket(packet); - } + return; + } - private void GetAgentNameHandler(Packet packet, Simulator simulator) - { - if (packet.Layout.Name == "UUIDNameReply") - { - LLUUID ID = new LLUUID(); - string Firstname = ""; - string Lastname = ""; + /// + /// + /// + /// + private void GetAgentDetails(LLUUID AgentID) + { + Packet packet = Packets.Communication.UUIDNameRequest(Protocol, AgentID); + Network.SendPacket(packet); - ArrayList blocks; + // TODO: Shouldn't this function block? + } - blocks = packet.Blocks(); + /// + /// + /// + /// + /// + private void GetAgentNameHandler(Packet packet, Simulator simulator) + { + if (packet.Layout.Name == "UUIDNameReply") + { + LLUUID ID = new LLUUID(); + string Firstname = ""; + string Lastname = ""; - foreach (Block block in blocks) - { - foreach (Field field in block.Fields) - { - if(field.Layout.Name == "ID") - { - ID = (LLUUID)field.Data; - } - else if(field.Layout.Name == "FirstName") - { - Firstname = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); - } - else if(field.Layout.Name == "LastName") - { - Lastname = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); - } - } - } - AvatarsMutex.WaitOne(); - ((Avatar)Avatars[ID]).Name = Firstname + " " + Lastname; - AvatarsMutex.ReleaseMutex(); - } - } - } + ArrayList blocks; - /// - /// Static helper functions and global variables - /// - public class Helpers - { - public readonly static string VERSION = "libsecondlife-cs 0.0.5"; + blocks = packet.Blocks(); - public const byte MSG_APPENDED_ACKS = 0x10; - public const byte MSG_RESENT = 0x20; - public const byte MSG_RELIABLE = 0x40; - public const byte MSG_ZEROCODED = 0x80; - public const ushort MSG_FREQ_HIGH = 0x0000; - public const ushort MSG_FREQ_MED = 0xFF00; - public const ushort MSG_FREQ_LOW = 0xFFFF; + foreach (Block block in blocks) + { + foreach (Field field in block.Fields) + { + if (field.Layout.Name == "ID") + { + ID = (LLUUID)field.Data; + } + else if (field.Layout.Name == "FirstName") + { + Firstname = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); + } + else if (field.Layout.Name == "LastName") + { + Lastname = System.Text.Encoding.UTF8.GetString((byte[])field.Data).Replace("\0", ""); + } + } + } + AvatarsMutex.WaitOne(); + ((Avatar)Avatars[ID]).Name = Firstname + " " + Lastname; + AvatarsMutex.ReleaseMutex(); + } + } + } - public enum LogLevel - { - Info, - Warning, - Error - }; + /// + /// Static helper functions and global variables + /// + public class Helpers + { + public readonly static string VERSION = "libsecondlife-cs 0.0.6"; - /// - /// Send a log message to the debugging output system - /// - /// The log message - /// From the LogLevel enum, either Info, Warning, or Error - public static void Log(string message, LogLevel level) - { - Console.WriteLine(level.ToString() + ": " + message); - } + public const byte MSG_APPENDED_ACKS = 0x10; + public const byte MSG_RESENT = 0x20; + public const byte MSG_RELIABLE = 0x40; + public const byte MSG_ZEROCODED = 0x80; + public const ushort MSG_FREQ_HIGH = 0x0000; + public const ushort MSG_FREQ_MED = 0xFF00; + public const ushort MSG_FREQ_LOW = 0xFFFF; - /// - /// Converting a variable length field (byte array) to a string - /// - /// The Data member of the Field class you are converting - public static string FieldToString(object data) - { - byte[] byteArray; + public enum LogLevel + { + Info, + Warning, + Error + }; - try - { - byteArray = (byte[])data; - } - catch (Exception e) - { - Helpers.Log(e.ToString(), Helpers.LogLevel.Warning); - return ""; - } + /// + /// Converting a variable length field (byte array) to a string + /// + /// The Data member of the Field class you are converting + public static string FieldToString(object data) + { + byte[] byteArray; - return System.Text.Encoding.ASCII.GetString(byteArray).Replace("\0", ""); - } - - /// - /// Takes a quantized value and its quantization range and returns a float - /// representation of the continuous value. For example, a value of 32767 - /// and a range of -128.0 to 128.0 would return approx. -0.0019531548028 - /// - /// The 16-bit quantized value - /// The lower quantization range - /// The upper quantization range - /// - public static float Dequantize(uint value, float lower, float upper) - { - decimal QV = (decimal)value; - decimal range = (decimal)(upper - lower); - decimal QF = (range) / 65535.0m; - return (float)(QV * QF - (0.5m * range)); - } - - /// - /// Decode a zerocoded byte array. Used to decompress packets marked - /// with the zerocoded flag. Any time a zero is encountered, the - /// next byte is a count of how many zeroes to expand. One zero is - /// encoded with 0x00 0x01, two zeroes is 0x00 0x02, three zeroes is - /// 0x00 0x03, etc. The first four bytes are copied directly to the - /// output buffer. - /// - /// The byte array to decode - /// The length of the byte array to decode - /// The output byte array to decode to - /// The length of the output buffer - public static int ZeroDecode(byte[] src, int srclen, byte[] dest) - { - uint zerolen = 0; + try + { + byteArray = (byte[])data; + } + catch (Exception) + { + return "[object]"; + } - try - { - Array.Copy(src, 0, dest, 0, 4); - zerolen += 4; + return System.Text.Encoding.ASCII.GetString(byteArray).Replace("\0", ""); + } - int bodylen; - if ((src[0] & MSG_APPENDED_ACKS) == 0) - { - bodylen = srclen; - } - else - { - bodylen = srclen - src[srclen - 1] * 4 - 1; - } + /// + /// Takes a quantized value and its quantization range and returns a float + /// representation of the continuous value. For example, a value of 32767 + /// and a range of -128.0 to 128.0 would return approx. -0.0019531548028 + /// + /// The 16-bit quantized value + /// The lower quantization range + /// The upper quantization range + /// + public static float Dequantize(uint value, float lower, float upper) + { + decimal QV = (decimal)value; + decimal range = (decimal)(upper - lower); + decimal QF = (range) / 65535.0m; + return (float)(QV * QF - (0.5m * range)); + } - uint i; - for (i = zerolen; i < bodylen; i++) - { - if (src[i] == 0x00) - { - for (byte j = 0; j < src[i + 1]; j++) - { - dest[zerolen++] = 0x00; - } + /// + /// Decode a zerocoded byte array. Used to decompress packets marked + /// with the zerocoded flag. Any time a zero is encountered, the + /// next byte is a count of how many zeroes to expand. One zero is + /// encoded with 0x00 0x01, two zeroes is 0x00 0x02, three zeroes is + /// 0x00 0x03, etc. The first four bytes are copied directly to the + /// output buffer. + /// + /// The byte array to decode + /// The length of the byte array to decode + /// The output byte array to decode to + /// The length of the output buffer + public static int ZeroDecode(byte[] src, int srclen, byte[] dest) + { + uint zerolen = 0; - i++; - } - else - { - dest[zerolen++] = src[i]; - } - } + Array.Copy(src, 0, dest, 0, 4); + zerolen += 4; - // HACK: Fix truncated zerocoded messages - for (uint j = zerolen; j < zerolen + 16; j++) - { - dest[j] = 0; - } - zerolen += 16; + int bodylen; + if ((src[0] & MSG_APPENDED_ACKS) == 0) + { + bodylen = srclen; + } + else + { + bodylen = srclen - src[srclen - 1] * 4 - 1; + } - // copy appended ACKs - for (; i < srclen; i++) - { - dest[zerolen++] = src[i]; - } - } - catch (Exception e) - { - Helpers.Log(e.ToString(), Helpers.LogLevel.Error); - } + uint i; + for (i = zerolen; i < bodylen; i++) + { + if (src[i] == 0x00) + { + for (byte j = 0; j < src[i + 1]; j++) + { + dest[zerolen++] = 0x00; + } - return (int)zerolen; - } - - /// - /// Decode enough of a byte array to get the packet ID. Data before and - /// after the packet ID is undefined. - /// - /// The byte array to decode - /// The output byte array to encode to - public static void ZeroDecodeCommand(byte[] src, byte[] dest) - { - for (int srcPos = 4, destPos = 4; destPos < 8; ++srcPos) - { - if (src[srcPos] == 0x00) - { - for (byte j = 0; j < src[srcPos + 1]; ++j) - { - dest[destPos++] = 0x00; - } + i++; + } + else + { + dest[zerolen++] = src[i]; + } + } - ++srcPos; - } - else - { - dest[destPos++] = src[srcPos]; - } - } - } + // HACK: Fix truncated zerocoded messages + for (uint j = zerolen; j < zerolen + 16; j++) + { + dest[j] = 0; + } + zerolen += 16; - /// - /// Encode a byte array with zerocoding. Used to compress packets marked - /// with the zerocoded flag. Any zeroes in the array are compressed down - /// to a single zero byte followed by a count of how many zeroes to expand - /// out. A single zero becomes 0x00 0x01, two zeroes becomes 0x00 0x02, - /// three zeroes becomes 0x00 0x03, etc. The first four bytes are copied - /// directly to the output buffer. - /// - /// The byte array to encode - /// The length of the byte array to encode - /// The output byte array to encode to - /// The length of the output buffer - public static int ZeroEncode(byte[] src, int srclen, byte[] dest) - { - uint zerolen = 0; - byte zerocount = 0; + // copy appended ACKs + for (; i < srclen; i++) + { + dest[zerolen++] = src[i]; + } - Array.Copy(src, 0, dest, 0, 4); - zerolen += 4; + return (int)zerolen; + } - int bodylen; - if ((src[0] & MSG_APPENDED_ACKS) == 0) - { - bodylen = srclen; - } - else - { - bodylen = srclen - src[srclen - 1] * 4 - 1; - } + /// + /// Decode enough of a byte array to get the packet ID. Data before and + /// after the packet ID is undefined. + /// + /// The byte array to decode + /// The output byte array to encode to + public static void ZeroDecodeCommand(byte[] src, byte[] dest) + { + for (int srcPos = 4, destPos = 4; destPos < 8; ++srcPos) + { + if (src[srcPos] == 0x00) + { + for (byte j = 0; j < src[srcPos + 1]; ++j) + { + dest[destPos++] = 0x00; + } - uint i; - for (i = zerolen; i < bodylen; i++) - { - if (src[i] == 0x00) - { - zerocount++; + ++srcPos; + } + else + { + dest[destPos++] = src[srcPos]; + } + } + } - if (zerocount == 0) - { - dest[zerolen++] = 0x00; - dest[zerolen++] = 0xff; - zerocount++; - } - } - else - { - if (zerocount != 0) - { - dest[zerolen++] = 0x00; - dest[zerolen++] = (byte)zerocount; - zerocount = 0; - } + /// + /// Encode a byte array with zerocoding. Used to compress packets marked + /// with the zerocoded flag. Any zeroes in the array are compressed down + /// to a single zero byte followed by a count of how many zeroes to expand + /// out. A single zero becomes 0x00 0x01, two zeroes becomes 0x00 0x02, + /// three zeroes becomes 0x00 0x03, etc. The first four bytes are copied + /// directly to the output buffer. + /// + /// The byte array to encode + /// The length of the byte array to encode + /// The output byte array to encode to + /// The length of the output buffer + public static int ZeroEncode(byte[] src, int srclen, byte[] dest) + { + uint zerolen = 0; + byte zerocount = 0; - dest[zerolen++] = src[i]; - } - } + Array.Copy(src, 0, dest, 0, 4); + zerolen += 4; - if (zerocount != 0) - { - dest[zerolen++] = 0x00; - dest[zerolen++] = (byte)zerocount; - } + int bodylen; + if ((src[0] & MSG_APPENDED_ACKS) == 0) + { + bodylen = srclen; + } + else + { + bodylen = srclen - src[srclen - 1] * 4 - 1; + } - // copy appended ACKs - for (; i < srclen; i++) - { - dest[zerolen++] = src[i]; - } + uint i; + for (i = zerolen; i < bodylen; i++) + { + if (src[i] == 0x00) + { + zerocount++; - return (int)zerolen; - } - } + if (zerocount == 0) + { + dest[zerolen++] = 0x00; + dest[zerolen++] = 0xff; + zerocount++; + } + } + else + { + if (zerocount != 0) + { + dest[zerolen++] = 0x00; + dest[zerolen++] = (byte)zerocount; + zerocount = 0; + } + + dest[zerolen++] = src[i]; + } + } + + if (zerocount != 0) + { + dest[zerolen++] = 0x00; + dest[zerolen++] = (byte)zerocount; + } + + // copy appended ACKs + for (; i < srclen; i++) + { + dest[zerolen++] = src[i]; + } + + return (int)zerolen; + } + } }