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