diff --git a/pyogp/lib/base/agent.py b/pyogp/lib/base/agent.py index a6ace75..9a2ec9e 100644 --- a/pyogp/lib/base/agent.py +++ b/pyogp/lib/base/agent.py @@ -33,6 +33,7 @@ from pyogp.lib.base.login import Login, LegacyLoginParams, OGPLoginParams from pyogp.lib.base.exc import LoginError from pyogp.lib.base.region import Region from pyogp.lib.base.inventory import Inventory +from pyogp.lib.base.groups import GroupManager, Group # from pyogp.lib.base.appearance import Appearance # pyogp messaging @@ -95,6 +96,8 @@ class Agent(object): self.udp_blacklist = None self.home = None self.inventory = None + self.group_manager = GroupManager(self, self.settings) + # additional attributes self.login_response = None @@ -106,7 +109,7 @@ class Agent(object): self.helpers = Helpers() # data we store as it comes in from the grid - self.Position = None + self.Position = (0.0, 0.0, 0.0) # this will get updated later, but seed it with 000 # should we include these here? self.agentdomain = None # the agent domain the agent is connected to if an OGP context @@ -128,6 +131,9 @@ class Agent(object): onHealthMessage_received = self.packet_handler._register('HealthMessage') onHealthMessage_received.subscribe(onHealthMessage, self) + onAgentGroupDataUpdate_received = self.packet_handler._register('AgentGroupDataUpdate') + onAgentGroupDataUpdate_received.subscribe(onAgentGroupDataUpdate, self) + if self.settings.ENABLE_COMMUNICATIONS_TRACKING: onChatFromSimulator_received = self.packet_handler._register('ChatFromSimulator') @@ -417,7 +423,53 @@ def onAgentMovementComplete(packet, agent): def onHealthMessage(packet, agent): - agent.health = packet.message_data.blocks['HealthData'][0].get_variable('Health') + agent.health = packet.message_data.blocks['HealthData'][0].get_variable('Health').data + +def onAgentGroupDataUpdate(packet, agent): + + # AgentData block + AgentID = packet.message_data.blocks['AgentData'][0].get_variable('AgentID') + + # GroupData block + for GroupData_block in packet.message_data.blocks['GroupData']: + + AcceptNotices = GroupData_block.get_variable('AcceptNotices').data + GroupPowers = GroupData_block.get_variable('GroupPowers').data + GroupID = uuid.UUID(str(GroupData_block.get_variable('GroupID').data)) + GroupName = GroupData_block.get_variable('GroupName').data + ListInProfile = GroupData_block.get_variable('ListInProfile').data + Contribution = GroupData_block.get_variable('Contribution').data + GroupInsigniaID = uuid.UUID(str(GroupData_block.get_variable('GroupInsigniaID').data)) + + # make sense of group powers + GroupPowers = [ord(x) for x in GroupPowers] + GroupPowers = ''.join([str(x) for x in GroupPowers]) + + group = Group(AcceptNotices, GroupPowers, GroupID, GroupName, ListInProfile, Contribution,GroupInsigniaID ) + + agent.group_manager.store_group(group) + + ''' + Name: AgentGroupDataUpdate + Block Name: GroupData + AcceptNotices: True + GroupPowers: ? + GroupID: 69fd708c-3f20-a01b-f9b5-b5c4b310e5ca + GroupName: EnusBot Army + ListInProfile: False + Contribution: 0 + GroupInsigniaID: 00000000-0000-0000-0000-000000000000 + AcceptNotices: True + GroupPowers: ? + GroupID: 69fd708c-3f20-a01b-f9b5-b5c4b310e5ca + GroupName: EnusBot Army + ListInProfile: False + Contribution: 0 + GroupInsigniaID: 00000000-0000-0000-0000-000000000000 + Block Name: AgentData + AgentID: a517168d-1af5-4854-ba6d-672c8a59e439 + + ''' def onChatFromSimulator(packet, agent): diff --git a/pyogp/lib/base/event_queue.py b/pyogp/lib/base/event_queue.py index ce02edd..1ff2ef8 100644 --- a/pyogp/lib/base/event_queue.py +++ b/pyogp/lib/base/event_queue.py @@ -315,7 +315,7 @@ class EventQueueHandler(object): if self.settings.LOG_VERBOSE: log(DEBUG, 'Creating a monitor for %s' % (name)) - return self.handlers.setdefault(name, EventQueueReceivedNotifier(name)) + return self.handlers.setdefault(name, EventQueueReceivedNotifier(name, self.settings)) def is_packet_handled(self, name): """ if the data is being monitored, return True, otherwise, return False @@ -352,9 +352,10 @@ class EventQueueHandler(object): class EventQueueReceivedNotifier(object): """ received TestMessage packet """ - def __init__(self, name): + def __init__(self, name, settings): self.event = Event() self.name = name + self.settings = settings def subscribe(self, *args, **kwdargs): self.event.subscribe(*args, **kwdargs) @@ -363,6 +364,11 @@ class EventQueueReceivedNotifier(object): self.event(data) + def unsubscribe(self, *args, **kwdargs): + self.event.unsubscribe(*args, **kwdargs) + + if self.settings.LOG_VERBOSE: log(DEBUG, "Removed the monitor for %s by %s" % (args, kwdargs)) + def __len__(self): return len(self.event) diff --git a/pyogp/lib/base/examples/sample_group_creation.py b/pyogp/lib/base/examples/sample_group_creation.py new file mode 100644 index 0000000..e602ea0 --- /dev/null +++ b/pyogp/lib/base/examples/sample_group_creation.py @@ -0,0 +1,121 @@ +#!/usr/bin/python +""" +@file sample_group_creation.py +@date 2009-03-05 +Contributors can be viewed at: +http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt + +$LicenseInfo:firstyear=2008&license=apachev2$ + +Copyright 2008, Linden Research, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"). +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 +or in +http://svn.secondlife.com/svn/linden/projects/2008/pyogp/LICENSE.txt + +$/LicenseInfo$ +""" + +# standard +import re +import getpass, sys, logging +from optparse import OptionParser +import uuid + +# related +from eventlet import api + +# pyogp +from pyogp.lib.base.agent import Agent +from pyogp.lib.base.settings import Settings + + +def login(): + """ login an to a login endpoint """ + + parser = OptionParser() + + logger = logging.getLogger("pyogp.lib.base.example") + + parser.add_option("-l", "--loginuri", dest="loginuri", default="https://login.aditi.lindenlab.com/cgi-bin/login.cgi", + help="specified the target loginuri") + parser.add_option("-r", "--region", dest="region", default=None, + help="specifies the region to connect to") +#http://ec2-75-101-203-98.compute-1.amazonaws.com:9000 + parser.add_option("-q", "--quiet", dest="verbose", default=True, action="store_false", + help="enable verbose mode") + + + (options, args) = parser.parse_args() + + if options.verbose: + console = logging.StreamHandler() + console.setLevel(logging.DEBUG) # seems to be a no op, set it for the logger + formatter = logging.Formatter('%(asctime)-30s%(name)-30s: %(levelname)-8s %(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + # setting the level for the handler above seems to be a no-op + # it needs to be set for the logger, here the root logger + # otherwise it is NOTSET(=0) which means to log nothing. + logging.getLogger('').setLevel(logging.DEBUG) + else: + print "Attention: This script will print nothing if you use -q. So it might be boring to use it like that ;-)" + + # example from a pure agent perspective + + print "Attention: Creating a group will cost you 100 Lindens. Stop now if you don't like the sound of that." + #grab a password! + password = getpass.getpass() + + # let's disable inventory handling for this example + settings = Settings() + settings.ENABLE_INVENTORY_MANAGEMENT = False + settings.ENABLE_OBJECT_TRACKING = False + settings.ENABLE_COMMUNICATIONS_TRACKING = False + settings.ENABLE_UDP_LOGGING = False + settings.ENABLE_EQ_LOGGING = False + + #First, initialize the agent + client = Agent(settings = settings) + + # Now let's log it in + api.spawn(client.login, options.loginuri, args[0], args[1], password, start_location = options.region, connect_region = True) + + # wait for the agent to connect to it's region + while client.connected == False: + api.sleep(0) + + while client.region.connected == False: + api.sleep(0) + + # do sample script specific stuff here + + # use a random uuid for a good chance at having success with creating a group + # Warning: this will cost you 100 Lindens + group_name = str(uuid.uuid4()) + client.group_manager.create_group(Name = group_name.split('-')[4]) + + while client.running: + api.sleep(0) + + print '' + print '' + print 'At this point, we have an Agent object, Inventory dirs, and with a Region attribute' + print 'Agent attributes:' + for attr in client.__dict__: + print attr, ':\t\t\t', client.__dict__[attr] + print '' + print '' + print 'Known Groups:' + for group in client.group_manager.group_store: + print ':\t\t\t', group.GroupName + +def main(): + return login() + +if __name__=="__main__": + main() diff --git a/pyogp/lib/base/examples/sample_object_creation.py b/pyogp/lib/base/examples/sample_object_creation.py index 850ec02..c015e60 100644 --- a/pyogp/lib/base/examples/sample_object_creation.py +++ b/pyogp/lib/base/examples/sample_object_creation.py @@ -110,7 +110,7 @@ def login(): print '' print '' for _object in client.region.objects.object_store: - print 'ID:', _object.ID, '\tUUID: ', _object.FullID + print 'ID:', _object.ID, '\tUUID: ', _object.FullID, '\tOwnerID: ', _object.OwnerID print '' print '' print 'Region attributes:' diff --git a/pyogp/lib/base/examples/sample_object_tracking.py b/pyogp/lib/base/examples/sample_object_tracking.py index 3284857..e0ea945 100644 --- a/pyogp/lib/base/examples/sample_object_tracking.py +++ b/pyogp/lib/base/examples/sample_object_tracking.py @@ -104,7 +104,7 @@ def login(): print '' print '' for _object in client.region.objects.object_store: - print 'ID:', _object.ID, '\tUUID: ', _object.FullID + print 'ID:', _object.ID, '\tUUID: ', _object.FullID , '\tUUID: ', _object.OwnerID print '' print '' print 'Region attributes:' diff --git a/pyogp/lib/base/examples/sample_region_connect.py b/pyogp/lib/base/examples/sample_region_connect.py index 1add783..2ee5c32 100644 --- a/pyogp/lib/base/examples/sample_region_connect.py +++ b/pyogp/lib/base/examples/sample_region_connect.py @@ -66,8 +66,15 @@ def login(): #grab a password! password = getpass.getpass() + settings = Settings() + settings.ENABLE_INVENTORY_MANAGEMENT = False + settings.ENABLE_OBJECT_TRACKING = False + settings.ENABLE_COMMUNICATIONS_TRACKING = False + settings.ENABLE_UDP_LOGGING = False + settings.ENABLE_EQ_LOGGING = False + #First, initialize the agent - client = Agent() + client = Agent(settings) # In this example, let's disable inventory handling client.settings.ENABLE_INVENTORY_MANAGEMENT = False diff --git a/pyogp/lib/base/exc.py b/pyogp/lib/base/exc.py index e0c0b1c..825cd14 100644 --- a/pyogp/lib/base/exc.py +++ b/pyogp/lib/base/exc.py @@ -37,7 +37,7 @@ class LoginError(Error): self.error = error def __str__(self): - return "Agent credential error: %s" % (self.error) + return "Agent login error: %s" % (self.error) class ParseStartLocError(LoginError): """ raised when the is an error in parsing the user specified start location """ diff --git a/pyogp/lib/base/groups.py b/pyogp/lib/base/groups.py index 80107b5..77372e2 100644 --- a/pyogp/lib/base/groups.py +++ b/pyogp/lib/base/groups.py @@ -19,11 +19,118 @@ $/LicenseInfo$ """ # standard python libs +from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG import uuid -class Group(object): +# pyogp +from pyogp.lib.base.exc import DataParsingError + +# pyogp messaging +from pyogp.lib.base.message.packets import CreateGroupRequestPacket + +# initialize logging +logger = getLogger('pyogp.lib.base.groups') +log = logger.log + +class GroupManager(object): + """ a storage bin for groups - pass + also, a functional area for group creation operations + """ + + def __init__(self, agent, settings = None): + """ initialize the group manager """ + + # allow the settings to be passed in + # otherwise, grab the defaults + if settings != None: + self.settings = settings + else: + from pyogp.lib.base.settings import Settings + self.settings = Settings() + + self.agent = agent + + # the group store consists of a list + # of Group() instances + self.group_store = [] + + # ~~~~~~~~~ + # Callbacks + # ~~~~~~~~~ + + if self.settings.LOG_VERBOSE: log(DEBUG, "Initialized the Group Manager") + + def store_group(self, _group): + """ append to or replace a group in self.group_store """ + + # replace an existing list member, else, append + + try: + + index = [self.group_store.index(_object_) for _group_ in self.group_store if _group_.ID == _group.GroupID] + + self.group_store[index[0]] = _group + + if self.settings.LOG_VERBOSE: log(DEBUG, 'Replacing a stored group: \'%s\'' % (_group.GroupID)) + + except: + + self.group_store.append(_group) + + if self.settings.LOG_VERBOSE: log(DEBUG, 'Stored a new group: \'%s\'' % (_group.GroupID)) + + def create_group(self, AgentID = None, SessionID = None, Name = None, Charter = '', ShowInList = True, InsigniaID = uuid.UUID('00000000-0000-0000-0000-000000000000'), MembershipFee = 0, OpenEnrollment = False, AllowPublish = False, MaturePublish = False): + """ sends a message to the agent's current region requesting to create a group + + enables a callback (which should be unsubscribed from once we get a response) + """ + + if Name != None: + + log(INFO, "Sending a request to create group with a name of \'%s\'" % (Name)) + + if AgentID == None: AgentID = self.agent.agent_id + if SessionID == None: SessionID = self.agent.session_id + + packet = CreateGroupRequestPacket() + + # populate the AgentData block + packet.AgentData['AgentID'] = uuid.UUID(str(AgentID)) # MVT_LLUUID + packet.AgentData['SessionID'] = uuid.UUID(str(SessionID)) # MVT_LLUUID + + # populate the GroupData block + packet.GroupData['Name'] = Name # MVT_VARIABLE + packet.GroupData['Charter'] = Charter # MVT_VARIABLE + packet.GroupData['ShowInList'] = ShowInList # MVT_BOOL + packet.GroupData['InsigniaID'] = uuid.UUID(str(InsigniaID)) # MVT_LLUUID + packet.GroupData['MembershipFee'] = MembershipFee # MVT_S32 + packet.GroupData['OpenEnrollment'] = OpenEnrollment # MVT_BOOL + packet.GroupData['AllowPublish'] = AllowPublish # MVT_BOOL + packet.GroupData['MaturePublish'] = MaturePublish # MVT_BOOL + + self.agent.region.enqueue_message(packet(), True) + + if self.settings.HANDLE_PACKETS: + # enable the callback to watch for the CreateGroupReply packet + self.onCreateGroupReply_received = self.agent.packet_handler._register('CreateGroupReply') + self.onCreateGroupReply_received.subscribe(onCreateGroupReply, self) + else: + + raise DataParsingError('Failed to create a group, please specify a name') + +class Group(object): + """ representation of a group """ + + def __init__(self, AcceptNotices, GroupPowers, GroupID, GroupName, ListInProfile, Contribution, GroupInsigniaID): + + self.AcceptNotices =AcceptNotices + self.GroupPowers = GroupPowers + self.GroupID = uuid.UUID(str(GroupID)) + self.GroupName = GroupName + self.ListInProfile = ListInProfile + self.Contribution = Contribution + self.GroupInsigniaID = uuid.UUID(str(GroupInsigniaID)) class ChatterBoxInvitation_Message(object): """ a group chat message sent over the event queue """ @@ -87,3 +194,852 @@ class ChatterBoxInvitation_Message(object): #self.message = message self.name = 'ChatterBoxInvitation' + +# ~~~~~~~~~~~~~~~~~~ +# Callback functions +# ~~~~~~~~~~~~~~~~~~ + +def onCreateGroupReply(packet, group_manager): + """ when we get a CreateGroupReply packet, log Success, and if True, request the group details. remove the callback in any case """ + + # remove the monitor + group_manager.onCreateGroupReply_received.unsubscribe(onCreateGroupReply, group_manager) + + AgentID = packet.message_data.blocks['AgentData'][0].get_variable('AgentID').data + GroupID = packet.message_data.blocks['ReplyData'][0].get_variable('GroupID').data + Success = packet.message_data.blocks['ReplyData'][0].get_variable('Success').data + Message = packet.message_data.blocks['ReplyData'][0].get_variable('Message').data + + if Success: + log(INFO, "Created group %s. Message data is: %s" % (GroupID, Message)) + log(WARNING, "We now need to request the group data...") + else: + log(INFO, "Failed to create group due to: %s" % (Message)) + +''' +Groups related messages: + +//----------------------------------------------------------------------------- +// Group messages +//----------------------------------------------------------------------------- + + // CreateGroupRequest + // viewer -> simulator + // simulator -> dataserver + // reliable + { + CreateGroupRequest Low 339 NotTrusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { Name Variable 1 } // string + { Charter Variable 2 } // string + { ShowInList BOOL } + { InsigniaID LLUUID } + { MembershipFee S32 } // S32 + { OpenEnrollment BOOL } // BOOL (U8) + { AllowPublish BOOL } // whether profile is externally visible or not + { MaturePublish BOOL } // profile is "mature" + } + } + + // CreateGroupReply + // dataserver -> simulator + // simulator -> viewer + // reliable + { + CreateGroupReply Low 340 Trusted Unencoded + { + AgentData Single + { AgentID LLUUID } + } + { + ReplyData Single + { GroupID LLUUID } + { Success BOOL } + { Message Variable 1 } // string + } + } + +// UpdateGroupInfo +// viewer -> simulator +// simulator -> dataserver +// reliable +{ + UpdateGroupInfo Low 341 NotTrusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + { Charter Variable 2 } // string + { ShowInList BOOL } + { InsigniaID LLUUID } + { MembershipFee S32 } + { OpenEnrollment BOOL } + { AllowPublish BOOL } + { MaturePublish BOOL } + } +} + +// GroupRoleChanges +// viewer -> simulator -> dataserver +// reliable +{ + GroupRoleChanges Low 342 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + { GroupID LLUUID } + } + { + RoleChange Variable + { RoleID LLUUID } + { MemberID LLUUID } + { Change U32 } + } +} + +// JoinGroupRequest +// viewer -> simulator -> dataserver +// reliable +{ + JoinGroupRequest Low 343 NotTrusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + } +} + +// JoinGroupReply +// dataserver -> simulator -> viewer +{ + JoinGroupReply Low 344 Trusted Unencoded + { + AgentData Single + { AgentID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + { Success BOOL } + } +} + + +// EjectGroupMemberRequest +// viewer -> simulator -> dataserver +// reliable +{ + EjectGroupMemberRequest Low 345 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + } + { + EjectData Variable + { EjecteeID LLUUID } + } +} + +// EjectGroupMemberReply +// dataserver -> simulator -> viewer +// reliable +{ + EjectGroupMemberReply Low 346 Trusted Unencoded + { + AgentData Single + { AgentID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + } + { + EjectData Single + { Success BOOL } + } +} + +// LeaveGroupRequest +// viewer -> simulator -> dataserver +// reliable +{ + LeaveGroupRequest Low 347 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + } +} + +// LeaveGroupReply +// dataserver -> simulator -> viewer +{ + LeaveGroupReply Low 348 Trusted Unencoded + { + AgentData Single + { AgentID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + { Success BOOL } + } +} + +// InviteGroupRequest +// viewer -> simulator -> dataserver +// reliable +{ + InviteGroupRequest Low 349 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } // UUID of inviting agent + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + } + { + InviteData Variable + { InviteeID LLUUID } + { RoleID LLUUID } + } +} + +// InviteGroupResponse +// simulator -> dataserver +// reliable +{ + InviteGroupResponse Low 350 Trusted Unencoded + { + InviteData Single + { AgentID LLUUID } + { InviteeID LLUUID } + { GroupID LLUUID } + { RoleID LLUUID } + { MembershipFee S32 } + } +} + +// GroupProfileRequest +// viewer-> simulator -> dataserver +// reliable +{ + GroupProfileRequest Low 351 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + } +} + +// GroupProfileReply +// dataserver -> simulator -> viewer +// reliable +{ + GroupProfileReply Low 352 Trusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + { Name Variable 1 } // string + { Charter Variable 2 } // string + { ShowInList BOOL } + { MemberTitle Variable 1 } // string + { PowersMask U64 } // U32 mask + { InsigniaID LLUUID } + { FounderID LLUUID } + { MembershipFee S32 } + { OpenEnrollment BOOL } // BOOL (U8) + { Money S32 } + { GroupMembershipCount S32 } + { GroupRolesCount S32 } + { AllowPublish BOOL } + { MaturePublish BOOL } + { OwnerRole LLUUID } + } +} + +// CurrentInterval = 0 => this period (week, day, etc.) +// CurrentInterval = 1 => last period +// viewer -> simulator -> dataserver +// reliable +{ + GroupAccountSummaryRequest Low 353 NotTrusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + { GroupID LLUUID } + } + { + MoneyData Single + { RequestID LLUUID } + { IntervalDays S32 } + { CurrentInterval S32 } + } +} + + +// dataserver -> simulator -> viewer +// Reliable +{ + GroupAccountSummaryReply Low 354 Trusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { GroupID LLUUID } + } + { + MoneyData Single + { RequestID LLUUID } + { IntervalDays S32 } + { CurrentInterval S32 } + { StartDate Variable 1 } // string + { Balance S32 } + { TotalCredits S32 } + { TotalDebits S32 } + { ObjectTaxCurrent S32 } + { LightTaxCurrent S32 } + { LandTaxCurrent S32 } + { GroupTaxCurrent S32 } + { ParcelDirFeeCurrent S32 } + { ObjectTaxEstimate S32 } + { LightTaxEstimate S32 } + { LandTaxEstimate S32 } + { GroupTaxEstimate S32 } + { ParcelDirFeeEstimate S32 } + { NonExemptMembers S32 } + { LastTaxDate Variable 1 } // string + { TaxDate Variable 1 } // string + } +} + + +// Reliable +{ + GroupAccountDetailsRequest Low 355 NotTrusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + { GroupID LLUUID } + } + { + MoneyData Single + { RequestID LLUUID } + { IntervalDays S32 } + { CurrentInterval S32 } + } +} + +// Reliable +{ + GroupAccountDetailsReply Low 356 Trusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { GroupID LLUUID } + } + { + MoneyData Single + { RequestID LLUUID } + { IntervalDays S32 } + { CurrentInterval S32 } + { StartDate Variable 1 } // string + } + { + HistoryData Variable + { Description Variable 1 } // string + { Amount S32 } + } +} + + +// Reliable +{ + GroupAccountTransactionsRequest Low 357 NotTrusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + { GroupID LLUUID } + } + { + MoneyData Single + { RequestID LLUUID } + { IntervalDays S32 } + { CurrentInterval S32 } + } +} + +// Reliable +{ + GroupAccountTransactionsReply Low 358 Trusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { GroupID LLUUID } + } + { + MoneyData Single + { RequestID LLUUID } + { IntervalDays S32 } + { CurrentInterval S32 } + { StartDate Variable 1 } // string + } + { + HistoryData Variable + { Time Variable 1 } // string + { User Variable 1 } // string + { Type S32 } + { Item Variable 1 } // string + { Amount S32 } + } +} + +// GroupActiveProposalsRequest +// viewer -> simulator -> dataserver +//reliable +{ + GroupActiveProposalsRequest Low 359 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + } + { + TransactionData Single + { TransactionID LLUUID } + } +} + +// GroupActiveProposalItemReply +// dataserver -> simulator -> viewer +// reliable +{ + GroupActiveProposalItemReply Low 360 Trusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { GroupID LLUUID } + } + { + TransactionData Single + { TransactionID LLUUID } + { TotalNumItems U32 } + } + { + ProposalData Variable + { VoteID LLUUID } + { VoteInitiator LLUUID } + { TerseDateID Variable 1 } // string + { StartDateTime Variable 1 } // string + { EndDateTime Variable 1 } // string + { AlreadyVoted BOOL } + { VoteCast Variable 1 } // string + { Majority F32 } + { Quorum S32 } + { ProposalText Variable 1 } // string + } +} + +// GroupVoteHistoryRequest +// viewer -> simulator -> dataserver +//reliable +{ + GroupVoteHistoryRequest Low 361 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + } + { + TransactionData Single + { TransactionID LLUUID } + } +} + +// GroupVoteHistoryItemReply +// dataserver -> simulator -> viewer +// reliable +{ + GroupVoteHistoryItemReply Low 362 Trusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { GroupID LLUUID } + } + { + TransactionData Single + { TransactionID LLUUID } + { TotalNumItems U32 } + } + { + HistoryItemData Single + { VoteID LLUUID } + { TerseDateID Variable 1 } // string + { StartDateTime Variable 1 } // string + { EndDateTime Variable 1 } // string + { VoteInitiator LLUUID } + { VoteType Variable 1 } // string + { VoteResult Variable 1 } // string + { Majority F32 } + { Quorum S32 } + { ProposalText Variable 2 } // string + } + { + VoteItem Variable + { CandidateID LLUUID } + { VoteCast Variable 1 } // string + { NumVotes S32 } + } +} + +// StartGroupProposal +// viewer -> simulator -> dataserver +// reliable +{ + StartGroupProposal Low 363 NotTrusted Zerocoded UDPDeprecated + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + ProposalData Single + { GroupID LLUUID } + { Quorum S32 } + { Majority F32 } // F32 + { Duration S32 } // S32, seconds + { ProposalText Variable 1 } // string + } +} + +// GroupProposalBallot +// viewer -> simulator -> dataserver +// reliable +{ + GroupProposalBallot Low 364 NotTrusted Unencoded UDPDeprecated + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + ProposalData Single + { ProposalID LLUUID } + { GroupID LLUUID } + { VoteCast Variable 1 } // string + } +} + +// TallyVotes userserver -> dataserver +// reliable +{ + TallyVotes Low 365 Trusted Unencoded +} + + + +// GroupMembersRequest +// get the group members +// simulator -> dataserver +// reliable +{ + GroupMembersRequest Low 366 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + { RequestID LLUUID } + } +} + +// GroupMembersReply +// list of uuids for the group members +// dataserver -> simulator +// reliable +{ + GroupMembersReply Low 367 Trusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + { RequestID LLUUID } + { MemberCount S32 } + } + { + MemberData Variable + { AgentID LLUUID } + { Contribution S32 } + { OnlineStatus Variable 1 } // string + { AgentPowers U64 } + { Title Variable 1 } // string + { IsOwner BOOL } + } +} + +// used to switch an agent's currently active group. +// viewer -> simulator -> dataserver -> AgentDataUpdate... +{ + ActivateGroup Low 368 NotTrusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + { GroupID LLUUID } + } +} + +// viewer -> simulator -> dataserver +{ + SetGroupContribution Low 369 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + Data Single + { GroupID LLUUID } + { Contribution S32 } + } +} + +// viewer -> simulator -> dataserver +{ + SetGroupAcceptNotices Low 370 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + Data Single + { GroupID LLUUID } + { AcceptNotices BOOL } + } + { + NewData Single + { ListInProfile BOOL } + } +} + +// GroupRoleDataRequest +// viewer -> simulator -> dataserver +{ + GroupRoleDataRequest Low 371 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + { RequestID LLUUID } + } +} + + +// GroupRoleDataReply +// All role data for this group +// dataserver -> simulator -> agent +{ + GroupRoleDataReply Low 372 Trusted Unencoded + { + AgentData Single + { AgentID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + { RequestID LLUUID } + { RoleCount S32 } + } + { + RoleData Variable + { RoleID LLUUID } + { Name Variable 1 } + { Title Variable 1 } + { Description Variable 1 } + { Powers U64 } + { Members U32 } + } +} + +// GroupRoleMembersRequest +// viewer -> simulator -> dataserver +{ + GroupRoleMembersRequest Low 373 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + } + { + GroupData Single + { GroupID LLUUID } + { RequestID LLUUID } + } +} + +// GroupRoleMembersReply +// All role::member pairs for this group. +// dataserver -> simulator -> agent +{ + GroupRoleMembersReply Low 374 Trusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { GroupID LLUUID } + { RequestID LLUUID } + { TotalPairs U32 } + } + { + MemberData Variable + { RoleID LLUUID } + { MemberID LLUUID } + } +} + +// GroupTitlesRequest +// viewer -> simulator -> dataserver +{ + GroupTitlesRequest Low 375 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + { GroupID LLUUID } + { RequestID LLUUID } + } +} + + +// GroupTitlesReply +// dataserver -> simulator -> viewer +{ + GroupTitlesReply Low 376 Trusted Zerocoded + { + AgentData Single + { AgentID LLUUID } + { GroupID LLUUID } + { RequestID LLUUID } + } + { + GroupData Variable + { Title Variable 1 } // string + { RoleID LLUUID } + { Selected BOOL } + } +} + +// GroupTitleUpdate +// viewer -> simulator -> dataserver +{ + GroupTitleUpdate Low 377 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + { GroupID LLUUID } + { TitleRoleID LLUUID } + } +} + +// GroupRoleUpdate +// viewer -> simulator -> dataserver +{ + GroupRoleUpdate Low 378 NotTrusted Unencoded + { + AgentData Single + { AgentID LLUUID } + { SessionID LLUUID } + { GroupID LLUUID } + } + { + RoleData Variable + { RoleID LLUUID } + { Name Variable 1 } + { Description Variable 1 } + { Title Variable 1 } + { Powers U64 } + { UpdateType U8 } + } +} + + + +// Request the members of the live help group needed for requesting agent. +// userserver -> dataserver +{ + LiveHelpGroupRequest Low 379 Trusted Unencoded + { + RequestData Single + { RequestID LLUUID } + { AgentID LLUUID } + } +} + +// Send down the group +// dataserver -> userserver +{ + LiveHelpGroupReply Low 380 Trusted Unencoded + { + ReplyData Single + { RequestID LLUUID } + { GroupID LLUUID } + { Selection Variable 1 } // selection criteria all or active + } +} + +''' diff --git a/pyogp/lib/base/login.py b/pyogp/lib/base/login.py index b7bb793..8b5db4c 100644 --- a/pyogp/lib/base/login.py +++ b/pyogp/lib/base/login.py @@ -220,7 +220,11 @@ class Login(object): # This handles the standard 'login_to_simulator' case # plus, all the transforms that may need to be followed login_handler = self.handler.__getattr__(login_method) - self.response = login_handler(self.login_params) + + try: + self.response = login_handler(self.login_params) + except ProtocolError, error: + raise LoginError('Failed to login agent due to: %s' % (error)) if self.response['login'] in ('true', 'false'): self._parse_response() diff --git a/pyogp/lib/base/message/packethandler.py b/pyogp/lib/base/message/packethandler.py index 5f406df..e1fa543 100644 --- a/pyogp/lib/base/message/packethandler.py +++ b/pyogp/lib/base/message/packethandler.py @@ -48,7 +48,7 @@ class PacketHandler(object): if self.settings.LOG_VERBOSE: log(DEBUG, 'Creating a monitor for %s' % (packet_name)) - return self.handlers.setdefault(packet_name, PacketReceivedNotifier(packet_name)) + return self.handlers.setdefault(packet_name, PacketReceivedNotifier(packet_name, self.settings)) def is_packet_handled(self, packet_name): """ if the packet is being monitored, return True, otherwise, return False @@ -85,9 +85,10 @@ class PacketHandler(object): class PacketReceivedNotifier(object): """ received TestMessage packet """ - def __init__(self, packet_name): + def __init__(self, packet_name, settings): self.event = Event() self.packet_name = packet_name + self.settings = settings def subscribe(self, *args, **kwdargs): self.event.subscribe(*args, **kwdargs) @@ -96,6 +97,11 @@ class PacketReceivedNotifier(object): self.event(packet) + def unsubscribe(self, *args, **kwdargs): + self.event.unsubscribe(*args, **kwdargs) + + if self.settings.LOG_VERBOSE: log(DEBUG, "Removed the monitor for %s by %s" % (args, kwdargs)) + def __len__(self): return len(self.event) diff --git a/pyogp/lib/base/message/udpdispatcher.py b/pyogp/lib/base/message/udpdispatcher.py index 9a5bee5..9a61fc1 100644 --- a/pyogp/lib/base/message/udpdispatcher.py +++ b/pyogp/lib/base/message/udpdispatcher.py @@ -165,6 +165,10 @@ class UDPDispatcher(object): packet = UDPPacket(message) + # enable monitoring of outgoing packets + if self.settings.HANDLE_PACKETS: + self.packet_handler._handle(packet) + #use circuit manager to get the circuit to send on circuit = self.find_circuit(host) diff --git a/pyogp/lib/base/objects.py b/pyogp/lib/base/objects.py index 097bc0f..7adaf9f 100644 --- a/pyogp/lib/base/objects.py +++ b/pyogp/lib/base/objects.py @@ -345,6 +345,8 @@ def onObjectUpdate(packet, objects): _RegionHandle = packet.message_data.blocks['RegionData'][0].get_variable('RegionHandle').data _TimeDilation = packet.message_data.blocks['RegionData'][0].get_variable('TimeDilation').data + print packet + for ObjectData_block in packet.message_data.blocks['ObjectData']: _ID = ObjectData_block.get_variable('ID').data diff --git a/pyogp/lib/base/region.py b/pyogp/lib/base/region.py index f209f13..101bb37 100644 --- a/pyogp/lib/base/region.py +++ b/pyogp/lib/base/region.py @@ -380,7 +380,7 @@ class Region(object): self.send_message(packet()) - def sendAgentUpdate(self, BodyRotation = (0.0,0.0,0.0,0.0), HeadRotation = (0.0,0.0,0.0,0.0), State = 0x00, CameraCenter = (0.0,0.0,0.0), CameraAtAxis = (0.0,0.0,0.0), CameraLeftAxis = (0.0,0.0,0.0), CameraUpAxis = (0.0,0.0,0.0), Far = 0, ControlFlags = 0x00, Flags = 0x00): + def sendAgentUpdate(self, BodyRotation = (0.0,0.0,0.0,1.0), HeadRotation = (0.0,0.0,0.0,1.0), State = 0x00, CameraCenter = (0.0,0.0,0.0), CameraAtAxis = (0.0,0.0,0.0), CameraLeftAxis = (0.0,0.0,0.0), CameraUpAxis = (0.0,0.0,0.0), Far = 0, ControlFlags = 0x00, Flags = 0x00): """ sends an AgentUpdate packet """ packet = AgentUpdatePacket() @@ -449,7 +449,7 @@ class Region(object): if self.messenger.has_unacked(): self.messenger.process_acks() - self.sendAgentUpdate() + self.sendAgentUpdate(CameraCenter = self.agent.Position, CameraAtAxis = self.settings.DEFAULT_CAMERA_AT_AXIS, CameraLeftAxis = self.settings.DEFAULT_CAMERA_AT_AXIS, CameraUpAxis = self.settings.DEFAULT_CAMERA_UP_AXIS, Far = self.settings.DEFAULT_CAMERA_DRAW_DISTANCE) # send pending messages in the queue for (packet, reliable) in self.packet_queue: @@ -518,7 +518,7 @@ def onEnableSimulator(packet, region): Port = packet.message_data.blocks['SimulatorInfo'][0].get_variable('Port').data - # nit sure what this is, but pass it up + # not sure what this is, but pass it up Handle = [ord(x) for x in packet.message_data.blocks['SimulatorInfo'][0].get_variable('Handle').data] region.enable_child_simulator(IP, Port, Handle) diff --git a/pyogp/lib/base/settings.py b/pyogp/lib/base/settings.py index 359cd02..7d7c0c5 100644 --- a/pyogp/lib/base/settings.py +++ b/pyogp/lib/base/settings.py @@ -20,13 +20,15 @@ $/LicenseInfo$ class Settings(object): - def __init__(self): + def __init__(self, quiet_logging = False): """ some lovely configurable settings These are applied application wide, and can be overridden at any time in a specific instance """ + self.quiet_logging = quiet_logging + #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Application behavior settings #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -46,8 +48,18 @@ class Settings(object): # enable communications monitoring self.ENABLE_COMMUNICATIONS_TRACKING = True + # toggle parsing all/handled packets self.ENABLE_DEFERRED_PACKET_PARSING = True + # ~~~~~~ + # Camera + # ~~~~~~ + + self.DEFAULT_CAMERA_DRAW_DISTANCE = 128 + self.DEFAULT_CAMERA_AT_AXIS = (1.0, 0.0, 0.0) + self.DEFAULT_CAMERA_LEFT_AXIS = (0.0, 1.0, 0.0) + self.DEFAULT_CAMERA_UP_AXIS = (0.0, 0.0, 1.0) + #~~~~~~~~~~~~~~~~~~~~~~~ # Extended Login Options #~~~~~~~~~~~~~~~~~~~~~~~ @@ -104,6 +116,15 @@ class Settings(object): self.DISABLE_SPAMMERS = True self.UDP_SPAMMERS = ['PacketAck', 'AgentUpdate'] + # override the defaults + if self.quiet_logging: + self.LOG_VERBOSE = False + self.ENABLE_BYTES_TO_HEX_LOGGING = False + self.ENABLE_CAPS_LOGGING = False + self.ENABLE_CAPS_LLSD_LOGGING = False + self.ENABLE_EQ_LOGGING = False + self.ENABLE_UDP_LOGGING = False + #~~~~~~~~~~~~~~~~~~~~~~ # Test related settings #~~~~~~~~~~~~~~~~~~~~~~ diff --git a/pyogp/lib/base/utilities/events.py b/pyogp/lib/base/utilities/events.py index 7311824..e48b7fa 100644 --- a/pyogp/lib/base/utilities/events.py +++ b/pyogp/lib/base/utilities/events.py @@ -32,13 +32,16 @@ class Event(object): return self - def unsubscribe(self, handler): + def unsubscribe(self, handler, *args, **kwargs): """ remove the subscriber (handler) to this event """ try: - self.subscribers.delete(handler) + + self.subscribers.remove((handler, args, kwargs)) + except: raise ValueError("Handler is not subscribed to this event.") + return self def notify(self, args): @@ -62,26 +65,4 @@ class Event(object): __iadd__ = subscribe __isub__ = unsubscribe __call__ = notify - __len__ = getSubscriberCount - -''' -class MockFileWatcher: - def __init__(self): - self.fileChanged = Event() - - def watchFiles(self): - source_path = "foo" - self.fileChanged(source_path) - -def log_file_change(source_path): - print "%r changed." % (source_path,) - -def log_file_change2(source_path): - print "%r changed!" % (source_path,) - -watcher = MockFileWatcher() -watcher.fileChanged += log_file_change2 -watcher.fileChanged += log_file_change -watcher.fileChanged -= log_file_change2 -watcher.watchFiles() -''' + __len__ = getSubscriberCount \ No newline at end of file diff --git a/setup.py b/setup.py index b6dfa64..d17bbf7 100644 --- a/setup.py +++ b/setup.py @@ -60,7 +60,8 @@ setup(name='pyogp.lib.base', 'inventory_handling = pyogp.lib.base.examples.sample_inventory_handling:main', 'object_tracking = pyogp.lib.base.examples.sample_object_tracking:main', 'object_creation = pyogp.lib.base.examples.sample_object_creation:main', - 'chat_and_instant_messaging = pyogp.lib.base.examples.sample_chat_and_instant_messaging:main' + 'chat_and_instant_messaging = pyogp.lib.base.examples.sample_chat_and_instant_messaging:main', + 'group_creation = pyogp.lib.base.examples.sample_group_creation:main' ], },