/*
* Copyright (c) 2006-2008, openmetaverse.org
* 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 openmetaverse.org 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.Net;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using System.Reflection;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Capabilities;
using OpenMetaverse.Packets;
namespace OpenMetaverse
{
#region Enums
///
/// Permission request flags, asked when a script wants to control an Avatar
///
[Flags]
public enum ScriptPermission : int
{
/// Placeholder for empty values, shouldn't ever see this
None = 0,
/// Script wants ability to take money from you
Debit = 1 << 1,
/// Script wants to take camera controls for you
TakeControls = 1 << 2,
/// Script wants to remap avatars controls
RemapControls = 1 << 3,
/// Script wants to trigger avatar animations
/// This function is not implemented on the grid
TriggerAnimation = 1 << 4,
/// Script wants to attach or detach the prim or primset to your avatar
Attach = 1 << 5,
/// Script wants permission to release ownership
/// This function is not implemented on the grid
/// The concept of "public" objects does not exist anymore.
ReleaseOwnership = 1 << 6,
/// Script wants ability to link/delink with other prims
ChangeLinks = 1 << 7,
/// Script wants permission to change joints
/// This function is not implemented on the grid
ChangeJoints = 1 << 8,
/// Script wants permissions to change permissions
/// This function is not implemented on the grid
ChangePermissions = 1 << 9,
/// Script wants to track avatars camera position and rotation
TrackCamera = 1 << 10,
/// Script wants to control your camera
ControlCamera = 1 << 11
}
///
/// Special commands used in Instant Messages
///
public enum InstantMessageDialog : byte
{
/// Indicates a regular IM from another agent
MessageFromAgent = 0,
/// Simple notification box with an OK button
MessageBox = 1,
// Used to show a countdown notification with an OK
// button, deprecated now
//[Obsolete]
//MessageBoxCountdown = 2,
/// You've been invited to join a group.
GroupInvitation = 3,
/// Inventory offer
InventoryOffered = 4,
/// Accepted inventory offer
InventoryAccepted = 5,
/// Declined inventory offer
InventoryDeclined = 6,
/// Group vote
GroupVote = 7,
// A message to everyone in the agent's group, no longer
// used
//[Obsolete]
//DeprecatedGroupMessage = 8,
/// An object is offering its inventory
TaskInventoryOffered = 9,
/// Accept an inventory offer from an object
TaskInventoryAccepted = 10,
/// Decline an inventory offer from an object
TaskInventoryDeclined = 11,
/// Unknown
NewUserDefault = 12,
/// Start a session, or add users to a session
SessionAdd = 13,
/// Start a session, but don't prune offline users
SessionOfflineAdd = 14,
/// Start a session with your group
SessionGroupStart = 15,
/// Start a session without a calling card (finder or objects)
SessionCardlessStart = 16,
/// Send a message to a session
SessionSend = 17,
/// Leave a session
SessionDrop = 18,
/// Indicates that the IM is from an object
MessageFromObject = 19,
/// Sent an IM to a busy user, this is the auto response
BusyAutoResponse = 20,
/// Shows the message in the console and chat history
ConsoleAndChatHistory = 21,
/// Send a teleport lure
RequestTeleport = 22,
/// Response sent to the agent which inititiated a teleport invitation
AcceptTeleport = 23,
/// Response sent to the agent which inititiated a teleport invitation
DenyTeleport = 24,
/// Only useful if you have Linden permissions
GodLikeRequestTeleport = 25,
/// A placeholder type for future expansion, currently not
/// used
CurrentlyUnused = 26,
// Notification of a new group election, this is
// deprecated
//[Obsolete]
//DeprecatedGroupElection = 27,
/// IM to tell the user to go to an URL
GotoUrl = 28,
/// IM for help
Session911Start = 29,
/// IM sent automatically on call for help, sends a lure
/// to each Helper reached
Lure911 = 30,
/// Like an IM but won't go to email
FromTaskAsAlert = 31,
/// IM from a group officer to all group members
GroupNotice = 32,
/// Unknown
GroupNoticeInventoryAccepted = 33,
/// Unknown
GroupNoticeInventoryDeclined = 34,
/// Accept a group invitation
GroupInvitationAccept = 35,
/// Decline a group invitation
GroupInvitationDecline = 36,
/// Unknown
GroupNoticeRequested = 37,
/// An avatar is offering you friendship
FriendshipOffered = 38,
/// An avatar has accepted your friendship offer
FriendshipAccepted = 39,
/// An avatar has declined your friendship offer
FriendshipDeclined = 40,
/// Indicates that a user has started typing
StartTyping = 41,
/// Indicates that a user has stopped typing
StopTyping = 42
}
///
/// Flag in Instant Messages, whether the IM should be delivered to
/// offline avatars as well
///
public enum InstantMessageOnline
{
/// Only deliver to online avatars
Online = 0,
/// If the avatar is offline the message will be held until
/// they login next, and possibly forwarded to their e-mail account
Offline = 1
}
///
/// Conversion type to denote Chat Packet types in an easier-to-understand format
///
public enum ChatType : byte
{
/// Whisper (5m radius)
Whisper = 0,
/// Normal chat (10/20m radius), what the official viewer typically sends
Normal = 1,
/// Shouting! (100m radius)
Shout = 2,
// Say chat (10/20m radius) - The official viewer will
// print "[4:15] You say, hey" instead of "[4:15] You: hey"
//[Obsolete]
//Say = 3,
/// Event message when an Avatar has begun to type
StartTyping = 4,
/// Event message when an Avatar has stopped typing
StopTyping = 5,
/// Unknown
Debug = 6,
/// Event message when an object uses llOwnerSay
OwnerSay = 8
}
///
/// Identifies the source of a chat message
///
public enum ChatSourceType : byte
{
/// Chat from the grid or simulator
System = 0,
/// Chat from another avatar
Agent = 1,
/// Chat from an object
Object = 2
}
///
///
///
public enum ChatAudibleLevel : sbyte
{
///
Not = -1,
///
Barely = 0,
///
Fully = 1
}
///
/// Effect type used in ViewerEffect packets
///
public enum EffectType : byte
{
///
Text = 0,
///
Icon,
///
Connector,
///
FlexibleObject,
///
AnimalControls,
///
AnimationObject,
///
Cloth,
/// Project a beam from a source to a destination, such as
/// the one used when editing an object
Beam,
///
Glow,
///
Point,
///
Trail,
/// Create a swirl of particles around an object
Sphere,
///
Spiral,
///
Edit,
/// Cause an avatar to look at an object
LookAt,
/// Cause an avatar to point at an object
PointAt
}
///
/// The action an avatar is doing when looking at something, used in
/// ViewerEffect packets for the LookAt effect
///
public enum LookAtType : byte
{
///
None,
///
Idle,
///
AutoListen,
///
FreeLook,
///
Respond,
///
Hover,
/// Deprecated
[Obsolete]
Conversation,
///
Select,
///
Focus,
///
Mouselook,
///
Clear
}
///
/// The action an avatar is doing when pointing at something, used in
/// ViewerEffect packets for the PointAt effect
///
public enum PointAtType : byte
{
///
None,
///
Select,
///
Grab,
///
Clear
}
///
/// Money transaction types
///
public enum MoneyTransactionType : int
{
///
None = 0,
///
FailSimulatorTimeout = 1,
///
FailDataserverTimeout = 2,
///
ObjectClaim = 1000,
///
LandClaim = 1001,
///
GroupCreate = 1002,
///
ObjectPublicClaim = 1003,
///
GroupJoin = 1004,
///
TeleportCharge = 1100,
///
UploadCharge = 1101,
///
LandAuction = 1102,
///
ClassifiedCharge = 1103,
///
ObjectTax = 2000,
///
LandTax = 2001,
///
LightTax = 2002,
///
ParcelDirFee = 2003,
///
GroupTax = 2004,
///
ClassifiedRenew = 2005,
///
GiveInventory = 3000,
///
ObjectSale = 5000,
///
Gift = 5001,
///
LandSale = 5002,
///
ReferBonus = 5003,
///
InventorySale = 5004,
///
RefundPurchase = 5005,
///
LandPassSale = 5006,
///
DwellBonus = 5007,
///
PayObject = 5008,
///
ObjectPays = 5009,
///
GroupLandDeed = 6001,
///
GroupObjectDeed = 6002,
///
GroupLiability = 6003,
///
GroupDividend = 6004,
///
GroupMembershipDues = 6005,
///
ObjectRelease = 8000,
///
LandRelease = 8001,
///
ObjectDelete = 8002,
///
ObjectPublicDecay = 8003,
///
ObjectPublicDelete = 8004,
///
LindenAdjustment = 9000,
///
LindenGrant = 9001,
///
LindenPenalty = 9002,
///
EventFee = 9003,
///
EventPrize = 9004,
///
StipendBasic = 10000,
///
StipendDeveloper = 10001,
///
StipendAlways = 10002,
///
StipendDaily = 10003,
///
StipendRating = 10004,
///
StipendDelta = 10005
}
///
///
///
[Flags]
public enum TransactionFlags : byte
{
///
None = 0,
///
SourceGroup = 1,
///
DestGroup = 2,
///
OwnerGroup = 4,
///
SimultaneousContribution = 8,
///
ContributionRemoval = 16
}
///
///
///
public enum MeanCollisionType : byte
{
///
None,
///
Bump,
///
LLPushObject,
///
SelectedObjectCollide,
///
ScriptedObjectCollide,
///
PhysicalObjectCollide
}
///
/// Flags sent when a script takes or releases a control
///
/// NOTE: (need to verify) These might be a subset of the ControlFlags enum in Movement,
[Flags]
public enum ScriptControlChange : uint
{
/// No Flags set
None = 0,
/// Forward (W or up Arrow)
Forward = 1,
/// Back (S or down arrow)
Back = 2,
/// Move left (shift+A or left arrow)
Left = 4,
/// Move right (shift+D or right arrow)
Right = 8,
/// Up (E or PgUp)
Up = 16,
/// Down (C or PgDown
Down = 32,
/// Rotate left (A or left arrow)
RotateLeft = 256,
/// Rotate right (D or right arrow)
RotateRight = 512,
/// Left Mouse Button
LeftButton = 268435456,
/// Left Mouse button in MouseLook
MouseLookLeftButton = 1073741824
}
#endregion Enums
#region Structs
///
/// Instant Message
///
public struct InstantMessage
{
/// Key of sender
public UUID FromAgentID;
/// Name of sender
public string FromAgentName;
/// Key of destination avatar
public UUID ToAgentID;
/// ID of originating estate
public uint ParentEstateID;
/// Key of originating region
public UUID RegionID;
/// Coordinates in originating region
public Vector3 Position;
/// Instant message type
public InstantMessageDialog Dialog;
/// Group IM session toggle
public bool GroupIM;
/// Key of IM session, for Group Messages, the groups UUID
public UUID IMSessionID;
/// Timestamp of the instant message
public DateTime Timestamp;
/// Instant message text
public string Message;
/// Whether this message is held for offline avatars
public InstantMessageOnline Offline;
/// Context specific packed data
public byte[] BinaryBucket;
//Print the contents of a message
public override string ToString(){
string result="";
Type imType = this.GetType();
FieldInfo[] fields = imType.GetFields();
foreach (FieldInfo field in fields){
result += (field.Name + " = " + field.GetValue(this) + " ");
}
return result;
}
}
#endregion Structs
///
/// Manager class for our own avatar
///
public partial class AgentManager
{
#region Enums
///
/// Currently only used to hide your group title
///
[Flags]
public enum AgentFlags : byte
{
/// No flags set
None = 0,
/// Hide your group title
HideTitle = 0x01,
}
///
/// Action state of the avatar, which can currently be typing and
/// editing
///
[Flags]
public enum AgentState : byte
{
///
None = 0x00,
///
Typing = 0x04,
///
Editing = 0x10
}
///
/// Current teleport status
///
public enum TeleportStatus
{
/// Unknown status
None,
/// Teleport initialized
Start,
/// Teleport in progress
Progress,
/// Teleport failed
Failed,
/// Teleport completed
Finished,
/// Teleport cancelled
Cancelled
}
///
///
///
[Flags]
public enum TeleportFlags : uint
{
/// No flags set, or teleport failed
Default = 0,
/// Set when newbie leaves help island for first time
SetHomeToTarget = 1 << 0,
///
SetLastToTarget = 1 << 1,
/// Via Lure
ViaLure = 1 << 2,
/// Via Landmark
ViaLandmark = 1 << 3,
/// Via Location
ViaLocation = 1 << 4,
/// Via Home
ViaHome = 1 << 5,
/// Via Telehub
ViaTelehub = 1 << 6,
/// Via Login
ViaLogin = 1 << 7,
/// Linden Summoned
ViaGodlikeLure = 1 << 8,
/// Linden Forced me
Godlike = 1 << 9,
///
NineOneOne = 1 << 10,
/// Agent Teleported Home via Script
DisableCancel = 1 << 11,
///
ViaRegionID = 1 << 12,
///
IsFlying = 1 << 13,
///
ResetHome = 1 << 14,
/// forced to new location for example when avatar is banned or ejected
ForceRedirect = 1 << 15,
/// Teleport Finished via a Lure
FinishedViaLure = 1 << 26,
/// Finished, Sim Changed
FinishedViaNewSim = 1 << 28,
/// Finished, Same Sim
FinishedViaSameSim = 1 << 29
}
///
///
///
[Flags]
public enum TeleportLureFlags
{
///
NormalLure = 0,
///
GodlikeLure = 1,
///
GodlikePursuit = 2
}
///
///
///
[Flags]
public enum ScriptSensorTypeFlags
{
///
Agent = 1,
///
Active = 2,
///
Passive = 4,
///
Scripted = 8,
}
#endregion Enums
#region Callbacks
///
/// Triggered on incoming chat messages
///
/// Text of chat message
/// Audible level of this chat message
/// Type of chat (whisper, shout, status, etc.)
/// Source of the chat message
/// Name of the sending object
/// Key of source
/// Key of the sender
/// Senders position
public delegate void ChatCallback(string message, ChatAudibleLevel audible, ChatType type,
ChatSourceType sourceType, string fromName, UUID id, UUID ownerid, Vector3 position);
///
/// Triggered when a script pops up a dialog box
///
/// The dialog box message
/// Name of the object that sent the dialog
/// Image to be displayed in the dialog
/// ID of the object that sent the dialog
/// First name of the object owner
/// Last name of the object owner
/// Chat channel that the object is communicating on
/// List of button labels
public delegate void ScriptDialogCallback(string message, string objectName, UUID imageID,
UUID objectID, string firstName, string lastName, int chatChannel, List buttons);
///
/// Triggered when a script asks for permissions
///
/// Simulator object this request comes from
/// Task ID of the script requesting permissions
/// ID of the object containing the script
/// Name of the object containing the script
/// Name of the object's owner
/// Bitwise value representing the requested permissions
public delegate void ScriptQuestionCallback(Simulator simulator, UUID taskID, UUID itemID, string objectName, string objectOwner, ScriptPermission questions);
///
/// Triggered when a script displays a URL via llLoadURL
///
/// Name of the scripted object
/// ID of the scripted object
/// ID of the object's owner
/// Whether or not ownerID is a group
/// Message displayed along with URL
/// Offered URL
public delegate void LoadURLCallback( string objectName, UUID objectID, UUID ownerID, bool ownerIsGroup, string message, string URL);
///
/// Triggered when the L$ account balance for this avatar changes
///
/// The new account balance
public delegate void BalanceCallback(int balance);
///
/// Triggered on Money Balance Reply
///
/// ID provided in Request Money Balance, or auto-generated by system events
/// Was the transaction successful
/// Current balance
/// Land use credits you have
/// Tier committed to group(s)
/// Description of the transaction
public delegate void MoneyBalanceReplyCallback(UUID transactionID, bool transactionSuccess, int balance, int metersCredit, int metersCommitted, string description);
///
/// Triggered on incoming instant messages
///
/// Instant message data structure
/// Simulator where this IM was received from
public delegate void InstantMessageCallback(InstantMessage im, Simulator simulator);
///
/// Triggered for any status updates of a teleport (progress, failed, succeeded)
///
/// A message about the current teleport status
/// The current status of the teleport
/// Various flags describing the teleport
public delegate void TeleportCallback(string message, TeleportStatus status, TeleportFlags flags);
///
/// Reply to a request to join a group, informs whether it was successful or not
///
/// The group we attempted to join
/// Whether we joined the group or not
public delegate void JoinGroupCallback(UUID groupID, bool success);
///
/// Reply to a request to leave a group, informs whether it was successful or not
///
/// The group we attempted to leave
/// Whether we left the group or not
public delegate void LeaveGroupCallback(UUID groupID, bool success);
///
/// Informs the avatar that it is no longer a member of a group
///
/// The group Key we are no longer a member of
public delegate void GroupDroppedCallback(UUID groupID);
///
/// Reply to an AgentData request
///
/// First name of Avatar
/// Last name of Avatar
/// Key of Group Avatar has active
/// Avatars Active Title
/// Powers Avatar has in group
/// Name of the Group
public delegate void AgentDataCallback(string firstName, string lastName, UUID activeGroupID,
string groupTitle, GroupPowers groupPowers, string groupName);
///
/// Triggered when the current agent animations change
///
/// A convenience reference to the
/// SignaledAnimations collection
public delegate void AnimationsChangedCallback(InternalDictionary agentAnimations);
///
/// Triggered when an object or avatar forcefully collides with our
/// agent
///
/// Collision type
/// Colliding object or avatar ID
/// Victim ID, should be our own AgentID
/// Velocity or total force of the collision
/// Time the collision occurred
public delegate void MeanCollisionCallback(MeanCollisionType type, UUID perp, UUID victim,
float magnitude, DateTime time);
///
/// Triggered when the agent physically moves in to a neighboring region
///
/// Simulator agent was previously occupying
/// Simulator agent is now currently occupying
public delegate void RegionCrossedCallback(Simulator oldSim, Simulator newSim);
///
/// Fired when group chat session confirmed joined
/// Key of Session (groups UUID)
/// Temporary session Key
/// if session start successful,
/// otherwise
public delegate void GroupChatJoined(UUID groupChatSessionID, UUID tmpSessionID, bool success);
/// Fired when agent group chat session terminated
/// Key of Session (groups UUID)
public delegate void GroupChatLeft(UUID groupchatSessionID);
///
/// Fired when alert message received from simulator
///
/// the message sent from the grid to our avatar.
public delegate void AlertMessage(string message);
///
/// Fired when a script wants to give or release controls.
///
/// Control to give or take
/// true of passing control to agent
/// true of taking control from agent
public delegate void ScriptControlCallback(ScriptControlChange controls, bool pass, bool take);
///
/// Fired when camera tries to view beyond its view limits
///
/// representing plane where constraints were hit
public delegate void CameraConstraintCallback(Vector4 collidePlane);
///
/// Fired when script sensor reply is received
///
/// requestors UUID
/// Sources Group UUID
/// Sources Name
/// Objects UUID
/// Object owners UUID
/// Position of Object
/// Range of Object
/// Rotation of object
/// Objects Type
/// representing the velocity of object
/// TODO: this should probably be a struct, and there should be an enum added for type
public delegate void ScriptSensorReplyCallback(UUID requestorID, UUID groupID, string name,
UUID objectID, UUID ownerID, Vector3 position, float range, Quaternion rotation,
ScriptSensorTypeFlags type, Vector3 velocity);
///
/// Fired in response to a RequestSit()
///
/// ID of primitive avatar will be sitting on
/// true of avatar autopiloted there
/// Camera offset when avatar is seated
/// Camera eye offset when avatar is seated
/// true of sitting on this object will force mouselook
/// position avatar will be in when seated
/// rotation avatar will be in when seated
public delegate void AvatarSitResponseCallback(UUID objectID, bool autoPilot, Vector3 cameraAtOffset,
Vector3 cameraEyeOffset, bool forceMouselook, Vector3 sitPosition, Quaternion sitRotation);
///
///
///
///
///
///
///
public delegate void AgentMovementCallback(Vector3 agentPosition, ulong regionHandle,
Vector3 agentLookAt, string simVersion);
#endregion Callbacks
#region Events
/// Fired when a is received from the simulator, Contains
/// Any Whisper, Shout, or Say within range of avatar
public event ChatCallback OnChat;
/// Fired when a is received, use
/// to respond to dialog
public event ScriptDialogCallback OnScriptDialog;
/// Fired when a is received in response to a
/// scripted object requesting permissions, Use to reply
public event ScriptQuestionCallback OnScriptQuestion;
/// Fired when a is received, contains a URL pasted in Chat
public event LoadURLCallback OnLoadURL;
/// Fired when a or a ChatterBoxInvitation is received
public event InstantMessageCallback OnInstantMessage;
/// Fired when a is received, occurs when a
/// or is called
public event TeleportCallback OnTeleport;
/// Fired when a indicating the agents
/// balance has changed by spending, sending, or receiving L$, Contains the Avatars new balance
public event BalanceCallback OnBalanceUpdated;
/// Fired when a is received, contains L$ balance and additional
/// details of the transaction
public event MoneyBalanceReplyCallback OnMoneyBalanceReplyReceived;
/// Fired when a is received, caused by changing the
/// Agents active group with
public event AgentDataCallback OnAgentDataUpdated;
/// Fired when a is received, will contain a Dictionary
/// of animations currently being played
public event AnimationsChangedCallback OnAnimationsChanged;
/// Callback for an object or avatar forcefully colliding
/// with the agent
public event MeanCollisionCallback OnMeanCollision;
/// Callback for the agent moving in to a neighboring sim
public event RegionCrossedCallback OnRegionCrossed;
/// Callback for when agent is confirmed joined group chat session.
public event GroupChatJoined OnGroupChatJoin;
/// Callback for when agent is confirmed to have left group chat session.
public event GroupChatLeft OnGroupChatLeft;
/// Alert messages sent to client from simulator
public event AlertMessage OnAlertMessage;
/// Fired when a script wants to take or release control of your avatar.
public event ScriptControlCallback OnScriptControlChange;
/// Fired when our avatar camera reaches the maximum possible point
public event CameraConstraintCallback OnCameraConstraint;
/// Fired when a script sensor reply is received
public event ScriptSensorReplyCallback OnScriptSensorReply;
/// Fired in response to a sit request
public event AvatarSitResponseCallback OnAvatarSitResponse;
#endregion Events
/// Reference to the GridClient object
public readonly GridClient Client;
/// Used for movement and camera tracking
public readonly AgentMovement Movement;
/// Currently playing animations for the agent. Can be used to
/// check the current movement status such as walking, hovering, aiming,
/// etc. by checking for system animations in the Animations class
public InternalDictionary SignaledAnimations = new InternalDictionary();
///
/// Dictionary containing current Group Chat sessions and members
///
public InternalDictionary> GroupChatSessions = new InternalDictionary>();
#region Properties
/// Your (client) avatars
/// "client", "agent", and "avatar" all represent the same thing
public UUID AgentID { get { return id; } }
/// Temporary assigned to this session, used for
/// verifying our identity in packets
public UUID SessionID { get { return sessionID; } }
/// Shared secret that is never sent over the wire
public UUID SecureSessionID { get { return secureSessionID; } }
/// Your (client) avatar ID, local to the current region/sim
public uint LocalID { get { return localID; } }
/// Where the avatar started at login. Can be "last", "home"
/// or a login
public string StartLocation { get { return startLocation; } }
/// The access level of this agent, usually M or PG
public string AgentAccess { get { return agentAccess; } }
/// The CollisionPlane of Agent
public Vector4 CollisionPlane { get { return collisionPlane; } }
/// An representing the velocity of our agent
public Vector3 Velocity { get { return velocity; } }
/// An representing the acceleration of our agent
public Vector3 Acceleration { get { return acceleration; } }
/// A which specifies the angular speed, and axis about which an Avatar is rotating.
public Vector3 AngularVelocity { get { return angularVelocity; } }
/// Position avatar client will goto when login to 'home' or during
/// teleport request to 'home' region.
public Vector3 HomePosition { get { return homePosition; } }
/// LookAt point saved/restored with HomePosition
public Vector3 HomeLookAt { get { return homeLookAt; } }
/// Avatar First Name (i.e. Philip)
public string FirstName { get { return firstName; } }
/// Avatar Last Name (i.e. Linden)
public string LastName { get { return lastName; } }
/// Avatar Full Name (i.e. Philip Linden)
public string Name
{
get
{
// This is a fairly common request, so assume the name doesn't
// change mid-session and cache the result
if (fullName == null || fullName.Length < 2)
fullName = String.Format("{0} {1}", firstName, lastName);
return fullName;
}
}
/// Gets the health of the agent
public float Health { get { return health; } }
/// Gets the current balance of the agent
public int Balance { get { return balance; } }
/// Gets the local ID of the prim the agent is sitting on,
/// zero if the avatar is not currently sitting
public uint SittingOn { get { return sittingOn; } }
/// Gets the of the agents active group.
public UUID ActiveGroup { get { return activeGroup; } }
/// Current status message for teleporting
public string TeleportMessage { get { return teleportMessage; } }
/// Current position of the agent as a relative offset from
/// the simulator, or the parent object if we are sitting on something
public Vector3 RelativePosition { get { return relativePosition; } }
/// Current rotation of the agent as a relative rotation from
/// the simulator, or the parent object if we are sitting on something
public Quaternion RelativeRotation { get { return relativeRotation; } }
/// Current position of the agent in the simulator
public Vector3 SimPosition
{
get
{
if (sittingOn != 0)
{
Primitive parent;
if(Client.Network.CurrentSim != null && Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(sittingOn, out parent))
{
return parent.Position + relativePosition;
}
else
{
Logger.Log("Currently sitting on object " + sittingOn + " which is not tracked, SimPosition will be inaccurate",
Helpers.LogLevel.Warning, Client);
return relativePosition;
}
}
else
{
return relativePosition;
}
}
}
///
/// A representing the agents current rotation
///
public Quaternion SimRotation
{
get
{
if (sittingOn != 0)
{
Primitive parent;
if (Client.Network.CurrentSim != null && Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(sittingOn, out parent))
{
return relativeRotation * parent.Rotation;
}
else
{
Logger.Log("Currently sitting on object " + sittingOn + " which is not tracked, SimRotation will be inaccurate",
Helpers.LogLevel.Warning, Client);
return relativeRotation;
}
}
else
{
return relativeRotation;
}
}
}
/// Returns the global grid position of the avatar
public Vector3d GlobalPosition
{
get
{
if (Client.Network.CurrentSim != null)
{
uint globalX, globalY;
Helpers.LongToUInts(Client.Network.CurrentSim.Handle, out globalX, out globalY);
Vector3 pos = SimPosition;
return new Vector3d(
(double)globalX + (double)pos.X,
(double)globalY + (double)pos.Y,
(double)pos.Z);
}
else
return Vector3d.Zero;
}
}
#endregion Properties
internal uint localID;
internal Vector3 relativePosition;
internal Quaternion relativeRotation = Quaternion.Identity;
internal Vector4 collisionPlane;
internal Vector3 velocity;
internal Vector3 acceleration;
internal Vector3 angularVelocity;
internal uint sittingOn;
internal int lastInterpolation;
#region Private Members
private UUID id;
private UUID sessionID;
private UUID secureSessionID;
private string startLocation = String.Empty;
private string agentAccess = String.Empty;
private Vector3 homePosition;
private Vector3 homeLookAt;
private string firstName = String.Empty;
private string lastName = String.Empty;
private string fullName;
private string teleportMessage = String.Empty;
private TeleportStatus teleportStat = TeleportStatus.None;
private ManualResetEvent teleportEvent = new ManualResetEvent(false);
private uint heightWidthGenCounter;
private float health;
private int balance;
private UUID activeGroup;
#endregion Private Members
///
/// Constructor, setup callbacks for packets related to our avatar
///
/// A reference to the Class
public AgentManager(GridClient client)
{
Client = client;
Movement = new AgentMovement(Client);
NetworkManager.PacketCallback callback;
Client.Network.OnDisconnected += new NetworkManager.DisconnectedCallback(Network_OnDisconnected);
// 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);
Client.Network.RegisterCallback(PacketType.TeleportCancel, callback);
Client.Network.RegisterCallback(PacketType.TeleportLocal, callback);
// Instant message callback
Client.Network.RegisterCallback(PacketType.ImprovedInstantMessage, new NetworkManager.PacketCallback(InstantMessageHandler));
// Chat callback
Client.Network.RegisterCallback(PacketType.ChatFromSimulator, new NetworkManager.PacketCallback(ChatHandler));
// Script dialog callback
Client.Network.RegisterCallback(PacketType.ScriptDialog, new NetworkManager.PacketCallback(ScriptDialogHandler));
// Script question callback
Client.Network.RegisterCallback(PacketType.ScriptQuestion, new NetworkManager.PacketCallback(ScriptQuestionHandler));
// Script URL callback
Client.Network.RegisterCallback(PacketType.LoadURL, new NetworkManager.PacketCallback(LoadURLHandler));
// Movement complete callback
Client.Network.RegisterCallback(PacketType.AgentMovementComplete, new NetworkManager.PacketCallback(MovementCompleteHandler));
// Health callback
Client.Network.RegisterCallback(PacketType.HealthMessage, new NetworkManager.PacketCallback(HealthHandler));
// Money callback
Client.Network.RegisterCallback(PacketType.MoneyBalanceReply, new NetworkManager.PacketCallback(BalanceHandler));
//Agent update callback
Client.Network.RegisterCallback(PacketType.AgentDataUpdate, new NetworkManager.PacketCallback(AgentDataUpdateHandler));
// Animation callback
Client.Network.RegisterCallback(PacketType.AvatarAnimation, new NetworkManager.PacketCallback(AvatarAnimationHandler));
// Object colliding into our agent callback
Client.Network.RegisterCallback(PacketType.MeanCollisionAlert, new NetworkManager.PacketCallback(MeanCollisionAlertHandler));
// Region Crossing
Client.Network.RegisterCallback(PacketType.CrossedRegion, new NetworkManager.PacketCallback(CrossedRegionHandler));
// CAPS callbacks
Client.Network.RegisterEventCallback("EstablishAgentCommunication", new Caps.EventQueueCallback(EstablishAgentCommunicationEventHandler));
// Incoming Group Chat
Client.Network.RegisterEventCallback("ChatterBoxInvitation", new Caps.EventQueueCallback(ChatterBoxInvitationHandler));
// Outgoing Group Chat Reply
Client.Network.RegisterEventCallback("ChatterBoxSessionEventReply", new Caps.EventQueueCallback(ChatterBoxSessionEventHandler));
Client.Network.RegisterEventCallback("ChatterBoxSessionStartReply", new Caps.EventQueueCallback(ChatterBoxSessionStartReplyHandler));
Client.Network.RegisterEventCallback("ChatterBoxSessionAgentListUpdates", new Caps.EventQueueCallback(ChatterBoxSessionAgentListReplyHandler));
// Login
Client.Network.RegisterLoginResponseCallback(new NetworkManager.LoginResponseCallback(Network_OnLoginResponse));
// Alert Messages
Client.Network.RegisterCallback(PacketType.AlertMessage, new NetworkManager.PacketCallback(AlertMessageHandler));
// script control change messages, ie: when an in-world LSL script wants to take control of your agent.
Client.Network.RegisterCallback(PacketType.ScriptControlChange, new NetworkManager.PacketCallback(ScriptControlChangeHandler));
// Camera Constraint (probably needs to move to AgentManagerCamera TODO:
Client.Network.RegisterCallback(PacketType.CameraConstraint, new NetworkManager.PacketCallback(CameraConstraintHandler));
Client.Network.RegisterCallback(PacketType.ScriptSensorReply, new NetworkManager.PacketCallback(ScriptSensorReplyHandler));
Client.Network.RegisterCallback(PacketType.AvatarSitResponse, new NetworkManager.PacketCallback(AvatarSitResponseHandler));
}
#region Chat and instant messages
///
/// 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.Self.SessionID;
chat.ChatData.Channel = channel;
chat.ChatData.Message = Utils.StringToBytes(message);
chat.ChatData.Type = (byte)type;
Client.Network.SendPacket(chat);
}
/// Requests missed/offline messages
public void RetrieveInstantMessages()
{
RetrieveInstantMessagesPacket p = new RetrieveInstantMessagesPacket();
p.AgentData.AgentID = Client.Self.AgentID;
p.AgentData.SessionID = Client.Self.SessionID;
Client.Network.SendPacket(p);
}
///
/// Send an Instant Message
///
/// Target of the Instant Message
/// Text message being sent
public void InstantMessage(UUID target, string message)
{
InstantMessage(Name, target, message, AgentID.Equals(target) ? AgentID : target ^ AgentID,
InstantMessageDialog.MessageFromAgent, InstantMessageOnline.Offline, this.SimPosition,
UUID.Zero, new byte[0]);
}
///
/// Send an Instant Message
///
/// Target of the Instant Message
/// Text message being sent
/// IM session ID (to differentiate between IM windows)
public void InstantMessage(UUID target, string message, UUID imSessionID)
{
InstantMessage(Name, target, message, imSessionID,
InstantMessageDialog.MessageFromAgent, InstantMessageOnline.Offline, this.SimPosition,
UUID.Zero, new byte[0]);
}
///
/// Send an Instant Message
///
/// The name this IM will show up as being from
/// Key of Avatar
/// Text message being sent
/// IM session ID (to differentiate between IM windows)
/// IDs of sessions for a conference
public void InstantMessage(string fromName, UUID target, string message, UUID imSessionID,
UUID[] conferenceIDs)
{
byte[] binaryBucket;
if (conferenceIDs != null && conferenceIDs.Length > 0)
{
binaryBucket = new byte[16 * conferenceIDs.Length];
for (int i = 0; i < conferenceIDs.Length; ++i)
Buffer.BlockCopy(conferenceIDs[i].GetBytes(), 0, binaryBucket, i * 16, 16);
}
else
{
binaryBucket = new byte[0];
}
InstantMessage(fromName, target, message, imSessionID, InstantMessageDialog.MessageFromAgent,
InstantMessageOnline.Offline, Vector3.Zero, UUID.Zero, binaryBucket);
}
///
/// Send an Instant Message
///
/// The name this IM will show up as being from
/// Key of Avatar
/// Text message being sent
/// IM session ID (to differentiate between IM windows)
/// Type of instant message to send
/// Whether to IM offline avatars as well
/// Senders Position
/// RegionID Sender is In
/// Packed binary data that is specific to
/// the dialog type
public void InstantMessage(string fromName, UUID target, string message, UUID imSessionID,
InstantMessageDialog dialog, InstantMessageOnline offline, Vector3 position, UUID regionID,
byte[] binaryBucket)
{
if (target != UUID.Zero)
{
ImprovedInstantMessagePacket im = new ImprovedInstantMessagePacket();
if (imSessionID.Equals(UUID.Zero) || imSessionID.Equals(AgentID))
imSessionID = AgentID.Equals(target) ? AgentID : target ^ AgentID;
im.AgentData.AgentID = Client.Self.AgentID;
im.AgentData.SessionID = Client.Self.SessionID;
im.MessageBlock.Dialog = (byte)dialog;
im.MessageBlock.FromAgentName = Utils.StringToBytes(fromName);
im.MessageBlock.FromGroup = false;
im.MessageBlock.ID = imSessionID;
im.MessageBlock.Message = Utils.StringToBytes(message);
im.MessageBlock.Offline = (byte)offline;
im.MessageBlock.ToAgentID = target;
if (binaryBucket != null)
im.MessageBlock.BinaryBucket = binaryBucket;
else
im.MessageBlock.BinaryBucket = new byte[0];
// These fields are mandatory, even if we don't have valid values for them
im.MessageBlock.Position = Vector3.Zero;
//TODO: Allow region id to be correctly set by caller or fetched from Client.*
im.MessageBlock.RegionID = regionID;
// Send the message
Client.Network.SendPacket(im);
}
else
{
Logger.Log(String.Format("Suppressing instant message \"{0}\" to UUID.Zero", message),
Helpers.LogLevel.Error, Client);
}
}
///
/// Send an Instant Message to a group
///
/// of the group to send message to
/// Text Message being sent.
public void InstantMessageGroup(UUID groupID, string message)
{
InstantMessageGroup(Name, groupID, message);
}
///
/// Send an Instant Message to a group
///
/// The name this IM will show up as being from
/// of the group to send message to
/// Text message being sent
/// This does not appear to function with groups the agent is not in
public void InstantMessageGroup(string fromName, UUID groupID, string message)
{
lock (GroupChatSessions.Dictionary)
if (GroupChatSessions.ContainsKey(groupID))
{
ImprovedInstantMessagePacket im = new ImprovedInstantMessagePacket();
im.AgentData.AgentID = Client.Self.AgentID;
im.AgentData.SessionID = Client.Self.SessionID;
im.MessageBlock.Dialog = (byte)InstantMessageDialog.SessionSend;
im.MessageBlock.FromAgentName = Utils.StringToBytes(fromName);
im.MessageBlock.FromGroup = false;
im.MessageBlock.Message = Utils.StringToBytes(message);
im.MessageBlock.Offline = 0;
im.MessageBlock.ID = groupID;
im.MessageBlock.ToAgentID = groupID;
im.MessageBlock.Position = Vector3.Zero;
im.MessageBlock.RegionID = UUID.Zero;
im.MessageBlock.BinaryBucket = Utils.StringToBytes("\0");
Client.Network.SendPacket(im);
}
else
{
Logger.Log("No Active group chat session appears to exist, use RequestJoinGroupChat() to join one",
Helpers.LogLevel.Error, Client);
}
}
///
/// Send a request to join a group chat session
///
/// of Group to leave
public void RequestJoinGroupChat(UUID groupID)
{
ImprovedInstantMessagePacket im = new ImprovedInstantMessagePacket();
im.AgentData.AgentID = Client.Self.AgentID;
im.AgentData.SessionID = Client.Self.SessionID;
im.MessageBlock.Dialog = (byte)InstantMessageDialog.SessionGroupStart;
im.MessageBlock.FromAgentName = Utils.StringToBytes(Client.Self.Name);
im.MessageBlock.FromGroup = false;
im.MessageBlock.Message = new byte[0];
im.MessageBlock.Offline = 0;
im.MessageBlock.ID = groupID;
im.MessageBlock.ToAgentID = groupID;
im.MessageBlock.BinaryBucket = new byte[0];
im.MessageBlock.Position = Vector3.Zero;
im.MessageBlock.RegionID = UUID.Zero;
Client.Network.SendPacket(im);
}
///
/// Request self terminates group chat. This will stop Group IM's from showing up
/// until session is rejoined or expires.
///
/// of Group to leave
public void RequestLeaveGroupChat(UUID groupID)
{
ImprovedInstantMessagePacket im = new ImprovedInstantMessagePacket();
im.AgentData.AgentID = Client.Self.AgentID;
im.AgentData.SessionID = Client.Self.SessionID;
im.MessageBlock.Dialog = (byte)InstantMessageDialog.SessionDrop;
im.MessageBlock.FromAgentName = Utils.StringToBytes(Client.Self.Name);
im.MessageBlock.FromGroup = false;
im.MessageBlock.Message = new byte[0];
im.MessageBlock.Offline = 0;
im.MessageBlock.ID = groupID;
im.MessageBlock.ToAgentID = groupID;
im.MessageBlock.BinaryBucket = new byte[0];
im.MessageBlock.Position = Vector3.Zero;
im.MessageBlock.RegionID = UUID.Zero;
Client.Network.SendPacket(im);
}
///
/// Reply to script dialog questions.
///
/// Channel initial request came on
/// Index of button you're "clicking"
/// Label of button you're "clicking"
/// of Object that sent the dialog request
///
public void ReplyToScriptDialog(int channel, int buttonIndex, string buttonlabel, UUID objectID)
{
ScriptDialogReplyPacket reply = new ScriptDialogReplyPacket();
reply.AgentData.AgentID = Client.Self.AgentID;
reply.AgentData.SessionID = Client.Self.SessionID;
reply.Data.ButtonIndex = buttonIndex;
reply.Data.ButtonLabel = Utils.StringToBytes(buttonlabel);
reply.Data.ChatChannel = channel;
reply.Data.ObjectID = objectID;
Client.Network.SendPacket(reply);
}
#endregion Chat and instant messages
#region Viewer Effects
///
/// Start a particle stream between an agent and an object
///
/// Key of the source agent
/// Key of the target object
///
/// The type from the enum
/// A unique for this effect
public void PointAtEffect(UUID sourceAvatar, UUID targetObject, Vector3d globalOffset, PointAtType type,
UUID effectID)
{
ViewerEffectPacket effect = new ViewerEffectPacket();
effect.AgentData.AgentID = Client.Self.AgentID;
effect.AgentData.SessionID = Client.Self.SessionID;
effect.Effect = new ViewerEffectPacket.EffectBlock[1];
effect.Effect[0] = new ViewerEffectPacket.EffectBlock();
effect.Effect[0].AgentID = Client.Self.AgentID;
effect.Effect[0].Color = new byte[4];
effect.Effect[0].Duration = (type == PointAtType.Clear) ? 0.0f : Single.MaxValue / 4.0f;
effect.Effect[0].ID = effectID;
effect.Effect[0].Type = (byte)EffectType.PointAt;
byte[] typeData = new byte[57];
if (sourceAvatar != UUID.Zero)
Buffer.BlockCopy(sourceAvatar.GetBytes(), 0, typeData, 0, 16);
if (targetObject != UUID.Zero)
Buffer.BlockCopy(targetObject.GetBytes(), 0, typeData, 16, 16);
Buffer.BlockCopy(globalOffset.GetBytes(), 0, typeData, 32, 24);
typeData[56] = (byte)type;
effect.Effect[0].TypeData = typeData;
Client.Network.SendPacket(effect);
}
///
/// Start a particle stream between an agent and an object
///
/// Key of the source agent
/// Key of the target object
/// A representing the beams offset from the source
/// A which sets the avatars lookat animation
/// of the Effect
public void LookAtEffect(UUID sourceAvatar, UUID targetObject, Vector3d globalOffset, LookAtType type,
UUID effectID)
{
ViewerEffectPacket effect = new ViewerEffectPacket();
effect.AgentData.AgentID = Client.Self.AgentID;
effect.AgentData.SessionID = Client.Self.SessionID;
float duration;
switch (type)
{
case LookAtType.Clear:
duration = 0.0f;
break;
case LookAtType.Hover:
duration = 1.0f;
break;
case LookAtType.FreeLook:
duration = 2.0f;
break;
case LookAtType.Idle:
duration = 3.0f;
break;
case LookAtType.AutoListen:
case LookAtType.Respond:
duration = 4.0f;
break;
case LookAtType.None:
case LookAtType.Select:
case LookAtType.Focus:
case LookAtType.Mouselook:
duration = Single.MaxValue / 2.0f;
break;
default:
duration = 0.0f;
break;
}
effect.Effect = new ViewerEffectPacket.EffectBlock[1];
effect.Effect[0] = new ViewerEffectPacket.EffectBlock();
effect.Effect[0].AgentID = Client.Self.AgentID;
effect.Effect[0].Color = new byte[4];
effect.Effect[0].Duration = duration;
effect.Effect[0].ID = effectID;
effect.Effect[0].Type = (byte)EffectType.LookAt;
byte[] typeData = new byte[57];
if (sourceAvatar != UUID.Zero)
Buffer.BlockCopy(sourceAvatar.GetBytes(), 0, typeData, 0, 16);
if (targetObject != UUID.Zero)
Buffer.BlockCopy(targetObject.GetBytes(), 0, typeData, 16, 16);
typeData[56] = (byte)type;
effect.Effect[0].TypeData = typeData;
Client.Network.SendPacket(effect);
}
///
/// Create a particle beam between an avatar and an primitive
///
/// of sources avatar
/// of the target
/// global offset
/// Color values of beam
/// a float representing the duration the beam will last
/// of the Effect
public void BeamEffect(UUID sourceAvatar, UUID targetObject, Vector3d globalOffset, Color4 color,
float duration, UUID effectID)
{
ViewerEffectPacket effect = new ViewerEffectPacket();
effect.AgentData.AgentID = Client.Self.AgentID;
effect.AgentData.SessionID = Client.Self.SessionID;
effect.Effect = new ViewerEffectPacket.EffectBlock[1];
effect.Effect[0] = new ViewerEffectPacket.EffectBlock();
effect.Effect[0].AgentID = Client.Self.AgentID;
effect.Effect[0].Color = color.GetBytes();
effect.Effect[0].Duration = duration;
effect.Effect[0].ID = effectID;
effect.Effect[0].Type = (byte)EffectType.Beam;
byte[] typeData = new byte[56];
Buffer.BlockCopy(sourceAvatar.GetBytes(), 0, typeData, 0, 16);
Buffer.BlockCopy(targetObject.GetBytes(), 0, typeData, 16, 16);
Buffer.BlockCopy(globalOffset.GetBytes(), 0, typeData, 32, 24);
effect.Effect[0].TypeData = typeData;
Client.Network.SendPacket(effect);
}
#endregion Viewer Effects
#region Movement Actions
///
/// Sends a request to sit on the specified object
///
/// of the object to sit on
/// Sit at offset
public void RequestSit(UUID targetID, Vector3 offset)
{
AgentRequestSitPacket requestSit = new AgentRequestSitPacket();
requestSit.AgentData.AgentID = Client.Self.AgentID;
requestSit.AgentData.SessionID = Client.Self.SessionID;
requestSit.TargetObject.TargetID = targetID;
requestSit.TargetObject.Offset = offset;
Client.Network.SendPacket(requestSit);
}
///
/// Follows a call to to actually sit on the object
///
public void Sit()
{
AgentSitPacket sit = new AgentSitPacket();
sit.AgentData.AgentID = Client.Self.AgentID;
sit.AgentData.SessionID = Client.Self.SessionID;
Client.Network.SendPacket(sit);
}
/// Stands up from sitting on a prim or the ground
/// true of AgentUpdate was sent
public bool Stand()
{
if (Client.Settings.SEND_AGENT_UPDATES)
{
Movement.StandUp = true;
Movement.SendUpdate();
Movement.StandUp = false;
Movement.SendUpdate();
return true;
}
else
{
Logger.Log("Attempted to Stand() but agent updates are disabled", Helpers.LogLevel.Warning, Client);
return false;
}
}
///
/// Does a "ground sit" at the avatar's current position
///
public void SitOnGround()
{
Movement.SitOnGround = true;
Movement.SendUpdate(true);
}
///
/// Starts or stops flying
///
/// True to start flying, false to stop flying
public void Fly(bool start)
{
if (start)
Movement.Fly = true;
else
Movement.Fly = false;
Movement.SendUpdate(true);
}
///
/// Starts or stops crouching
///
/// True to start crouching, false to stop crouching
public void Crouch(bool start)
{
if (start)
Movement.UpNeg = true;
else
Movement.UpNeg = false;
Movement.SendUpdate(true);
}
///
/// Starts a jump (begin holding the jump key)
///
public void Jump()
{
Movement.UpPos = true;
Movement.FastUp = true;
Movement.SendUpdate(true);
}
///
/// Use the autopilot sim function to move the avatar to a new
/// position. Uses double precision to get precise movements
///
/// The z value is currently not handled properly by the simulator
/// Global X coordinate to move to
/// Global Y coordinate to move to
/// Z coordinate to move to
public void AutoPilot(double globalX, double globalY, double z)
{
GenericMessagePacket autopilot = new GenericMessagePacket();
autopilot.AgentData.AgentID = Client.Self.AgentID;
autopilot.AgentData.SessionID = Client.Self.SessionID;
autopilot.AgentData.TransactionID = UUID.Zero;
autopilot.MethodData.Invoice = UUID.Zero;
autopilot.MethodData.Method = Utils.StringToBytes("autopilot");
autopilot.ParamList = new GenericMessagePacket.ParamListBlock[3];
autopilot.ParamList[0] = new GenericMessagePacket.ParamListBlock();
autopilot.ParamList[0].Parameter = Utils.StringToBytes(globalX.ToString());
autopilot.ParamList[1] = new GenericMessagePacket.ParamListBlock();
autopilot.ParamList[1].Parameter = Utils.StringToBytes(globalY.ToString());
autopilot.ParamList[2] = new GenericMessagePacket.ParamListBlock();
autopilot.ParamList[2].Parameter = Utils.StringToBytes(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 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
public void AutoPilot(ulong globalX, ulong globalY, float z)
{
GenericMessagePacket autopilot = new GenericMessagePacket();
autopilot.AgentData.AgentID = Client.Self.AgentID;
autopilot.AgentData.SessionID = Client.Self.SessionID;
autopilot.AgentData.TransactionID = UUID.Zero;
autopilot.MethodData.Invoice = UUID.Zero;
autopilot.MethodData.Method = Utils.StringToBytes("autopilot");
autopilot.ParamList = new GenericMessagePacket.ParamListBlock[3];
autopilot.ParamList[0] = new GenericMessagePacket.ParamListBlock();
autopilot.ParamList[0].Parameter = Utils.StringToBytes(globalX.ToString());
autopilot.ParamList[1] = new GenericMessagePacket.ParamListBlock();
autopilot.ParamList[1].Parameter = Utils.StringToBytes(globalY.ToString());
autopilot.ParamList[2] = new GenericMessagePacket.ParamListBlock();
autopilot.ParamList[2].Parameter = Utils.StringToBytes(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
public void AutoPilotLocal(int localX, int localY, float z)
{
uint x, y;
Helpers.LongToUInts(Client.Network.CurrentSim.Handle, out x, out y);
AutoPilot((ulong)(x + localX), (ulong)(y + localY), z);
}
/// Macro to cancel autopilot sim function
/// Not certain if this is how it is really done
/// true if control flags were set and AgentUpdate was sent to the simulator
public bool AutoPilotCancel()
{
if (Client.Settings.SEND_AGENT_UPDATES)
{
Movement.AtPos = true;
Movement.SendUpdate();
Movement.AtPos = false;
Movement.SendUpdate();
return true;
}
else
{
Logger.Log("Attempted to AutoPilotCancel() but agent updates are disabled", Helpers.LogLevel.Warning, Client);
return false;
}
}
#endregion Movement actions
#region Touch and grab
///
/// Grabs an object
///
/// an unsigned integer of the objects ID within the simulator
///
public void Grab(uint objectLocalID)
{
ObjectGrabPacket grab = new ObjectGrabPacket();
grab.AgentData.AgentID = Client.Self.AgentID;
grab.AgentData.SessionID = Client.Self.SessionID;
grab.ObjectData.LocalID = objectLocalID;
grab.ObjectData.GrabOffset = new Vector3(0, 0, 0);
Client.Network.SendPacket(grab);
}
///
/// Drags an object
///
/// of the object to drag
/// Drag target in region coordinates
public void GrabUpdate(UUID objectID, Vector3 grabPosition)
{
ObjectGrabUpdatePacket grab = new ObjectGrabUpdatePacket();
grab.AgentData.AgentID = Client.Self.AgentID;
grab.AgentData.SessionID = Client.Self.SessionID;
grab.ObjectData.ObjectID = objectID;
grab.ObjectData.GrabOffsetInitial = new Vector3(0, 0, 0);
grab.ObjectData.GrabPosition = grabPosition;
grab.ObjectData.TimeSinceLast = 0;
Client.Network.SendPacket(grab);
}
///
/// Releases a grabbed object
///
/// The Objects Simulator Local ID
///
///
///
public void DeGrab(uint objectLocalID)
{
ObjectDeGrabPacket degrab = new ObjectDeGrabPacket();
degrab.AgentData.AgentID = Client.Self.AgentID;
degrab.AgentData.SessionID = Client.Self.SessionID;
degrab.ObjectData.LocalID = objectLocalID;
Client.Network.SendPacket(degrab);
}
///
/// Touches an object
///
/// an unsigned integer of the objects ID within the simulator
///
public void Touch(uint objectLocalID)
{
Client.Self.Grab(objectLocalID);
Client.Self.DeGrab(objectLocalID);
}
#endregion Touch and grab
#region Money
///
/// Request the current L$ balance
///
public void RequestBalance()
{
MoneyBalanceRequestPacket money = new MoneyBalanceRequestPacket();
money.AgentData.AgentID = Client.Self.AgentID;
money.AgentData.SessionID = Client.Self.SessionID;
money.MoneyData.TransactionID = UUID.Zero;
Client.Network.SendPacket(money);
}
///
/// Give Money to destination Avatar
///
/// UUID of the Target Avatar
/// Amount in L$
public void GiveAvatarMoney(UUID target, int amount)
{
GiveMoney(target, amount, String.Empty, MoneyTransactionType.Gift, TransactionFlags.None);
}
///
/// Give Money to destination Avatar
///
/// UUID of the Target Avatar
/// Amount in L$
/// Description that will show up in the
/// recipients transaction history
public void GiveAvatarMoney(UUID target, int amount, string description)
{
GiveMoney(target, amount, description, MoneyTransactionType.Gift, TransactionFlags.None);
}
///
/// Give L$ to an object
///
/// object to give money to
/// amount of L$ to give
/// name of object
public void GiveObjectMoney(UUID target, int amount, string objectName)
{
GiveMoney(target, amount, objectName, MoneyTransactionType.PayObject, TransactionFlags.None);
}
///
/// Give L$ to a group
///
/// group to give money to
/// amount of L$ to give
public void GiveGroupMoney(UUID target, int amount)
{
GiveMoney(target, amount, String.Empty, MoneyTransactionType.Gift, TransactionFlags.DestGroup);
}
///
/// Give L$ to a group
///
/// group to give money to
/// amount of L$ to give
/// description of transaction
public void GiveGroupMoney(UUID target, int amount, string description)
{
GiveMoney(target, amount, description, MoneyTransactionType.Gift, TransactionFlags.DestGroup);
}
///
/// Pay texture/animation upload fee
///
public void PayUploadFee()
{
GiveMoney(UUID.Zero, Client.Settings.UPLOAD_COST, String.Empty, MoneyTransactionType.UploadCharge,
TransactionFlags.None);
}
///
/// Pay texture/animation upload fee
///
/// description of the transaction
public void PayUploadFee(string description)
{
GiveMoney(UUID.Zero, Client.Settings.UPLOAD_COST, description, MoneyTransactionType.UploadCharge,
TransactionFlags.None);
}
///
/// Give Money to destionation Object or Avatar
///
/// UUID of the Target Object/Avatar
/// Amount in L$
/// Reason (Optional normally)
/// The type of transaction
/// Transaction flags, mostly for identifying group
/// transactions
public void GiveMoney(UUID target, int amount, string description, MoneyTransactionType type, TransactionFlags flags)
{
MoneyTransferRequestPacket money = new MoneyTransferRequestPacket();
money.AgentData.AgentID = this.id;
money.AgentData.SessionID = Client.Self.SessionID;
money.MoneyData.Description = Utils.StringToBytes(description);
money.MoneyData.DestID = target;
money.MoneyData.SourceID = this.id;
money.MoneyData.TransactionType = (int)type;
money.MoneyData.AggregatePermInventory = 0; // This is weird, apparently always set to zero though
money.MoneyData.AggregatePermNextOwner = 0; // This is weird, apparently always set to zero though
money.MoneyData.Flags = (byte)flags;
money.MoneyData.Amount = amount;
Client.Network.SendPacket(money);
}
#endregion Money
#region Animations
///
/// Send an AgentAnimation packet that toggles a single animation on
///
/// The of the animation to start playing
/// Whether to ensure delivery of this packet or not
public void AnimationStart(UUID animation, bool reliable)
{
Dictionary animations = new Dictionary();
animations[animation] = true;
Animate(animations, reliable);
}
///
/// Send an AgentAnimation packet that toggles a single animation off
///
/// The of a
/// currently playing animation to stop playing
/// Whether to ensure delivery of this packet or not
public void AnimationStop(UUID animation, bool reliable)
{
Dictionary animations = new Dictionary();
animations[animation] = false;
Animate(animations, reliable);
}
///
/// Send an AgentAnimation packet that will toggle animations on or off
///
/// A list of animation s, and whether to
/// turn that animation on or off
/// Whether to ensure delivery of this packet or not
public void Animate(Dictionary animations, bool reliable)
{
AgentAnimationPacket animate = new AgentAnimationPacket();
animate.Header.Reliable = reliable;
animate.AgentData.AgentID = Client.Self.AgentID;
animate.AgentData.SessionID = Client.Self.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);
}
#endregion Animations
#region Teleporting
///
/// Teleports agent to their stored home location
///
/// true on successful teleport to home location
public bool GoHome()
{
return Teleport(UUID.Zero);
}
///
/// Teleport agent to a landmark
///
/// of the landmark to teleport agent to
/// true on success, false on failure
public bool Teleport(UUID landmark)
{
teleportStat = TeleportStatus.None;
teleportEvent.Reset();
TeleportLandmarkRequestPacket p = new TeleportLandmarkRequestPacket();
p.Info = new TeleportLandmarkRequestPacket.InfoBlock();
p.Info.AgentID = Client.Self.AgentID;
p.Info.SessionID = Client.Self.SessionID;
p.Info.LandmarkID = landmark;
Client.Network.SendPacket(p);
teleportEvent.WaitOne(Client.Settings.TELEPORT_TIMEOUT, false);
if (teleportStat == TeleportStatus.None ||
teleportStat == TeleportStatus.Start ||
teleportStat == TeleportStatus.Progress)
{
teleportMessage = "Teleport timed out.";
teleportStat = TeleportStatus.Failed;
}
return (teleportStat == TeleportStatus.Finished);
}
///
/// Attempt to look up a simulator name and teleport to the discovered
/// destination
///
/// Region name to look up
/// Position to teleport to
/// True if the lookup and teleport were successful, otherwise
/// false
public bool Teleport(string simName, Vector3 position)
{
return Teleport(simName, position, new Vector3(0, 1.0f, 0));
}
///
/// Attempt to look up a simulator name and teleport to the discovered
/// destination
///
/// Region name to look up
/// Position to teleport to
/// Target to look at
/// True if the lookup and teleport were successful, otherwise
/// false
public bool Teleport(string simName, Vector3 position, Vector3 lookAt)
{
teleportStat = TeleportStatus.None;
simName = simName.ToLower();
if (simName != Client.Network.CurrentSim.Name.ToLower())
{
// Teleporting to a foreign sim
GridRegion region;
if (Client.Grid.GetGridRegion(simName, GridLayerType.Objects, out region))
{
return Teleport(region.RegionHandle, position, lookAt);
}
else
{
teleportMessage = "Unable to resolve name: " + simName;
teleportStat = TeleportStatus.Failed;
return false;
}
}
else
{
// Teleporting to the sim we're already in
return Teleport(Client.Network.CurrentSim.Handle, position, lookAt);
}
}
///
/// Teleport agent to another region
///
/// handle of region to teleport agent to
/// position in destination sim to teleport to
/// true on success, false on failure
/// This call is blocking
public bool Teleport(ulong regionHandle, Vector3 position)
{
return Teleport(regionHandle, position, new Vector3(0.0f, 1.0f, 0.0f));
}
///
/// Teleport agent to another region
///
/// handle of region to teleport agent to
/// position in destination sim to teleport to
/// direction in destination sim agent will look at
/// true on success, false on failure
/// This call is blocking
public bool Teleport(ulong regionHandle, Vector3 position, Vector3 lookAt)
{
teleportStat = TeleportStatus.None;
teleportEvent.Reset();
RequestTeleport(regionHandle, position, lookAt);
teleportEvent.WaitOne(Client.Settings.TELEPORT_TIMEOUT, false);
if (teleportStat == TeleportStatus.None ||
teleportStat == TeleportStatus.Start ||
teleportStat == TeleportStatus.Progress)
{
teleportMessage = "Teleport timed out.";
teleportStat = TeleportStatus.Failed;
}
return (teleportStat == TeleportStatus.Finished);
}
///
/// Request teleport to a another simulator
///
/// handle of region to teleport agent to
/// position in destination sim to teleport to
public void RequestTeleport(ulong regionHandle, Vector3 position)
{
RequestTeleport(regionHandle, position, new Vector3(0.0f, 1.0f, 0.0f));
}
///
/// Request teleport to a another simulator
///
/// handle of region to teleport agent to
/// position in destination sim to teleport to
/// direction in destination sim agent will look at
public void RequestTeleport(ulong regionHandle, Vector3 position, Vector3 lookAt)
{
if (Client.Network.CurrentSim != null &&
Client.Network.CurrentSim.Caps != null &&
Client.Network.CurrentSim.Caps.IsEventQueueRunning)
{
TeleportLocationRequestPacket teleport = new TeleportLocationRequestPacket();
teleport.AgentData.AgentID = Client.Self.AgentID;
teleport.AgentData.SessionID = Client.Self.SessionID;
teleport.Info.LookAt = lookAt;
teleport.Info.Position = position;
teleport.Info.RegionHandle = regionHandle;
Logger.Log("Requesting teleport to region handle " + regionHandle.ToString(), Helpers.LogLevel.Info, Client);
Client.Network.SendPacket(teleport);
}
else
{
teleportMessage = "CAPS event queue is not running";
teleportEvent.Set();
teleportStat = TeleportStatus.Failed;
}
}
///
/// Teleport agent to a landmark
///
/// of the landmark to teleport agent to
public void RequestTeleport(UUID landmark)
{
TeleportLandmarkRequestPacket p = new TeleportLandmarkRequestPacket();
p.Info = new TeleportLandmarkRequestPacket.InfoBlock();
p.Info.AgentID = Client.Self.AgentID;
p.Info.SessionID = Client.Self.SessionID;
p.Info.LandmarkID = landmark;
Client.Network.SendPacket(p);
}
///
/// Send a teleport lure to another avatar with default "Join me in ..." invitation message
///
/// target avatars to lure
public void SendTeleportLure(UUID targetID)
{
SendTeleportLure(targetID, "Join me in " + Client.Network.CurrentSim.Name + "!");
}
///
/// Send a teleport lure to another avatar with custom invitation message
///
/// target avatars to lure
/// custom message to send with invitation
public void SendTeleportLure(UUID targetID, string message)
{
StartLurePacket p = new StartLurePacket();
p.AgentData.AgentID = Client.Self.id;
p.AgentData.SessionID = Client.Self.SessionID;
p.Info.LureType = 0;
p.Info.Message = Utils.StringToBytes(message);
p.TargetData = new StartLurePacket.TargetDataBlock[] { new StartLurePacket.TargetDataBlock() };
p.TargetData[0].TargetID = targetID;
Client.Network.SendPacket(p);
}
///
/// Respond to a teleport lure by either accepting it and initiating
/// the teleport, or denying it
///
/// of the avatar sending the lure
/// true to accept the lure, false to decline it
public void TeleportLureRespond(UUID requesterID, bool accept)
{
InstantMessage(Name, requesterID, String.Empty, UUID.Random(),
accept ? InstantMessageDialog.AcceptTeleport : InstantMessageDialog.DenyTeleport,
InstantMessageOnline.Offline, this.SimPosition, UUID.Zero, new byte[0]);
if (accept)
{
TeleportLureRequestPacket lure = new TeleportLureRequestPacket();
lure.Info.AgentID = Client.Self.AgentID;
lure.Info.SessionID = Client.Self.SessionID;
lure.Info.LureID = Client.Self.AgentID;
lure.Info.TeleportFlags = (uint)TeleportFlags.ViaLure;
Client.Network.SendPacket(lure);
}
}
#endregion Teleporting
#region Misc
///
/// Update agent profile
///
/// struct containing updated
/// profile information
public void UpdateProfile(Avatar.AvatarProperties profile)
{
AvatarPropertiesUpdatePacket apup = new AvatarPropertiesUpdatePacket();
apup.AgentData.AgentID = id;
apup.AgentData.SessionID = sessionID;
apup.PropertiesData.AboutText = Utils.StringToBytes(profile.AboutText);
apup.PropertiesData.AllowPublish = profile.AllowPublish;
apup.PropertiesData.FLAboutText = Utils.StringToBytes(profile.FirstLifeText);
apup.PropertiesData.FLImageID = profile.FirstLifeImage;
apup.PropertiesData.ImageID = profile.ProfileImage;
apup.PropertiesData.MaturePublish = profile.MaturePublish;
apup.PropertiesData.ProfileURL = Utils.StringToBytes(profile.ProfileURL);
Client.Network.SendPacket(apup);
}
///
/// Update agents profile interests
///
/// selection of interests from struct
public void UpdateInterests(Avatar.Interests interests)
{
AvatarInterestsUpdatePacket aiup = new AvatarInterestsUpdatePacket();
aiup.AgentData.AgentID = id;
aiup.AgentData.SessionID = sessionID;
aiup.PropertiesData.LanguagesText = Utils.StringToBytes(interests.LanguagesText);
aiup.PropertiesData.SkillsMask = interests.SkillsMask;
aiup.PropertiesData.SkillsText = Utils.StringToBytes(interests.SkillsText);
aiup.PropertiesData.WantToMask = interests.WantToMask;
aiup.PropertiesData.WantToText = Utils.StringToBytes(interests.WantToText);
Client.Network.SendPacket(aiup);
}
///
/// Set the height and the width of the client window. This is used
/// by the server to build a virtual camera frustum for our avatar
///
/// New height of the viewer window
/// New width of the viewer window
public void SetHeightWidth(ushort height, ushort width)
{
AgentHeightWidthPacket heightwidth = new AgentHeightWidthPacket();
heightwidth.AgentData.AgentID = Client.Self.AgentID;
heightwidth.AgentData.SessionID = Client.Self.SessionID;
heightwidth.AgentData.CircuitCode = Client.Network.CircuitCode;
heightwidth.HeightWidthBlock.Height = height;
heightwidth.HeightWidthBlock.Width = width;
heightwidth.HeightWidthBlock.GenCounter = heightWidthGenCounter++;
Client.Network.SendPacket(heightwidth);
}
///
/// Request the list of muted objects and avatars for this agent
///
public void RequestMuteList()
{
MuteListRequestPacket mute = new MuteListRequestPacket();
mute.AgentData.AgentID = Client.Self.AgentID;
mute.AgentData.SessionID = Client.Self.SessionID;
mute.MuteData.MuteCRC = 0;
Client.Network.SendPacket(mute);
}
///
/// Sets home location to agents current position
///
/// will fire an AlertMessage () with
/// success or failure message
public void SetHome()
{
SetStartLocationRequestPacket s = new SetStartLocationRequestPacket();
s.AgentData = new SetStartLocationRequestPacket.AgentDataBlock();
s.AgentData.AgentID = Client.Self.AgentID;
s.AgentData.SessionID = Client.Self.SessionID;
s.StartLocationData = new SetStartLocationRequestPacket.StartLocationDataBlock();
s.StartLocationData.LocationPos = Client.Self.SimPosition;
s.StartLocationData.LocationID = 1;
s.StartLocationData.SimName = Utils.StringToBytes(String.Empty);
s.StartLocationData.LocationLookAt = Movement.Camera.AtAxis;
Client.Network.SendPacket(s);
}
///
/// Move an agent in to a simulator. This packet is the last packet
/// needed to complete the transition in to a new simulator
///
/// Object
public void CompleteAgentMovement(Simulator simulator)
{
CompleteAgentMovementPacket move = new CompleteAgentMovementPacket();
move.AgentData.AgentID = Client.Self.AgentID;
move.AgentData.SessionID = Client.Self.SessionID;
move.AgentData.CircuitCode = Client.Network.CircuitCode;
Client.Network.SendPacket(move, simulator);
}
///
/// Reply to script permissions request
///
/// Object
/// of the itemID requesting permissions
/// of the taskID requesting permissions
/// list of permissions to allow
public void ScriptQuestionReply(Simulator simulator, UUID itemID, UUID taskID, ScriptPermission permissions)
{
ScriptAnswerYesPacket yes = new ScriptAnswerYesPacket();
yes.AgentData.AgentID = Client.Self.AgentID;
yes.AgentData.SessionID = Client.Self.SessionID;
yes.Data.ItemID = itemID;
yes.Data.TaskID = taskID;
yes.Data.Questions = (int)permissions;
Client.Network.SendPacket(yes, simulator);
}
///
/// Respond to a group invitation by either accepting or denying it
///
/// UUID of the group (sent in the AgentID field of the invite message)
/// IM Session ID from the group invitation message
/// Accept the group invitation or deny it
public void GroupInviteRespond(UUID groupID, UUID imSessionID, bool accept)
{
InstantMessage(Name, groupID, String.Empty, imSessionID,
accept ? InstantMessageDialog.GroupInvitationAccept : InstantMessageDialog.GroupInvitationDecline,
InstantMessageOnline.Offline, Vector3.Zero, UUID.Zero, new byte[0]);
}
///
/// Requests script detection of objects and avatars
///
/// name of the object/avatar to search for
/// UUID of the object or avatar to search for
/// Type of search from ScriptSensorTypeFlags
/// range of scan (96 max?)
/// the arc in radians to search within
/// an user generated ID to correlate replies with
/// Simulator to perform search in
public void RequestScriptSensor(string name, UUID searchID, ScriptSensorTypeFlags type, float range, float arc, UUID requestID, Simulator sim)
{
ScriptSensorRequestPacket request = new ScriptSensorRequestPacket();
request.Requester.Arc = arc;
request.Requester.Range = range;
request.Requester.RegionHandle = sim.Handle;
request.Requester.RequestID = requestID;
request.Requester.SearchDir = Quaternion.Identity; // TODO: this needs to be tested
request.Requester.SearchID = searchID;
request.Requester.SearchName = Utils.StringToBytes(name);
request.Requester.SearchPos = Vector3.Zero;
request.Requester.SearchRegions = 0; // TODO: ?
request.Requester.SourceID = Client.Self.AgentID;
request.Requester.Type = (int)type;
Client.Network.SendPacket(request, sim);
}
#endregion Misc
#region Packet Handlers
///
/// 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)
{
InstantMessage message;
message.FromAgentID = im.AgentData.AgentID;
message.FromAgentName = Utils.BytesToString(im.MessageBlock.FromAgentName);
message.ToAgentID = im.MessageBlock.ToAgentID;
message.ParentEstateID = im.MessageBlock.ParentEstateID;
message.RegionID = im.MessageBlock.RegionID;
message.Position = im.MessageBlock.Position;
message.Dialog = (InstantMessageDialog)im.MessageBlock.Dialog;
message.GroupIM = im.MessageBlock.FromGroup;
message.IMSessionID = im.MessageBlock.ID;
message.Timestamp = new DateTime(im.MessageBlock.Timestamp);
message.Message = Utils.BytesToString(im.MessageBlock.Message);
message.Offline = (InstantMessageOnline)im.MessageBlock.Offline;
message.BinaryBucket = im.MessageBlock.BinaryBucket;
try { OnInstantMessage(message, simulator); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
}
///
/// 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 (OnChat != null)
{
ChatFromSimulatorPacket chat = (ChatFromSimulatorPacket)packet;
OnChat(Utils.BytesToString(chat.ChatData.Message)
, (ChatAudibleLevel)chat.ChatData.Audible
, (ChatType)chat.ChatData.ChatType
, (ChatSourceType)chat.ChatData.SourceType
, Utils.BytesToString(chat.ChatData.FromName)
, chat.ChatData.SourceID
, chat.ChatData.OwnerID
, chat.ChatData.Position
);
}
}
///
/// Used for parsing llDialogs
///
/// Incoming ScriptDialog packet
/// Unused
private void ScriptDialogHandler(Packet packet, Simulator simulator)
{
if (OnScriptDialog != null)
{
ScriptDialogPacket dialog = (ScriptDialogPacket)packet;
List buttons = new List();
foreach (ScriptDialogPacket.ButtonsBlock button in dialog.Buttons)
{
buttons.Add(Utils.BytesToString(button.ButtonLabel));
}
OnScriptDialog(Utils.BytesToString(dialog.Data.Message),
Utils.BytesToString(dialog.Data.ObjectName),
dialog.Data.ImageID,
dialog.Data.ObjectID,
Utils.BytesToString(dialog.Data.FirstName),
Utils.BytesToString(dialog.Data.LastName),
dialog.Data.ChatChannel,
buttons);
}
}
///
/// Used for parsing llRequestPermissions dialogs
///
/// Incoming ScriptDialog packet
/// Unused
private void ScriptQuestionHandler(Packet packet, Simulator simulator)
{
if (OnScriptQuestion != null)
{
ScriptQuestionPacket question = (ScriptQuestionPacket)packet;
try
{
OnScriptQuestion(simulator,
question.Data.TaskID,
question.Data.ItemID,
Utils.BytesToString(question.Data.ObjectName),
Utils.BytesToString(question.Data.ObjectOwner),
(ScriptPermission)question.Data.Questions);
}
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
///
/// Handles Script Control changes when Script with permissions releases or takes a control
///
///
///
private void ScriptControlChangeHandler(Packet packet, Simulator simulator)
{
if (OnScriptControlChange != null)
{
ScriptControlChangePacket change = (ScriptControlChangePacket)packet;
for (int i = 0; i < change.Data.Length; i++)
{
try
{
OnScriptControlChange((ScriptControlChange)change.Data[i].Controls,
change.Data[i].PassToAgent,
change.Data[i].TakeControls);
}
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
}
///
/// Used for parsing llLoadURL Dialogs
///
///
///
private void LoadURLHandler(Packet packet, Simulator simulator)
{
LoadURLPacket loadURL = (LoadURLPacket)packet;
if (OnLoadURL != null)
{
try {
OnLoadURL(
Utils.BytesToString(loadURL.Data.ObjectName),
loadURL.Data.ObjectID,
loadURL.Data.OwnerID,
loadURL.Data.OwnerIsGroup,
Utils.BytesToString(loadURL.Data.Message),
Utils.BytesToString(loadURL.Data.URL)
);
}
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
///
/// Update client's Position, LookAt and region handle from incoming packet
///
/// Incoming AgentMovementCompletePacket
/// Unused
/// This occurs when after an avatar moves into a new sim
private void MovementCompleteHandler(Packet packet, Simulator simulator)
{
AgentMovementCompletePacket movement = (AgentMovementCompletePacket)packet;
relativePosition = movement.Data.Position;
Movement.Camera.LookDirection(movement.Data.LookAt);
simulator.Handle = movement.Data.RegionHandle;
simulator.SimVersion = Utils.BytesToString(movement.SimData.ChannelVersion);
}
///
/// Update Client Avatar's health via incoming packet
///
/// Incoming HealthMessagePacket
/// Unused
private void HealthHandler(Packet packet, Simulator simulator)
{
health = ((HealthMessagePacket)packet).HealthData.Health;
}
private void AgentDataUpdateHandler(Packet packet, Simulator simulator)
{
AgentDataUpdatePacket p = (AgentDataUpdatePacket)packet;
if (p.AgentData.AgentID == simulator.Client.Self.AgentID)
{
firstName = Utils.BytesToString(p.AgentData.FirstName);
lastName = Utils.BytesToString(p.AgentData.LastName);
activeGroup = p.AgentData.ActiveGroupID;
if (OnAgentDataUpdated != null)
{
string groupTitle = Utils.BytesToString(p.AgentData.GroupTitle);
string groupName = Utils.BytesToString(p.AgentData.GroupName);
try { OnAgentDataUpdated(firstName, lastName, activeGroup, groupTitle, (GroupPowers)p.AgentData.GroupPowers, groupName); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
else
{
Logger.Log("Got an AgentDataUpdate packet for avatar " + p.AgentData.AgentID.ToString() +
" instead of " + Client.Self.AgentID.ToString() + ", this shouldn't happen", Helpers.LogLevel.Error, Client);
}
}
///
/// Update Client Avatar's L$ balance from incoming packet
///
/// Incoming MoneyBalanceReplyPacket
/// Unused
private void BalanceHandler(Packet packet, Simulator simulator)
{
if (packet.Type == PacketType.MoneyBalanceReply)
{
MoneyBalanceReplyPacket mbrp = (MoneyBalanceReplyPacket)packet;
balance = mbrp.MoneyData.MoneyBalance;
if (OnMoneyBalanceReplyReceived != null)
{
try { OnMoneyBalanceReplyReceived(mbrp.MoneyData.TransactionID,
mbrp.MoneyData.TransactionSuccess, mbrp.MoneyData.MoneyBalance,
mbrp.MoneyData.SquareMetersCredit, mbrp.MoneyData.SquareMetersCommitted,
Utils.BytesToString(mbrp.MoneyData.Description)); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
if (OnBalanceUpdated != null)
{
try { OnBalanceUpdated(balance); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
private void EstablishAgentCommunicationEventHandler(string message, LLSD llsd, Simulator simulator)
{
StructuredData.LLSDMap body = (StructuredData.LLSDMap)llsd;
if (Client.Settings.MULTIPLE_SIMS && body.ContainsKey("sim-ip-and-port"))
{
string ipAndPort = body["sim-ip-and-port"].AsString();
string[] pieces = ipAndPort.Split(':');
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(pieces[0]), Convert.ToInt32(pieces[1]));
Simulator sim = Client.Network.FindSimulator(endPoint);
if (sim == null)
{
Logger.Log("Got EstablishAgentCommunication for unknown sim " + ipAndPort,
Helpers.LogLevel.Error, Client);
// FIXME: Should we use this opportunity to connect to the simulator?
}
else
{
Logger.Log("Got EstablishAgentCommunication for " + sim.ToString(),
Helpers.LogLevel.Info, Client);
sim.SetSeedCaps(body["seed-capability"].AsString());
}
}
}
///
/// Handler for teleport Requests
///
/// Incoming TeleportHandler packet
/// Simulator sending teleport information
private void TeleportHandler(Packet packet, Simulator simulator)
{
bool finished = false;
TeleportFlags flags = TeleportFlags.Default;
if (packet.Type == PacketType.TeleportStart)
{
TeleportStartPacket start = (TeleportStartPacket)packet;
teleportMessage = "Teleport started";
flags = (TeleportFlags)start.Info.TeleportFlags;
teleportStat = TeleportStatus.Start;
Logger.DebugLog("TeleportStart received, Flags: " + flags.ToString(), Client);
}
else if (packet.Type == PacketType.TeleportProgress)
{
TeleportProgressPacket progress = (TeleportProgressPacket)packet;
teleportMessage = Utils.BytesToString(progress.Info.Message);
flags = (TeleportFlags)progress.Info.TeleportFlags;
teleportStat = TeleportStatus.Progress;
Logger.DebugLog("TeleportProgress received, Message: " + teleportMessage + ", Flags: " + flags.ToString(), Client);
}
else if (packet.Type == PacketType.TeleportFailed)
{
TeleportFailedPacket failed = (TeleportFailedPacket)packet;
teleportMessage = Utils.BytesToString(failed.Info.Reason);
teleportStat = TeleportStatus.Failed;
finished = true;
Logger.DebugLog("TeleportFailed received, Reason: " + teleportMessage, Client);
}
else if (packet.Type == PacketType.TeleportFinish)
{
TeleportFinishPacket finish = (TeleportFinishPacket)packet;
flags = (TeleportFlags)finish.Info.TeleportFlags;
string seedcaps = Utils.BytesToString(finish.Info.SeedCapability);
finished = true;
Logger.DebugLog("TeleportFinish received, Flags: " + flags.ToString(), Client);
// Connect to the new sim
Simulator newSimulator = Client.Network.Connect(new IPAddress(finish.Info.SimIP),
finish.Info.SimPort, finish.Info.RegionHandle, true, seedcaps);
if (newSimulator != null)
{
teleportMessage = "Teleport finished";
teleportStat = TeleportStatus.Finished;
// Disconnect from the previous sim
Client.Network.DisconnectSim(simulator, true);
Logger.Log("Moved to new sim " + newSimulator.ToString(), Helpers.LogLevel.Info, Client);
}
else
{
teleportMessage = "Failed to connect to the new sim after a teleport";
teleportStat = TeleportStatus.Failed;
// We're going to get disconnected now
Logger.Log(teleportMessage, Helpers.LogLevel.Error, Client);
}
}
else if (packet.Type == PacketType.TeleportCancel)
{
//TeleportCancelPacket cancel = (TeleportCancelPacket)packet;
teleportMessage = "Cancelled";
teleportStat = TeleportStatus.Cancelled;
finished = true;
Logger.DebugLog("TeleportCancel received from " + simulator.ToString(), Client);
}
else if (packet.Type == PacketType.TeleportLocal)
{
TeleportLocalPacket local = (TeleportLocalPacket)packet;
teleportMessage = "Teleport finished";
flags = (TeleportFlags)local.Info.TeleportFlags;
teleportStat = TeleportStatus.Finished;
relativePosition = local.Info.Position;
Movement.Camera.LookDirection(local.Info.LookAt);
// This field is apparently not used for anything
//local.Info.LocationID;
finished = true;
Logger.DebugLog("TeleportLocal received, Flags: " + flags.ToString(), Client);
}
if (OnTeleport != null)
{
try { OnTeleport(teleportMessage, teleportStat, flags); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
if (finished) teleportEvent.Set();
}
///
///
///
///
///
private void AvatarAnimationHandler(Packet packet, Simulator sim)
{
AvatarAnimationPacket animation = (AvatarAnimationPacket)packet;
if (animation.Sender.ID == Client.Self.AgentID)
{
lock (SignaledAnimations.Dictionary)
{
// Reset the signaled animation list
SignaledAnimations.Dictionary.Clear();
for (int i = 0; i < animation.AnimationList.Length; i++)
{
UUID animID = animation.AnimationList[i].AnimID;
int sequenceID = animation.AnimationList[i].AnimSequenceID;
// Add this animation to the list of currently signaled animations
SignaledAnimations.Dictionary[animID] = sequenceID;
if (i < animation.AnimationSourceList.Length)
{
// FIXME: The server tells us which objects triggered our animations,
// we should store this info
//animation.AnimationSourceList[i].ObjectID
}
if (i < animation.PhysicalAvatarEventList.Length)
{
// FIXME: What is this?
}
if (Client.Settings.SEND_AGENT_UPDATES)
{
// We have to manually tell the server to stop playing some animations
if (animID == Animations.STANDUP ||
animID == Animations.PRE_JUMP ||
animID == Animations.LAND ||
animID == Animations.MEDIUM_LAND)
{
Movement.FinishAnim = true;
Movement.SendUpdate(true);
}
}
}
}
if (OnAnimationsChanged != null)
{
try { OnAnimationsChanged(SignaledAnimations); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
}
private void MeanCollisionAlertHandler(Packet packet, Simulator sim)
{
if (OnMeanCollision != null)
{
MeanCollisionAlertPacket collision = (MeanCollisionAlertPacket)packet;
for (int i = 0; i < collision.MeanCollision.Length; i++)
{
MeanCollisionAlertPacket.MeanCollisionBlock block = collision.MeanCollision[i];
DateTime time = Utils.UnixTimeToDateTime(block.Time);
MeanCollisionType type = (MeanCollisionType)block.Type;
try { OnMeanCollision(type, block.Perp, block.Victim, block.Mag, time); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
}
private void Network_OnLoginResponse(bool loginSuccess, bool redirect, string message, string reason,
LoginResponseData reply)
{
id = reply.AgentID;
sessionID = reply.SessionID;
secureSessionID = reply.SecureSessionID;
firstName = reply.FirstName;
lastName = reply.LastName;
startLocation = reply.StartLocation;
agentAccess = reply.AgentAccess;
Movement.Camera.LookDirection(reply.LookAt);
homePosition = reply.HomePosition;
homeLookAt = reply.HomeLookAt;
}
private void Network_OnDisconnected(NetworkManager.DisconnectType reason, string message)
{
// Null out the cached fullName since it can change after logging
// in again (with a different account name or different login
// server but using the same GridClient object
fullName = null;
}
///
/// Allows agent to cross over (walk, fly, vehicle) in to neighboring
/// simulators
///
private void CrossedRegionHandler(Packet packet, Simulator sim)
{
CrossedRegionPacket crossing = (CrossedRegionPacket)packet;
string seedCap = Utils.BytesToString(crossing.RegionData.SeedCapability);
IPEndPoint endPoint = new IPEndPoint(crossing.RegionData.SimIP, crossing.RegionData.SimPort);
Logger.DebugLog("Crossed in to new region area, attempting to connect to " + endPoint.ToString(), Client);
Simulator oldSim = Client.Network.CurrentSim;
Simulator newSim = Client.Network.Connect(endPoint, crossing.RegionData.RegionHandle, true, seedCap);
if (newSim != null)
{
Logger.Log("Finished crossing over in to region " + newSim.ToString(), Helpers.LogLevel.Info, Client);
if (OnRegionCrossed != null)
{
try { OnRegionCrossed(oldSim, newSim); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
else
{
// The old simulator will (poorly) handle our movement still, so the connection isn't
// completely shot yet
Logger.Log("Failed to connect to new region " + endPoint.ToString() + " after crossing over",
Helpers.LogLevel.Warning, Client);
}
}
///
/// Group Chat event handler
///
/// The capability Key
///
///
private void ChatterBoxSessionEventHandler(string capsKey, LLSD llsd, Simulator simulator)
{
LLSDMap map = (LLSDMap)llsd;
if (map["success"].AsBoolean() != true)
{
Logger.Log("Attempt to send group chat to non-existant session for group " + map["session_id"].AsString(),
Helpers.LogLevel.Info, Client);
}
}
///
/// Response from request to join a group chat
///
///
///
///
private void ChatterBoxSessionStartReplyHandler(string capsKey, LLSD llsd, Simulator simulator)
{
LLSDMap map = (LLSDMap)llsd;
UUID sessionID = map["session_id"].AsUUID();
UUID tmpSessionID = map["temp_session_id"].AsUUID();
bool success = map["success"].AsBoolean();
if (success)
{
LLSDArray agentlist = (LLSDArray)map["agents"];
List agents = new List();
foreach (LLSD id in agentlist)
agents.Add(id.AsUUID());
lock (GroupChatSessions.Dictionary)
{
if (GroupChatSessions.ContainsKey(sessionID))
GroupChatSessions.Dictionary[sessionID] = agents;
else
GroupChatSessions.Add(sessionID, agents);
}
}
if (OnGroupChatJoin != null)
{
try { OnGroupChatJoin(sessionID, tmpSessionID, success); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
///
/// Someone joined or left group chat
///
///
///
///
private void ChatterBoxSessionAgentListReplyHandler(string capsKey, LLSD llsd, Simulator simulator)
{
LLSDMap map = (LLSDMap)llsd;
UUID sessionID = map["session_id"].AsUUID();
LLSDMap update = (LLSDMap)map["updates"];
string errormsg = map["error"].AsString();
//if (errormsg.Equals("already in session"))
// return;
foreach (KeyValuePair kvp in update)
{
if (kvp.Value.Equals("ENTER"))
{
lock (GroupChatSessions.Dictionary)
{
if (!GroupChatSessions.Dictionary[sessionID].Contains((UUID)kvp.Key))
GroupChatSessions.Dictionary[sessionID].Add((UUID)kvp.Key);
}
}
else if (kvp.Value.Equals("LEAVE"))
{
lock (GroupChatSessions.Dictionary)
{
if (GroupChatSessions.Dictionary[sessionID].Contains((UUID)kvp.Key))
GroupChatSessions.Dictionary[sessionID].Remove((UUID)kvp.Key);
// we left session, remove from dictionary
if (kvp.Key.Equals(Client.Self.id) && OnGroupChatLeft != null)
{
GroupChatSessions.Dictionary.Remove(sessionID);
OnGroupChatLeft(sessionID);
}
}
}
}
}
///
/// Group Chat Request
///
/// Caps Key
/// LLSD Map containing invitation
/// Originating Simulator
private void ChatterBoxInvitationHandler(string capsKey, LLSD llsd, Simulator simulator)
{
if (OnInstantMessage != null)
{
LLSDMap map = (LLSDMap)llsd;
LLSDMap im = (LLSDMap)map["instantmessage"];
LLSDMap agent = (LLSDMap)im["agent_params"];
LLSDMap msg = (LLSDMap)im["message_params"];
LLSDMap msgdata = (LLSDMap)msg["data"];
InstantMessage message = new InstantMessage();
message.FromAgentID = map["from_id"].AsUUID();
message.FromAgentName = map["from_name"].AsString();
message.ToAgentID = msg["to_id"].AsString();
message.ParentEstateID = (uint)msg["parent_estate_id"].AsInteger();
message.RegionID = msg["region_id"].AsUUID();
message.Position = ((LLSDArray)msg["position"]).AsVector3();
message.Dialog = (InstantMessageDialog)msgdata["type"].AsInteger();
message.GroupIM = true;
message.IMSessionID = map["session_id"].AsUUID();
message.Timestamp = new DateTime(msgdata["timestamp"].AsInteger());
message.Message = msg["message"].AsString();
message.Offline = (InstantMessageOnline)msg["offline"].AsInteger();
message.BinaryBucket = msg["binary_bucket"].AsBinary();
try { OnInstantMessage(message, simulator); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
///
/// Alert Message packet handler
///
/// AlertMessagePacket
/// not used
private void AlertMessageHandler(Packet packet, Simulator simulator)
{
AlertMessagePacket alert = (AlertMessagePacket)packet;
string message = Utils.BytesToString(alert.AlertData.Message);
if (OnAlertMessage != null)
{
try { OnAlertMessage(message); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
///
/// detects camera constraint collisions
///
///
///
private void CameraConstraintHandler(Packet packet, Simulator simulator)
{
if (OnCameraConstraint != null)
{
CameraConstraintPacket camera = (CameraConstraintPacket)packet;
try { OnCameraConstraint(camera.CameraCollidePlane.Plane); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
///
/// Packet handler for ScriptSensorReply packet
///
///
///
private void ScriptSensorReplyHandler(Packet packet, Simulator simulator)
{
if (OnScriptSensorReply != null)
{
ScriptSensorReplyPacket reply = (ScriptSensorReplyPacket)packet;
for (int i = 0; i < reply.SensedData.Length; i++)
{
ScriptSensorReplyPacket.SensedDataBlock block = reply.SensedData[i];
ScriptSensorReplyPacket.RequesterBlock requestor = reply.Requester;
try { OnScriptSensorReply(requestor.SourceID, block.GroupID, Utils.BytesToString(block.Name),
block.ObjectID, block.OwnerID, block.Position, block.Range, block.Rotation, (ScriptSensorTypeFlags)block.Type, block.Velocity); }
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
}
///
/// Packet handler for AvatarSitResponse packet
///
///
///
private void AvatarSitResponseHandler(Packet packet, Simulator simulator)
{
if (OnAvatarSitResponse != null)
{
AvatarSitResponsePacket sit = (AvatarSitResponsePacket)packet;
try
{
OnAvatarSitResponse(sit.SitObject.ID, sit.SitTransform.AutoPilot, sit.SitTransform.CameraAtOffset,
sit.SitTransform.CameraEyeOffset, sit.SitTransform.ForceMouselook, sit.SitTransform.SitPosition,
sit.SitTransform.SitRotation);
}
catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
}
}
#endregion Packet Handlers
}
}