From 4cdcf553979cffd60d06f604400a8a0b3f9c8056 Mon Sep 17 00:00:00 2001 From: Date: Sat, 9 Dec 2006 00:45:40 +0000 Subject: [PATCH] Moved MainAvatar class to new MainAvatar.cs Mapped a few more dialog values in InstantMessageDialog git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@695 52acb1d6-8a22-11de-b505-999d5b087335 --- libsecondlife-cs/Avatar.cs | 935 +------------------------ libsecondlife-cs/MainAvatar.cs | 954 ++++++++++++++++++++++++++ libsecondlife-cs/libsecondlife.csproj | 1 + 3 files changed, 969 insertions(+), 921 deletions(-) create mode 100644 libsecondlife-cs/MainAvatar.cs diff --git a/libsecondlife-cs/Avatar.cs b/libsecondlife-cs/Avatar.cs index 67611f22..f04bbe21 100644 --- a/libsecondlife-cs/Avatar.cs +++ b/libsecondlife-cs/Avatar.cs @@ -99,12 +99,22 @@ namespace libsecondlife /// public enum InstantMessageDialog { - /// + /// Indicates a regular IM from another agent + MessageFromAgent = 0, + /// Indicates that someone has given the user inventory + GiveInventory = 4, + /// Indicates that the IM is from an object + MessageFromObject = 19, + /// Indicates that the IM is a teleport invitation RequestTeleport = 22, - /// + /// Response sent to the agent which inititiated a teleport invitation AcceptTeleport = 23, - /// - DenyTeleport = 24 + /// Response sent to the agent which inititiated a teleport invitation + DenyTeleport = 24, + /// Indicates that a user has started typing + StartTyping = 41, + /// Indicates that a user has stopped typing + StopTyping = 42 } /// @@ -279,921 +289,4 @@ namespace libsecondlife protected const int TOTAL_CONTROLS = 32; } - /// - /// Class to hold Client Avatar's data - /// - public class MainAvatar - { - /// Callback for incoming chat packets - public event ChatCallback OnChat; - /// Callback for incoming IMs - public event InstantMessageCallback OnInstantMessage; - /// Callback for Teleport request update - public event TeleportCallback OnTeleport; - /// Callback for incoming change in L$ balance - public event BalanceCallback OnBalanceUpdated; - - /// Your (client) Avatar UUID, asset server - public LLUUID ID = LLUUID.Zero; - /// Your (client) Avatar ID, local to Region/sim - public uint LocalID; - /// Avatar First Name (i.e. Philip) - public string FirstName = ""; - /// Avatar Last Name (i.e. Linden) - public string LastName = ""; - /// - public string TeleportMessage; - /// Current position of avatar - public LLVector3 Position = LLVector3.Zero; - /// Current rotation of avatar - public LLQuaternion Rotation = LLQuaternion.Identity; - /// The point the avatar is currently looking at - /// (may not stay updated) - public LLVector3 LookAt = LLVector3.Zero; - /// Position avatar client will goto when login to 'home' or during - /// teleport request to 'home' region. - public LLVector3 HomePosition = LLVector3.Zero; - /// LookAt point saved/restored with HomePosition - public LLVector3 HomeLookAt = LLVector3.Zero; - /// Gets the health of the agent - protected float health; - public float Health - { - get { return health; } - } - /// Gets the current balance of the agent - protected int balance; - public int Balance - { - get { return balance; } - } - - private SecondLife Client; - private TeleportCallback OnBeginTeleport; - private TeleportStatus TeleportStat; - private Timer TeleportTimer; - private bool TeleportTimeout; - private uint HeightWidthGenCounter; - - /// - /// Constructor, aka 'CallBack Central' - Setup callbacks for packets related to our avatar - /// - /// - public MainAvatar(SecondLife client) - { - NetworkManager.PacketCallback callback; - Client = client; - TeleportMessage = ""; - - // Coarse location callback - Client.Network.RegisterCallback(PacketType.CoarseLocationUpdate, new NetworkManager.PacketCallback(CoarseLocationHandler)); - - // Teleport callbacks - callback = new NetworkManager.PacketCallback(TeleportHandler); - Client.Network.RegisterCallback(PacketType.TeleportStart, callback); - Client.Network.RegisterCallback(PacketType.TeleportProgress, callback); - Client.Network.RegisterCallback(PacketType.TeleportFailed, callback); - Client.Network.RegisterCallback(PacketType.TeleportFinish, callback); - - // Instant Message callback - Client.Network.RegisterCallback(PacketType.ImprovedInstantMessage, new NetworkManager.PacketCallback(InstantMessageHandler)); - - // Chat callback - Client.Network.RegisterCallback(PacketType.ChatFromSimulator, new NetworkManager.PacketCallback(ChatHandler)); - - TeleportTimer = new Timer(18000); - TeleportTimer.Elapsed += new ElapsedEventHandler(TeleportTimerEvent); - TeleportTimeout = false; - - // Movement complete callback - Client.Network.RegisterCallback(PacketType.AgentMovementComplete, new NetworkManager.PacketCallback(MovementCompleteHandler)); - - // Health callback - Client.Network.RegisterCallback(PacketType.HealthMessage, new NetworkManager.PacketCallback(HealthHandler)); - - // Money callbacks - callback = new NetworkManager.PacketCallback(BalanceHandler); - Client.Network.RegisterCallback(PacketType.MoneyBalanceReply, callback); - Client.Network.RegisterCallback(PacketType.MoneySummaryReply, callback); - Client.Network.RegisterCallback(PacketType.AdjustBalance, callback); - } - - /// - /// - /// - /// - /// - public void InstantMessage(LLUUID target, string message) - { - InstantMessage(FirstName + " " + LastName, LLUUID.Random(), target, message, null, LLUUID.Random()); - } - - /// - /// - /// - /// - /// - /// - public void InstantMessage(LLUUID target, string message, LLUUID IMSessionID) - { - InstantMessage(FirstName + " " + LastName, LLUUID.Random(), target, message, null, IMSessionID); - } - - /// - /// - /// - /// - /// - /// - /// - /// - public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, LLUUID[] conferenceIDs) - { - InstantMessage(fromName, sessionID, target, message, conferenceIDs, LLUUID.Random()); - } - - /// - /// Generate an Instant Message (Full Arguments). - /// - /// Client's Avatar - /// SessionID of current connection to grid - /// UUID of target Av. - /// Text Message being sent. - /// - /// - /// - /// TODO: Have fromName grabbed from elsewhere and remove argument, to prevent inadvertant spoofing. - /// - public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, - LLUUID[] conferenceIDs, LLUUID IMSessionID) - { - ImprovedInstantMessagePacket im = new ImprovedInstantMessagePacket(); - im.AgentData.AgentID = this.ID; - im.AgentData.SessionID = Client.Network.SessionID; - im.MessageBlock.Dialog = 0; - im.MessageBlock.FromAgentName = Helpers.StringToField(fromName); - im.MessageBlock.FromGroup = false; - im.MessageBlock.ID = IMSessionID; - im.MessageBlock.Message = Helpers.StringToField(message); - im.MessageBlock.Offline = 1; - im.MessageBlock.ToAgentID = target; - if (conferenceIDs != null && conferenceIDs.Length > 0) - { - im.MessageBlock.BinaryBucket = new byte[16 * conferenceIDs.Length]; - - for (int i = 0; i < conferenceIDs.Length; ++i) - { - Array.Copy(conferenceIDs[i].Data, 0, im.MessageBlock.BinaryBucket, i * 16, 16); - } - } - else - { - im.MessageBlock.BinaryBucket = new byte[0]; - } - - // These fields are mandatory, even if we don't have valid values for them - im.MessageBlock.Position = LLVector3.Zero; - //TODO: Allow region id to be correctly set by caller or fetched from Client.* - im.MessageBlock.RegionID = LLUUID.Zero; - - - // Send the message - Client.Network.SendPacket((Packet)im); - } - - /// - /// Conversion type to denote Chat Packet types in an easier-to-understand format - /// - public enum ChatType - { - /// Whispers (5m radius) - Whisper = 0, - /// Normal chat (10/20m radius) - Why is this here twice? - Normal = 1, - /// Shouting! (100m radius) - Shout = 2, - /// Normal chat (10/20m radius) - Why is this here twice? - Say = 3, - /// Event message when an Avatar has begun to type - StartTyping = 4, - /// Event message when an Avatar has stopped typing - StopTyping = 5 - } - - /// - /// Send a Chat message. - /// - /// The Message you're sending out. - /// Channel number (0 would be default 'Say' message, other numbers - /// denote the equivalent of /# in normal client). - /// Chat Type, see above. - public void Chat(string message, int channel, ChatType type) - { - ChatFromViewerPacket chat = new ChatFromViewerPacket(); - chat.AgentData.AgentID = this.ID; - chat.AgentData.SessionID = Client.Network.SessionID; - chat.ChatData.Channel = channel; - chat.ChatData.Message = Helpers.StringToField(message); - chat.ChatData.Type = (byte)type; - - Client.Network.SendPacket((Packet)chat); - } - - /// - /// Set the height and the width of your avatar. This is used to scale - /// the avatar mesh. - /// - /// New height of the avatar - /// New width of the avatar - public void SetHeightWidth(ushort height, ushort width) - { - AgentHeightWidthPacket heightwidth = new AgentHeightWidthPacket(); - heightwidth.AgentData.AgentID = Client.Network.AgentID; - heightwidth.AgentData.SessionID = Client.Network.SessionID; - heightwidth.AgentData.CircuitCode = Client.Network.CurrentSim.CircuitCode; - heightwidth.HeightWidthBlock.Height = height; - heightwidth.HeightWidthBlock.Width = width; - heightwidth.HeightWidthBlock.GenCounter = HeightWidthGenCounter++; - - Client.Network.SendPacket((Packet)heightwidth); - } - - /// - /// Sends a request to sit on the specified object - /// - /// LLUUID of the object to sit on - /// Sit at offset - public void RequestSit(LLUUID targetID, LLVector3 offset) - { - AgentRequestSitPacket requestSit = new AgentRequestSitPacket(); - requestSit.AgentData.AgentID = Client.Network.AgentID; - requestSit.AgentData.SessionID = Client.Network.SessionID; - requestSit.TargetObject.TargetID = targetID; - requestSit.TargetObject.Offset = offset; - Client.Network.SendPacket(requestSit); - } - - /// - /// Follows a call to RequestSit() to actually sit on the object - /// - public void Sit() - { - AgentSitPacket sit = new AgentSitPacket(); - sit.AgentData.AgentID = Client.Network.AgentID; - sit.AgentData.SessionID = Client.Network.SessionID; - Client.Network.SendPacket(sit); - } - - /// - /// Give Money to destination Avatar - /// - /// UUID of the Target Avatar - /// Amount in L$ - /// Reason (optional normally) - public void GiveMoney(LLUUID target, int amount, string description) - { - // 5001 - transaction type for av to av money transfers - - GiveMoney(target, amount, description, 5001); - } - - /// - /// Toggle running on or off - /// - public void SetAlwaysRun(bool running) - { - SetAlwaysRunPacket run = new SetAlwaysRunPacket(); - run.AgentData.AgentID = Client.Network.AgentID; - run.AgentData.SessionID = Client.Network.SessionID; - run.AgentData.AlwaysRun = running; - Client.Network.SendPacket(run); - } - - /// - /// Give Money to destionation Object or Avatar - /// - /// UUID of the Target Object/Avatar - /// Amount in L$ - /// Reason (Optional normally) - /// The type of transaction. Currently only 5001 is - /// documented for Av->Av money transfers. - public void GiveMoney(LLUUID target, int amount, string description, int transactiontype) - { - MoneyTransferRequestPacket money = new MoneyTransferRequestPacket(); - money.AgentData.AgentID = this.ID; - money.AgentData.SessionID = Client.Network.SessionID; - money.MoneyData.Description = Helpers.StringToField(description); - money.MoneyData.DestID = target; - money.MoneyData.SourceID = this.ID; - money.MoneyData.TransactionType = transactiontype; - money.MoneyData.AggregatePermInventory = 0; //TODO: whats this? - money.MoneyData.AggregatePermNextOwner = 0; //TODO: whats this? - money.MoneyData.Flags = 0; //TODO: whats this? - money.MoneyData.Amount = amount; - - Client.Network.SendPacket((Packet)money); - } - - /// - /// Send an AgentAnimation packet that will toggle animations on or off - /// - /// A list of animation UUIDs, and whether to - /// turn that animation on or off - public void Animate(Dictionary animations) - { - AgentAnimationPacket animate = new AgentAnimationPacket(); - - animate.AgentData.AgentID = Client.Network.AgentID; - animate.AgentData.SessionID = Client.Network.SessionID; - animate.AnimationList = new AgentAnimationPacket.AnimationListBlock[animations.Count]; - int i = 0; - - foreach (KeyValuePair animation in animations) - { - animate.AnimationList[i] = new AgentAnimationPacket.AnimationListBlock(); - animate.AnimationList[i].AnimID = animation.Key; - animate.AnimationList[i].StartAnim = animation.Value; - - i++; - } - - Client.Network.SendPacket(animate); - } - - /// - /// Use the autopilot sim function to move the avatar to a new position - /// - /// The z value is currently not handled properly by the simulator - /// Integer value for the global X coordinate to move to - /// Integer value for the global Y coordinate to move to - /// Floating-point value for the Z coordinate to move to - /// AutoPilot(252620, 247078, 20.2674); - public void AutoPilot(ulong globalX, ulong globalY, float z) - { - GenericMessagePacket autopilot = new GenericMessagePacket(); - - autopilot.AgentData.AgentID = Client.Network.AgentID; - autopilot.AgentData.SessionID = Client.Network.SessionID; - autopilot.MethodData.Invoice = LLUUID.Zero; - autopilot.MethodData.Method = Helpers.StringToField("autopilot"); - autopilot.ParamList = new GenericMessagePacket.ParamListBlock[3]; - autopilot.ParamList[0] = new GenericMessagePacket.ParamListBlock(); - autopilot.ParamList[0].Parameter = Helpers.StringToField(globalX.ToString()); - autopilot.ParamList[1] = new GenericMessagePacket.ParamListBlock(); - autopilot.ParamList[1].Parameter = Helpers.StringToField(globalY.ToString()); - autopilot.ParamList[2] = new GenericMessagePacket.ParamListBlock(); - // TODO: Do we need to prevent z coordinates from being sent in 1.4827e-18 notation? - autopilot.ParamList[2].Parameter = Helpers.StringToField(z.ToString()); - - Client.Network.SendPacket(autopilot); - } - - /// - /// Use the autopilot sim function to move the avatar to a new position - /// - /// The z value is currently not handled properly by the simulator - /// Integer value for the local X coordinate to move to - /// Integer value for the local Y coordinate to move to - /// Floating-point value for the Z coordinate to move to - /// AutoPilot(252620, 247078, 20.2674); - public void AutoPilotLocal(int localX, int localY, float z) - { - GridRegion gr = Client.Network.CurrentSim.Region.GridRegionData; - ulong GridCornerX = ((ulong)gr.X * (ulong)256) + (ulong)localX; - ulong GridCornerY = ((ulong)gr.Y * (ulong)256) + (ulong)localY; - AutoPilot(GridCornerX, GridCornerY, z); - } - - /// - /// - /// - /// - /// - /// - public void BeginTeleport(ulong regionHandle, LLVector3 position, TeleportCallback tc) - { - BeginTeleport(regionHandle, position, new LLVector3(position.X + 1.0f, position.Y, position.Z), tc); - } - - /// - /// - /// - /// - /// - /// - /// - public void BeginTeleport(ulong regionHandle, LLVector3 position, LLVector3 lookAt, TeleportCallback tc) - { - OnBeginTeleport = tc; - - TeleportLocationRequestPacket teleport = new TeleportLocationRequestPacket(); - teleport.AgentData.AgentID = Client.Network.AgentID; - teleport.AgentData.SessionID = Client.Network.SessionID; - teleport.Info.LookAt = lookAt; - teleport.Info.Position = position; - teleport.Info.RegionHandle = regionHandle; - - Client.Log("Teleporting to region " + regionHandle.ToString(), Helpers.LogLevel.Info); - - Client.Network.SendPacket(teleport); - } - - /// - /// - /// - /// - /// - /// - public bool Teleport(ulong regionHandle, LLVector3 position) - { - return Teleport(regionHandle, position, new LLVector3(position.X + 1.0f, position.Y, position.Z)); - } - - /// - /// - /// - /// - /// - /// - /// - public bool Teleport(ulong regionHandle, LLVector3 position, LLVector3 lookAt) - { - TeleportStat = TeleportStatus.None; - - TeleportLocationRequestPacket teleport = new TeleportLocationRequestPacket(); - teleport.AgentData.AgentID = Client.Network.AgentID; - teleport.AgentData.SessionID = Client.Network.SessionID; - teleport.Info.LookAt = lookAt; - teleport.Info.Position = position; - - teleport.Info.RegionHandle = regionHandle; - - Client.Log("Teleporting to region " + regionHandle.ToString(), Helpers.LogLevel.Info); - - // Start the timeout check - TeleportTimeout = false; - TeleportTimer.Start(); - - Client.Network.SendPacket(teleport); - - while (TeleportStat != TeleportStatus.Failed && TeleportStat != TeleportStatus.Finished && !TeleportTimeout) - { - Client.Tick(); - } - - TeleportTimer.Stop(); - - if (TeleportTimeout) - { - TeleportMessage = "Teleport timed out."; - TeleportStat = TeleportStatus.Failed; - - if (OnTeleport != null) { OnTeleport(TeleportMessage, TeleportStat); } - } - else - { - if (OnTeleport != null) { OnTeleport(TeleportMessage, TeleportStat); } - } - - return (TeleportStat == TeleportStatus.Finished); - } - - /// - /// - /// - /// - /// - /// - public bool Teleport(string simName, LLVector3 position) - { - //position.Z = 0; //why was this here? - return Teleport(simName, position, new LLVector3(0, 1.0F, 0)); - } - - /// - /// - /// - /// - /// - /// - /// - public bool Teleport(string simName, LLVector3 position, LLVector3 lookAt) - { - int attempts = 0; - TeleportStat = TeleportStatus.None; - - simName = simName.ToLower(); - - GridRegion region = Client.Grid.GetGridRegion(simName); - - if (region != null) - { - return Teleport(region.RegionHandle, position, lookAt); - } - else - { - while (attempts++ < 5) - { - region = Client.Grid.GetGridRegion(simName); - - if (region != null) - { - return Teleport(region.RegionHandle, position, lookAt); - } - else - { - // Request the region info again - Client.Grid.AddSim(simName); - - System.Threading.Thread.Sleep(1000); - } - } - } - - if (OnTeleport != null) - { - TeleportMessage = "Unable to resolve name: " + simName; - TeleportStat = TeleportStatus.Failed; - OnTeleport(TeleportMessage, TeleportStat); - } - - return false; - } - - /// - /// Respond to a teleport lure by either accepting it and initiating - /// the teleport, or denying it - /// - /// UUID of the avatar requesting the teleport - /// Accept the teleport request or deny it - public void TeleportLureRespond(LLUUID requesterID, bool accept) - { - ImprovedInstantMessagePacket im = new ImprovedInstantMessagePacket(); - - im.AgentData.AgentID = Client.Network.AgentID; - im.AgentData.SessionID = Client.Network.SessionID; - im.MessageBlock.BinaryBucket = new byte[0]; - im.MessageBlock.FromAgentName = Helpers.StringToField(this.FirstName + " " + this.LastName); - im.MessageBlock.FromGroup = false; - im.MessageBlock.ID = Client.Network.AgentID; - im.MessageBlock.Message = new byte[0]; - im.MessageBlock.Offline = 0; - im.MessageBlock.ParentEstateID = 0; - im.MessageBlock.Position = this.Position; - im.MessageBlock.RegionID = LLUUID.Zero; - im.MessageBlock.Timestamp = 0; - im.MessageBlock.ToAgentID = requesterID; - - if (accept) - { - im.MessageBlock.Dialog = (byte)InstantMessageDialog.AcceptTeleport; - - Client.Network.SendPacket(im); - - TeleportLureRequestPacket lure = new TeleportLureRequestPacket(); - - lure.Info.AgentID = Client.Network.AgentID; - lure.Info.SessionID = Client.Network.SessionID; - lure.Info.LureID = Client.Network.AgentID; - lure.Info.TeleportFlags = 4; // TODO: What does this mean? - - Client.Network.SendPacket(lure); - } - else - { - im.MessageBlock.Dialog = (byte)InstantMessageDialog.DenyTeleport; - - Client.Network.SendPacket(im); - } - } - - /// - /// Grabs an object - /// - public void Grab(uint objectLocalID) - { - ObjectGrabPacket grab = new ObjectGrabPacket(); - grab.AgentData.AgentID = Client.Network.AgentID; - grab.AgentData.SessionID = Client.Network.SessionID; - grab.ObjectData.LocalID = objectLocalID; - grab.ObjectData.GrabOffset = new LLVector3(0, 0, 0); - Client.Network.SendPacket(grab); - } - - /// - /// Drags on an object - /// - /// Strangely, LLUID instead of local ID - /// Drag target in region coordinates - public void GrabUpdate(LLUUID objectID, LLVector3 grabPosition) - { - ObjectGrabUpdatePacket grab = new ObjectGrabUpdatePacket(); - grab.AgentData.AgentID = Client.Network.AgentID; - grab.AgentData.SessionID = Client.Network.SessionID; - grab.ObjectData.ObjectID = objectID; - grab.ObjectData.GrabOffsetInitial = new LLVector3(0, 0, 0); - grab.ObjectData.GrabPosition = grabPosition; - grab.ObjectData.TimeSinceLast = 0; - Client.Network.SendPacket(grab); - } - - /// - /// Releases a grabbed object - /// - public void DeGrab(uint objectLocalID) - { - ObjectDeGrabPacket degrab = new ObjectDeGrabPacket(); - degrab.AgentData.AgentID = Client.Network.AgentID; - degrab.AgentData.SessionID = Client.Network.SessionID; - degrab.ObjectData.LocalID = objectLocalID; - Client.Network.SendPacket(degrab); - } - - /// - /// Touches an object - /// - public void Touch(uint objectLocalID) - { - Client.Self.Grab(objectLocalID); - Client.Self.DeGrab(objectLocalID); - } - - /// - /// - /// - /// - public void CompleteAgentMovement(Simulator simulator) - { - CompleteAgentMovementPacket move = new CompleteAgentMovementPacket(); - - move.AgentData.AgentID = Client.Network.AgentID; - move.AgentData.SessionID = Client.Network.SessionID; - move.AgentData.CircuitCode = simulator.CircuitCode; - - Client.Network.SendPacket(move, simulator); - } - - /// - /// - /// - /// - public void UpdateCamera(Avatar.AgentUpdateFlags controlFlags, LLVector3 position, LLVector3 forwardAxis, - LLVector3 leftAxis, LLVector3 upAxis, LLQuaternion bodyRotation, LLQuaternion headRotation, float farClip, - bool reliable) - { - AgentUpdatePacket update = new AgentUpdatePacket(); - update.AgentData.AgentID = Client.Network.AgentID; - update.AgentData.SessionID = Client.Network.SessionID; - update.AgentData.State = 0; - // Semi-sane default values - update.AgentData.BodyRotation = bodyRotation; //new LLQuaternion(0, 0.6519076f, 0, 0); - update.AgentData.HeadRotation = headRotation; //LLQuaternion.Identity; - update.AgentData.CameraCenter = position; //new LLVector3(9.549901f, 7.033957f, 11.75f); - update.AgentData.CameraAtAxis = forwardAxis; //new LLVector3(0.7f, 0.7f, 0); - update.AgentData.CameraLeftAxis = leftAxis; //new LLVector3(-0.7f, 0.7f, 0); - update.AgentData.CameraUpAxis = upAxis; //new LLVector3(0.1822026f, 0.9828722f, 0); - update.AgentData.Far = farClip; - update.AgentData.ControlFlags = (uint)controlFlags; - update.AgentData.Flags = 0; - update.Header.Reliable = reliable; - - Client.Network.SendPacket(update); - - // Send an AgentFOV packet widening our field of vision - /*AgentFOVPacket fovPacket = new AgentFOVPacket(); - fovPacket.AgentData.AgentID = this.ID; - fovPacket.AgentData.SessionID = Client.Network.SessionID; - fovPacket.AgentData.CircuitCode = simulator.CircuitCode; - fovPacket.FOVBlock.GenCounter = 0; - fovPacket.FOVBlock.VerticalAngle = 6.28318531f; - fovPacket.Header.Reliable = true; - Client.Network.SendPacket((Packet)fovPacket);*/ - } - - /// - /// [UNUSED - for now] - /// - /// - /// - private void CoarseLocationHandler(Packet packet, Simulator simulator) - { - // TODO: This will be useful one day - } - - /// - /// Take an incoming ImprovedInstantMessage packet, auto-parse, and if - /// OnInstantMessage is defined call that with the appropriate arguments. - /// - /// Incoming ImprovedInstantMessagePacket - /// [UNUSED] - private void InstantMessageHandler(Packet packet, Simulator simulator) - { - if (packet.Type == PacketType.ImprovedInstantMessage) - { - ImprovedInstantMessagePacket im = (ImprovedInstantMessagePacket)packet; - - if (OnInstantMessage != null) - { - OnInstantMessage( - im.AgentData.AgentID - , Helpers.FieldToString(im.MessageBlock.FromAgentName), - im.MessageBlock.ToAgentID - , im.MessageBlock.ParentEstateID - , im.MessageBlock.RegionID - , im.MessageBlock.Position - , im.MessageBlock.Dialog - , im.MessageBlock.FromGroup - , im.MessageBlock.ID - , new DateTime(im.MessageBlock.Timestamp) - , Helpers.FieldToString(im.MessageBlock.Message) - , im.MessageBlock.Offline - , im.MessageBlock.BinaryBucket - ); - } - } - } - - /// - /// Take an incoming Chat packet, auto-parse, and if OnChat is defined call - /// that with the appropriate arguments. - /// - /// Incoming ChatFromSimulatorPacket - /// [UNUSED] - private void ChatHandler(Packet packet, Simulator simulator) - { - if (packet.Type == PacketType.ChatFromSimulator) - { - ChatFromSimulatorPacket chat = (ChatFromSimulatorPacket)packet; - - if (OnChat != null) - { - OnChat(Helpers.FieldToString(chat.ChatData.Message) - , chat.ChatData.Audible - , chat.ChatData.ChatType - , chat.ChatData.SourceType - , Helpers.FieldToString(chat.ChatData.FromName) - , chat.ChatData.SourceID - , chat.ChatData.OwnerID - , chat.ChatData.Position - ); - } - } - } - - /// - /// Update client's Position and LookAt from incoming packet - /// - /// Incoming AgentMovementCompletePacket - /// [UNUSED] - private void MovementCompleteHandler(Packet packet, Simulator simulator) - { - AgentMovementCompletePacket movement = (AgentMovementCompletePacket)packet; - - this.Position = movement.Data.Position; - this.LookAt = movement.Data.LookAt; - } - - /// - /// Update Client Avatar's health via incoming packet - /// - /// Incoming HealthMessagePacket - /// [UNUSED] - private void HealthHandler(Packet packet, Simulator simulator) - { - health = ((HealthMessagePacket)packet).HealthData.Health; - } - - /// - /// Update Client Avatar's L$ balance from incoming packet - /// - /// Incoming MoneyBalanceReplyPacket - /// [UNUSED] - private void BalanceHandler(Packet packet, Simulator simulator) - { - if (packet.Type == PacketType.MoneyBalanceReply) - { - balance = ((MoneyBalanceReplyPacket)packet).MoneyData.MoneyBalance; - } - else if (packet.Type == PacketType.MoneySummaryReply) - { - balance = ((MoneySummaryReplyPacket)packet).MoneyData.Balance; - } - else if (packet.Type == PacketType.AdjustBalance) - { - balance += ((AdjustBalancePacket)packet).AgentData.Delta; - } - - if (OnBalanceUpdated != null) - { - OnBalanceUpdated(balance); - } - } - - /// - /// - /// - /// - /// - private void TeleportHandler(Packet packet, Simulator simulator) - { - if (packet.Type == PacketType.TeleportStart) - { - Client.DebugLog("TeleportStart received from " + simulator.ToString()); - - TeleportMessage = "Teleport started"; - TeleportStat = TeleportStatus.Start; - - if (OnBeginTeleport != null) - { - OnBeginTeleport(TeleportMessage, TeleportStat); - } - } - else if (packet.Type == PacketType.TeleportProgress) - { - Client.DebugLog("TeleportProgress received from " + simulator.ToString()); - - TeleportMessage = Helpers.FieldToString(((TeleportProgressPacket)packet).Info.Message); - TeleportStat = TeleportStatus.Progress; - - if (OnBeginTeleport != null) - { - OnBeginTeleport(TeleportMessage, TeleportStat); - } - } - else if (packet.Type == PacketType.TeleportFailed) - { - Client.DebugLog("TeleportFailed received from " + simulator.ToString()); - - TeleportMessage = Helpers.FieldToString(((TeleportFailedPacket)packet).Info.Reason); - TeleportStat = TeleportStatus.Failed; - - if (OnBeginTeleport != null) - { - OnBeginTeleport(TeleportMessage, TeleportStat); - } - - OnBeginTeleport = null; - } - else if (packet.Type == PacketType.TeleportFinish) - { - Client.DebugLog("TeleportFinish received from " + simulator.ToString()); - - TeleportFinishPacket finish = (TeleportFinishPacket)packet; - - // Connect to the new sim - Simulator sim = Client.Network.Connect(new IPAddress((long)finish.Info.SimIP), finish.Info.SimPort, - simulator.CircuitCode, true); - - if (sim != null) - { - TeleportMessage = "Teleport finished"; - TeleportStat = TeleportStatus.Finished; - - // Move the avatar in to the new sim - CompleteAgentMovementPacket move = new CompleteAgentMovementPacket(); - move.AgentData.AgentID = Client.Network.AgentID; - move.AgentData.SessionID = Client.Network.SessionID; - move.AgentData.CircuitCode = simulator.CircuitCode; - Client.Network.SendPacket(move, sim); - - Client.Log("Moved to new sim " + sim.ToString(), Helpers.LogLevel.Info); - - if (OnBeginTeleport != null) - { - OnBeginTeleport(TeleportMessage, TeleportStat); - } - else - { - // Sleep a little while so we can collect parcel information - // FIXME: This doesn't belong in libsecondlife - System.Threading.Thread.Sleep(1000); - } - } - else - { - TeleportMessage = "Failed to connect to the new sim after a teleport"; - TeleportStat = TeleportStatus.Failed; - - Client.Log(TeleportMessage, Helpers.LogLevel.Warning); - - if (OnBeginTeleport != null) - { - OnBeginTeleport(TeleportMessage, TeleportStat); - } - } - - OnBeginTeleport = null; - } - } - - /// - /// - /// - /// - /// - private void TeleportTimerEvent(object source, System.Timers.ElapsedEventArgs ea) - { - TeleportTimeout = true; - } - } } diff --git a/libsecondlife-cs/MainAvatar.cs b/libsecondlife-cs/MainAvatar.cs new file mode 100644 index 00000000..2cfa6bd6 --- /dev/null +++ b/libsecondlife-cs/MainAvatar.cs @@ -0,0 +1,954 @@ +/* + * Copyright (c) 2006, Second Life Reverse Engineering Team + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the Second Life Reverse Engineering Team nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Timers; +using System.Net; +using System.Collections.Generic; +using libsecondlife.Packets; + +namespace libsecondlife +{ + + /// + /// Class to hold Client Avatar's data + /// + public class MainAvatar + { + /// Callback for incoming chat packets + public event ChatCallback OnChat; + /// Callback for incoming IMs + public event InstantMessageCallback OnInstantMessage; + /// Callback for Teleport request update + public event TeleportCallback OnTeleport; + /// Callback for incoming change in L$ balance + public event BalanceCallback OnBalanceUpdated; + + /// Your (client) Avatar UUID, asset server + public LLUUID ID = LLUUID.Zero; + /// Your (client) Avatar ID, local to Region/sim + public uint LocalID; + /// Avatar First Name (i.e. Philip) + public string FirstName = ""; + /// Avatar Last Name (i.e. Linden) + public string LastName = ""; + /// + public string TeleportMessage; + /// Current position of avatar + public LLVector3 Position = LLVector3.Zero; + /// Current rotation of avatar + public LLQuaternion Rotation = LLQuaternion.Identity; + /// The point the avatar is currently looking at + /// (may not stay updated) + public LLVector3 LookAt = LLVector3.Zero; + /// Position avatar client will goto when login to 'home' or during + /// teleport request to 'home' region. + public LLVector3 HomePosition = LLVector3.Zero; + /// LookAt point saved/restored with HomePosition + public LLVector3 HomeLookAt = LLVector3.Zero; + /// Gets the health of the agent + protected float health; + public float Health + { + get { return health; } + } + /// Gets the current balance of the agent + protected int balance; + public int Balance + { + get { return balance; } + } + + private SecondLife Client; + private TeleportCallback OnBeginTeleport; + private TeleportStatus TeleportStat; + private Timer TeleportTimer; + private bool TeleportTimeout; + private uint HeightWidthGenCounter; + + /// + /// Constructor, aka 'CallBack Central' - Setup callbacks for packets related to our avatar + /// + /// + public MainAvatar(SecondLife client) + { + NetworkManager.PacketCallback callback; + Client = client; + TeleportMessage = ""; + + // Coarse location callback + Client.Network.RegisterCallback(PacketType.CoarseLocationUpdate, new NetworkManager.PacketCallback(CoarseLocationHandler)); + + // Teleport callbacks + callback = new NetworkManager.PacketCallback(TeleportHandler); + Client.Network.RegisterCallback(PacketType.TeleportStart, callback); + Client.Network.RegisterCallback(PacketType.TeleportProgress, callback); + Client.Network.RegisterCallback(PacketType.TeleportFailed, callback); + Client.Network.RegisterCallback(PacketType.TeleportFinish, callback); + + // Instant Message callback + Client.Network.RegisterCallback(PacketType.ImprovedInstantMessage, new NetworkManager.PacketCallback(InstantMessageHandler)); + + // Chat callback + Client.Network.RegisterCallback(PacketType.ChatFromSimulator, new NetworkManager.PacketCallback(ChatHandler)); + + TeleportTimer = new Timer(18000); + TeleportTimer.Elapsed += new ElapsedEventHandler(TeleportTimerEvent); + TeleportTimeout = false; + + // Movement complete callback + Client.Network.RegisterCallback(PacketType.AgentMovementComplete, new NetworkManager.PacketCallback(MovementCompleteHandler)); + + // Health callback + Client.Network.RegisterCallback(PacketType.HealthMessage, new NetworkManager.PacketCallback(HealthHandler)); + + // Money callbacks + callback = new NetworkManager.PacketCallback(BalanceHandler); + Client.Network.RegisterCallback(PacketType.MoneyBalanceReply, callback); + Client.Network.RegisterCallback(PacketType.MoneySummaryReply, callback); + Client.Network.RegisterCallback(PacketType.AdjustBalance, callback); + } + + /// + /// + /// + /// + /// + public void InstantMessage(LLUUID target, string message) + { + InstantMessage(FirstName + " " + LastName, LLUUID.Random(), target, message, null, LLUUID.Random()); + } + + /// + /// + /// + /// + /// + /// + public void InstantMessage(LLUUID target, string message, LLUUID IMSessionID) + { + InstantMessage(FirstName + " " + LastName, LLUUID.Random(), target, message, null, IMSessionID); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, LLUUID[] conferenceIDs) + { + InstantMessage(fromName, sessionID, target, message, conferenceIDs, LLUUID.Random()); + } + + /// + /// Generate an Instant Message (Full Arguments). + /// + /// Client's Avatar + /// SessionID of current connection to grid + /// UUID of target Av. + /// Text Message being sent. + /// + /// + /// + /// TODO: Have fromName grabbed from elsewhere and remove argument, to prevent inadvertant spoofing. + /// + public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, + LLUUID[] conferenceIDs, LLUUID IMSessionID) + { + ImprovedInstantMessagePacket im = new ImprovedInstantMessagePacket(); + im.AgentData.AgentID = this.ID; + im.AgentData.SessionID = Client.Network.SessionID; + im.MessageBlock.Dialog = 0; + im.MessageBlock.FromAgentName = Helpers.StringToField(fromName); + im.MessageBlock.FromGroup = false; + im.MessageBlock.ID = IMSessionID; + im.MessageBlock.Message = Helpers.StringToField(message); + im.MessageBlock.Offline = 1; + im.MessageBlock.ToAgentID = target; + if (conferenceIDs != null && conferenceIDs.Length > 0) + { + im.MessageBlock.BinaryBucket = new byte[16 * conferenceIDs.Length]; + + for (int i = 0; i < conferenceIDs.Length; ++i) + { + Array.Copy(conferenceIDs[i].Data, 0, im.MessageBlock.BinaryBucket, i * 16, 16); + } + } + else + { + im.MessageBlock.BinaryBucket = new byte[0]; + } + + // These fields are mandatory, even if we don't have valid values for them + im.MessageBlock.Position = LLVector3.Zero; + //TODO: Allow region id to be correctly set by caller or fetched from Client.* + im.MessageBlock.RegionID = LLUUID.Zero; + + + // Send the message + Client.Network.SendPacket((Packet)im); + } + + /// + /// Conversion type to denote Chat Packet types in an easier-to-understand format + /// + public enum ChatType + { + /// Whispers (5m radius) + Whisper = 0, + /// Normal chat (10/20m radius) - Why is this here twice? + Normal = 1, + /// Shouting! (100m radius) + Shout = 2, + /// Normal chat (10/20m radius) - Why is this here twice? + Say = 3, + /// Event message when an Avatar has begun to type + StartTyping = 4, + /// Event message when an Avatar has stopped typing + StopTyping = 5 + } + + /// + /// Send a Chat message. + /// + /// The Message you're sending out. + /// Channel number (0 would be default 'Say' message, other numbers + /// denote the equivalent of /# in normal client). + /// Chat Type, see above. + public void Chat(string message, int channel, ChatType type) + { + ChatFromViewerPacket chat = new ChatFromViewerPacket(); + chat.AgentData.AgentID = this.ID; + chat.AgentData.SessionID = Client.Network.SessionID; + chat.ChatData.Channel = channel; + chat.ChatData.Message = Helpers.StringToField(message); + chat.ChatData.Type = (byte)type; + + Client.Network.SendPacket((Packet)chat); + } + + /// + /// Set the height and the width of your avatar. This is used to scale + /// the avatar mesh. + /// + /// New height of the avatar + /// New width of the avatar + public void SetHeightWidth(ushort height, ushort width) + { + AgentHeightWidthPacket heightwidth = new AgentHeightWidthPacket(); + heightwidth.AgentData.AgentID = Client.Network.AgentID; + heightwidth.AgentData.SessionID = Client.Network.SessionID; + heightwidth.AgentData.CircuitCode = Client.Network.CurrentSim.CircuitCode; + heightwidth.HeightWidthBlock.Height = height; + heightwidth.HeightWidthBlock.Width = width; + heightwidth.HeightWidthBlock.GenCounter = HeightWidthGenCounter++; + + Client.Network.SendPacket((Packet)heightwidth); + } + + /// + /// Sends a request to sit on the specified object + /// + /// LLUUID of the object to sit on + /// Sit at offset + public void RequestSit(LLUUID targetID, LLVector3 offset) + { + AgentRequestSitPacket requestSit = new AgentRequestSitPacket(); + requestSit.AgentData.AgentID = Client.Network.AgentID; + requestSit.AgentData.SessionID = Client.Network.SessionID; + requestSit.TargetObject.TargetID = targetID; + requestSit.TargetObject.Offset = offset; + Client.Network.SendPacket(requestSit); + } + + /// + /// Follows a call to RequestSit() to actually sit on the object + /// + public void Sit() + { + AgentSitPacket sit = new AgentSitPacket(); + sit.AgentData.AgentID = Client.Network.AgentID; + sit.AgentData.SessionID = Client.Network.SessionID; + Client.Network.SendPacket(sit); + } + + /// + /// Give Money to destination Avatar + /// + /// UUID of the Target Avatar + /// Amount in L$ + /// Reason (optional normally) + public void GiveMoney(LLUUID target, int amount, string description) + { + // 5001 - transaction type for av to av money transfers + + GiveMoney(target, amount, description, 5001); + } + + /// + /// Toggle running on or off + /// + public void SetAlwaysRun(bool running) + { + SetAlwaysRunPacket run = new SetAlwaysRunPacket(); + run.AgentData.AgentID = Client.Network.AgentID; + run.AgentData.SessionID = Client.Network.SessionID; + run.AgentData.AlwaysRun = running; + Client.Network.SendPacket(run); + } + + /// + /// Give Money to destionation Object or Avatar + /// + /// UUID of the Target Object/Avatar + /// Amount in L$ + /// Reason (Optional normally) + /// The type of transaction. Currently only 5001 is + /// documented for Av->Av money transfers. + public void GiveMoney(LLUUID target, int amount, string description, int transactiontype) + { + MoneyTransferRequestPacket money = new MoneyTransferRequestPacket(); + money.AgentData.AgentID = this.ID; + money.AgentData.SessionID = Client.Network.SessionID; + money.MoneyData.Description = Helpers.StringToField(description); + money.MoneyData.DestID = target; + money.MoneyData.SourceID = this.ID; + money.MoneyData.TransactionType = transactiontype; + money.MoneyData.AggregatePermInventory = 0; //TODO: whats this? + money.MoneyData.AggregatePermNextOwner = 0; //TODO: whats this? + money.MoneyData.Flags = 0; //TODO: whats this? + money.MoneyData.Amount = amount; + + Client.Network.SendPacket((Packet)money); + } + + /// + /// Send an AgentAnimation packet that will toggle animations on or off + /// + /// A list of animation UUIDs, and whether to + /// turn that animation on or off + public void Animate(Dictionary animations) + { + AgentAnimationPacket animate = new AgentAnimationPacket(); + + animate.AgentData.AgentID = Client.Network.AgentID; + animate.AgentData.SessionID = Client.Network.SessionID; + animate.AnimationList = new AgentAnimationPacket.AnimationListBlock[animations.Count]; + int i = 0; + + foreach (KeyValuePair animation in animations) + { + animate.AnimationList[i] = new AgentAnimationPacket.AnimationListBlock(); + animate.AnimationList[i].AnimID = animation.Key; + animate.AnimationList[i].StartAnim = animation.Value; + + i++; + } + + Client.Network.SendPacket(animate); + } + + /// + /// Use the autopilot sim function to move the avatar to a new position + /// + /// The z value is currently not handled properly by the simulator + /// Integer value for the global X coordinate to move to + /// Integer value for the global Y coordinate to move to + /// Floating-point value for the Z coordinate to move to + /// AutoPilot(252620, 247078, 20.2674); + public void AutoPilot(ulong globalX, ulong globalY, float z) + { + GenericMessagePacket autopilot = new GenericMessagePacket(); + + autopilot.AgentData.AgentID = Client.Network.AgentID; + autopilot.AgentData.SessionID = Client.Network.SessionID; + autopilot.MethodData.Invoice = LLUUID.Zero; + autopilot.MethodData.Method = Helpers.StringToField("autopilot"); + autopilot.ParamList = new GenericMessagePacket.ParamListBlock[3]; + autopilot.ParamList[0] = new GenericMessagePacket.ParamListBlock(); + autopilot.ParamList[0].Parameter = Helpers.StringToField(globalX.ToString()); + autopilot.ParamList[1] = new GenericMessagePacket.ParamListBlock(); + autopilot.ParamList[1].Parameter = Helpers.StringToField(globalY.ToString()); + autopilot.ParamList[2] = new GenericMessagePacket.ParamListBlock(); + // TODO: Do we need to prevent z coordinates from being sent in 1.4827e-18 notation? + autopilot.ParamList[2].Parameter = Helpers.StringToField(z.ToString()); + + Client.Network.SendPacket(autopilot); + } + + /// + /// Use the autopilot sim function to move the avatar to a new position + /// + /// The z value is currently not handled properly by the simulator + /// Integer value for the local X coordinate to move to + /// Integer value for the local Y coordinate to move to + /// Floating-point value for the Z coordinate to move to + /// AutoPilot(252620, 247078, 20.2674); + public void AutoPilotLocal(int localX, int localY, float z) + { + GridRegion gr = Client.Network.CurrentSim.Region.GridRegionData; + ulong GridCornerX = ((ulong)gr.X * (ulong)256) + (ulong)localX; + ulong GridCornerY = ((ulong)gr.Y * (ulong)256) + (ulong)localY; + AutoPilot(GridCornerX, GridCornerY, z); + } + + /// + /// + /// + /// + /// + /// + public void BeginTeleport(ulong regionHandle, LLVector3 position, TeleportCallback tc) + { + BeginTeleport(regionHandle, position, new LLVector3(position.X + 1.0f, position.Y, position.Z), tc); + } + + /// + /// + /// + /// + /// + /// + /// + public void BeginTeleport(ulong regionHandle, LLVector3 position, LLVector3 lookAt, TeleportCallback tc) + { + OnBeginTeleport = tc; + + TeleportLocationRequestPacket teleport = new TeleportLocationRequestPacket(); + teleport.AgentData.AgentID = Client.Network.AgentID; + teleport.AgentData.SessionID = Client.Network.SessionID; + teleport.Info.LookAt = lookAt; + teleport.Info.Position = position; + teleport.Info.RegionHandle = regionHandle; + + Client.Log("Teleporting to region " + regionHandle.ToString(), Helpers.LogLevel.Info); + + Client.Network.SendPacket(teleport); + } + + /// + /// + /// + /// + /// + /// + public bool Teleport(ulong regionHandle, LLVector3 position) + { + return Teleport(regionHandle, position, new LLVector3(position.X + 1.0f, position.Y, position.Z)); + } + + /// + /// + /// + /// + /// + /// + /// + public bool Teleport(ulong regionHandle, LLVector3 position, LLVector3 lookAt) + { + TeleportStat = TeleportStatus.None; + + TeleportLocationRequestPacket teleport = new TeleportLocationRequestPacket(); + teleport.AgentData.AgentID = Client.Network.AgentID; + teleport.AgentData.SessionID = Client.Network.SessionID; + teleport.Info.LookAt = lookAt; + teleport.Info.Position = position; + + teleport.Info.RegionHandle = regionHandle; + + Client.Log("Teleporting to region " + regionHandle.ToString(), Helpers.LogLevel.Info); + + // Start the timeout check + TeleportTimeout = false; + TeleportTimer.Start(); + + Client.Network.SendPacket(teleport); + + while (TeleportStat != TeleportStatus.Failed && TeleportStat != TeleportStatus.Finished && !TeleportTimeout) + { + Client.Tick(); + } + + TeleportTimer.Stop(); + + if (TeleportTimeout) + { + TeleportMessage = "Teleport timed out."; + TeleportStat = TeleportStatus.Failed; + + if (OnTeleport != null) { OnTeleport(TeleportMessage, TeleportStat); } + } + else + { + if (OnTeleport != null) { OnTeleport(TeleportMessage, TeleportStat); } + } + + return (TeleportStat == TeleportStatus.Finished); + } + + /// + /// + /// + /// + /// + /// + public bool Teleport(string simName, LLVector3 position) + { + //position.Z = 0; //why was this here? + return Teleport(simName, position, new LLVector3(0, 1.0F, 0)); + } + + /// + /// + /// + /// + /// + /// + /// + public bool Teleport(string simName, LLVector3 position, LLVector3 lookAt) + { + int attempts = 0; + TeleportStat = TeleportStatus.None; + + simName = simName.ToLower(); + + GridRegion region = Client.Grid.GetGridRegion(simName); + + if (region != null) + { + return Teleport(region.RegionHandle, position, lookAt); + } + else + { + while (attempts++ < 5) + { + region = Client.Grid.GetGridRegion(simName); + + if (region != null) + { + return Teleport(region.RegionHandle, position, lookAt); + } + else + { + // Request the region info again + Client.Grid.AddSim(simName); + + System.Threading.Thread.Sleep(1000); + } + } + } + + if (OnTeleport != null) + { + TeleportMessage = "Unable to resolve name: " + simName; + TeleportStat = TeleportStatus.Failed; + OnTeleport(TeleportMessage, TeleportStat); + } + + return false; + } + + /// + /// Respond to a teleport lure by either accepting it and initiating + /// the teleport, or denying it + /// + /// UUID of the avatar requesting the teleport + /// Accept the teleport request or deny it + public void TeleportLureRespond(LLUUID requesterID, bool accept) + { + ImprovedInstantMessagePacket im = new ImprovedInstantMessagePacket(); + + im.AgentData.AgentID = Client.Network.AgentID; + im.AgentData.SessionID = Client.Network.SessionID; + im.MessageBlock.BinaryBucket = new byte[0]; + im.MessageBlock.FromAgentName = Helpers.StringToField(this.FirstName + " " + this.LastName); + im.MessageBlock.FromGroup = false; + im.MessageBlock.ID = Client.Network.AgentID; + im.MessageBlock.Message = new byte[0]; + im.MessageBlock.Offline = 0; + im.MessageBlock.ParentEstateID = 0; + im.MessageBlock.Position = this.Position; + im.MessageBlock.RegionID = LLUUID.Zero; + im.MessageBlock.Timestamp = 0; + im.MessageBlock.ToAgentID = requesterID; + + if (accept) + { + im.MessageBlock.Dialog = (byte)InstantMessageDialog.AcceptTeleport; + + Client.Network.SendPacket(im); + + TeleportLureRequestPacket lure = new TeleportLureRequestPacket(); + + lure.Info.AgentID = Client.Network.AgentID; + lure.Info.SessionID = Client.Network.SessionID; + lure.Info.LureID = Client.Network.AgentID; + lure.Info.TeleportFlags = 4; // TODO: What does this mean? + + Client.Network.SendPacket(lure); + } + else + { + im.MessageBlock.Dialog = (byte)InstantMessageDialog.DenyTeleport; + + Client.Network.SendPacket(im); + } + } + + /// + /// Grabs an object + /// + public void Grab(uint objectLocalID) + { + ObjectGrabPacket grab = new ObjectGrabPacket(); + grab.AgentData.AgentID = Client.Network.AgentID; + grab.AgentData.SessionID = Client.Network.SessionID; + grab.ObjectData.LocalID = objectLocalID; + grab.ObjectData.GrabOffset = new LLVector3(0, 0, 0); + Client.Network.SendPacket(grab); + } + + /// + /// Drags on an object + /// + /// Strangely, LLUID instead of local ID + /// Drag target in region coordinates + public void GrabUpdate(LLUUID objectID, LLVector3 grabPosition) + { + ObjectGrabUpdatePacket grab = new ObjectGrabUpdatePacket(); + grab.AgentData.AgentID = Client.Network.AgentID; + grab.AgentData.SessionID = Client.Network.SessionID; + grab.ObjectData.ObjectID = objectID; + grab.ObjectData.GrabOffsetInitial = new LLVector3(0, 0, 0); + grab.ObjectData.GrabPosition = grabPosition; + grab.ObjectData.TimeSinceLast = 0; + Client.Network.SendPacket(grab); + } + + /// + /// Releases a grabbed object + /// + public void DeGrab(uint objectLocalID) + { + ObjectDeGrabPacket degrab = new ObjectDeGrabPacket(); + degrab.AgentData.AgentID = Client.Network.AgentID; + degrab.AgentData.SessionID = Client.Network.SessionID; + degrab.ObjectData.LocalID = objectLocalID; + Client.Network.SendPacket(degrab); + } + + /// + /// Touches an object + /// + public void Touch(uint objectLocalID) + { + Client.Self.Grab(objectLocalID); + Client.Self.DeGrab(objectLocalID); + } + + /// + /// + /// + /// + public void CompleteAgentMovement(Simulator simulator) + { + CompleteAgentMovementPacket move = new CompleteAgentMovementPacket(); + + move.AgentData.AgentID = Client.Network.AgentID; + move.AgentData.SessionID = Client.Network.SessionID; + move.AgentData.CircuitCode = simulator.CircuitCode; + + Client.Network.SendPacket(move, simulator); + } + + /// + /// + /// + /// + public void UpdateCamera(Avatar.AgentUpdateFlags controlFlags, LLVector3 position, LLVector3 forwardAxis, + LLVector3 leftAxis, LLVector3 upAxis, LLQuaternion bodyRotation, LLQuaternion headRotation, float farClip, + bool reliable) + { + AgentUpdatePacket update = new AgentUpdatePacket(); + update.AgentData.AgentID = Client.Network.AgentID; + update.AgentData.SessionID = Client.Network.SessionID; + update.AgentData.State = 0; + // Semi-sane default values + update.AgentData.BodyRotation = bodyRotation; //new LLQuaternion(0, 0.6519076f, 0, 0); + update.AgentData.HeadRotation = headRotation; //LLQuaternion.Identity; + update.AgentData.CameraCenter = position; //new LLVector3(9.549901f, 7.033957f, 11.75f); + update.AgentData.CameraAtAxis = forwardAxis; //new LLVector3(0.7f, 0.7f, 0); + update.AgentData.CameraLeftAxis = leftAxis; //new LLVector3(-0.7f, 0.7f, 0); + update.AgentData.CameraUpAxis = upAxis; //new LLVector3(0.1822026f, 0.9828722f, 0); + update.AgentData.Far = farClip; + update.AgentData.ControlFlags = (uint)controlFlags; + update.AgentData.Flags = 0; + update.Header.Reliable = reliable; + + Client.Network.SendPacket(update); + + // Send an AgentFOV packet widening our field of vision + /*AgentFOVPacket fovPacket = new AgentFOVPacket(); + fovPacket.AgentData.AgentID = this.ID; + fovPacket.AgentData.SessionID = Client.Network.SessionID; + fovPacket.AgentData.CircuitCode = simulator.CircuitCode; + fovPacket.FOVBlock.GenCounter = 0; + fovPacket.FOVBlock.VerticalAngle = 6.28318531f; + fovPacket.Header.Reliable = true; + Client.Network.SendPacket((Packet)fovPacket);*/ + } + + /// + /// [UNUSED - for now] + /// + /// + /// + private void CoarseLocationHandler(Packet packet, Simulator simulator) + { + // TODO: This will be useful one day + } + + /// + /// Take an incoming ImprovedInstantMessage packet, auto-parse, and if + /// OnInstantMessage is defined call that with the appropriate arguments. + /// + /// Incoming ImprovedInstantMessagePacket + /// [UNUSED] + private void InstantMessageHandler(Packet packet, Simulator simulator) + { + if (packet.Type == PacketType.ImprovedInstantMessage) + { + ImprovedInstantMessagePacket im = (ImprovedInstantMessagePacket)packet; + + if (OnInstantMessage != null) + { + OnInstantMessage( + im.AgentData.AgentID + , Helpers.FieldToString(im.MessageBlock.FromAgentName), + im.MessageBlock.ToAgentID + , im.MessageBlock.ParentEstateID + , im.MessageBlock.RegionID + , im.MessageBlock.Position + , im.MessageBlock.Dialog + , im.MessageBlock.FromGroup + , im.MessageBlock.ID + , new DateTime(im.MessageBlock.Timestamp) + , Helpers.FieldToString(im.MessageBlock.Message) + , im.MessageBlock.Offline + , im.MessageBlock.BinaryBucket + ); + } + } + } + + /// + /// Take an incoming Chat packet, auto-parse, and if OnChat is defined call + /// that with the appropriate arguments. + /// + /// Incoming ChatFromSimulatorPacket + /// [UNUSED] + private void ChatHandler(Packet packet, Simulator simulator) + { + if (packet.Type == PacketType.ChatFromSimulator) + { + ChatFromSimulatorPacket chat = (ChatFromSimulatorPacket)packet; + + if (OnChat != null) + { + OnChat(Helpers.FieldToString(chat.ChatData.Message) + , chat.ChatData.Audible + , chat.ChatData.ChatType + , chat.ChatData.SourceType + , Helpers.FieldToString(chat.ChatData.FromName) + , chat.ChatData.SourceID + , chat.ChatData.OwnerID + , chat.ChatData.Position + ); + } + } + } + + /// + /// Update client's Position and LookAt from incoming packet + /// + /// Incoming AgentMovementCompletePacket + /// [UNUSED] + private void MovementCompleteHandler(Packet packet, Simulator simulator) + { + AgentMovementCompletePacket movement = (AgentMovementCompletePacket)packet; + + this.Position = movement.Data.Position; + this.LookAt = movement.Data.LookAt; + } + + /// + /// Update Client Avatar's health via incoming packet + /// + /// Incoming HealthMessagePacket + /// [UNUSED] + private void HealthHandler(Packet packet, Simulator simulator) + { + health = ((HealthMessagePacket)packet).HealthData.Health; + } + + /// + /// Update Client Avatar's L$ balance from incoming packet + /// + /// Incoming MoneyBalanceReplyPacket + /// [UNUSED] + private void BalanceHandler(Packet packet, Simulator simulator) + { + if (packet.Type == PacketType.MoneyBalanceReply) + { + balance = ((MoneyBalanceReplyPacket)packet).MoneyData.MoneyBalance; + } + else if (packet.Type == PacketType.MoneySummaryReply) + { + balance = ((MoneySummaryReplyPacket)packet).MoneyData.Balance; + } + else if (packet.Type == PacketType.AdjustBalance) + { + balance += ((AdjustBalancePacket)packet).AgentData.Delta; + } + + if (OnBalanceUpdated != null) + { + OnBalanceUpdated(balance); + } + } + + /// + /// + /// + /// + /// + private void TeleportHandler(Packet packet, Simulator simulator) + { + if (packet.Type == PacketType.TeleportStart) + { + Client.DebugLog("TeleportStart received from " + simulator.ToString()); + + TeleportMessage = "Teleport started"; + TeleportStat = TeleportStatus.Start; + + if (OnBeginTeleport != null) + { + OnBeginTeleport(TeleportMessage, TeleportStat); + } + } + else if (packet.Type == PacketType.TeleportProgress) + { + Client.DebugLog("TeleportProgress received from " + simulator.ToString()); + + TeleportMessage = Helpers.FieldToString(((TeleportProgressPacket)packet).Info.Message); + TeleportStat = TeleportStatus.Progress; + + if (OnBeginTeleport != null) + { + OnBeginTeleport(TeleportMessage, TeleportStat); + } + } + else if (packet.Type == PacketType.TeleportFailed) + { + Client.DebugLog("TeleportFailed received from " + simulator.ToString()); + + TeleportMessage = Helpers.FieldToString(((TeleportFailedPacket)packet).Info.Reason); + TeleportStat = TeleportStatus.Failed; + + if (OnBeginTeleport != null) + { + OnBeginTeleport(TeleportMessage, TeleportStat); + } + + OnBeginTeleport = null; + } + else if (packet.Type == PacketType.TeleportFinish) + { + Client.DebugLog("TeleportFinish received from " + simulator.ToString()); + + TeleportFinishPacket finish = (TeleportFinishPacket)packet; + + // Connect to the new sim + Simulator sim = Client.Network.Connect(new IPAddress((long)finish.Info.SimIP), finish.Info.SimPort, + simulator.CircuitCode, true); + + if (sim != null) + { + TeleportMessage = "Teleport finished"; + TeleportStat = TeleportStatus.Finished; + + // Move the avatar in to the new sim + CompleteAgentMovementPacket move = new CompleteAgentMovementPacket(); + move.AgentData.AgentID = Client.Network.AgentID; + move.AgentData.SessionID = Client.Network.SessionID; + move.AgentData.CircuitCode = simulator.CircuitCode; + Client.Network.SendPacket(move, sim); + + Client.Log("Moved to new sim " + sim.ToString(), Helpers.LogLevel.Info); + + if (OnBeginTeleport != null) + { + OnBeginTeleport(TeleportMessage, TeleportStat); + } + else + { + // Sleep a little while so we can collect parcel information + // FIXME: This doesn't belong in libsecondlife + System.Threading.Thread.Sleep(1000); + } + } + else + { + TeleportMessage = "Failed to connect to the new sim after a teleport"; + TeleportStat = TeleportStatus.Failed; + + Client.Log(TeleportMessage, Helpers.LogLevel.Warning); + + if (OnBeginTeleport != null) + { + OnBeginTeleport(TeleportMessage, TeleportStat); + } + } + + OnBeginTeleport = null; + } + } + + /// + /// + /// + /// + /// + private void TeleportTimerEvent(object source, System.Timers.ElapsedEventArgs ea) + { + TeleportTimeout = true; + } + } + +} diff --git a/libsecondlife-cs/libsecondlife.csproj b/libsecondlife-cs/libsecondlife.csproj index 72e6788d..728a1b25 100644 --- a/libsecondlife-cs/libsecondlife.csproj +++ b/libsecondlife-cs/libsecondlife.csproj @@ -107,6 +107,7 @@ Code + Code