Example #1
0
class ClientAccountManager(ClientOperationManager):
    notify = notify.new_category('ClientAccountManager')

    def __init__(self, *args, **kwargs):
        ClientOperationManager.__init__(self, *args, **kwargs)

        self._dbm = semidbm.open(
            config.GetString('clientagent-dbm-filename',
                             'databases/database.dbm'),
            config.GetString('clientagent-dbm-mode', 'c'))

    @property
    def dbm(self):
        return self._dbm

    def handle_operation(self, operationFSM, client, callback, *args,
                         **kwargs):
        operation = self.run_operation(operationFSM, client, callback, *args,
                                       **kwargs)
        if not operation:
            self.notify.warning('Failed to handle operation: %r!' %
                                operationFSM)
            return

        operation.request('Start')
class MessageDirector(io.NetworkListener, component.Component):
    notify = notify.new_category('MessageDirector')

    def __init__(self):
        address = config.GetString('messagedirector-address', '0.0.0.0')
        port = config.GetInt('messagedirector-port', 7100)

        io.NetworkListener.__init__(self, address, port, Participant)

        self._interface = ParticipantInterface(self)
        self._message_interface = MessageInterface(self)

    @property
    def interface(self):
        return self._interface

    @property
    def message_interface(self):
        return self._message_interface

    def setup(self):
        io.NetworkListener.setup(self)
        self._message_interface.setup()

    def shutdown(self):
        self._message_interface.shutdown()
        io.NetworkListener.shutdown(self)
Example #3
0
class RetrieveAvatarsFSM(ClientOperation):
    notify = notify.new_category('RetrieveAvatarsFSM')

    def __init__(self, manager, client, callback, account_id):
        ClientOperation.__init__(self, manager, client, callback)

        self._account_id = account_id
        self._pending_avatars = []
        self._avatar_fields = {}

    def enterStart(self):
        self.manager.network.database_interface.query_object(
            self.client.channel, types.DATABASE_CHANNEL, self._account_id,
            self.__account_loaded,
            self.manager.network.dc_loader.dclasses_by_name['Account'])

    def exitStart(self):
        pass

    def __account_loaded(self, dclass, fields):
        if not dclass:
            self.cleanup(False)
            return

        avatar_list = fields['ACCOUNT_AV_SET'][0]
        for avatar_id in avatar_list:
            if not avatar_id:
                continue

            self._pending_avatars.append(avatar_id)

            def response(dclass, fields, avatar_id=avatar_id):
                self._avatar_fields[avatar_id] = fields
                self._pending_avatars.remove(avatar_id)
                if not self._pending_avatars:
                    self.request('SetAvatars')

            self.manager.network.database_interface.query_object(
                self.client.channel, types.DATABASE_CHANNEL, avatar_id,
                response, self.manager.network.dc_loader.
                dclasses_by_name['DistributedToon'])

        if not self._pending_avatars:
            self.request('SetAvatars')

    def enterSetAvatars(self):
        avatar_list = []
        for avatar_id, fields in self._avatar_fields.items():
            avatar_data = ClientAvatarData(avatar_id,
                                           [fields['setName'][0], '', '', ''],
                                           fields['setDNAString'][0],
                                           fields['setPosIndex'][0], 0)

            avatar_list.append(avatar_data)

        # we're all done.
        self.cleanup(True, avatar_list)

    def exitSetAvatars(self):
        pass
Example #4
0
class SetNameFSM(ClientOperation):
    notify = notify.new_category('SetNameFSM')

    def __init__(self, manager, client, callback, avatar_id, wish_name):
        self.notify.debug("SetNameFSM.__init__(%s, %s, %s, %s, %s)" %
                          (str(manager), str(client), str(callback),
                           str(avatar_id), str(wish_name)))

        ClientOperation.__init__(self, manager, client, callback)

        self._avatar_id = avatar_id
        self._wish_name = wish_name
        self._callback = callback
        self._dc_class = None
        self._fields = {}

    def enterStart(self):
        self.notify.debug("SetNameFSM.enterQuery()")

        def response(dclass, fields):
            self.notify.debug("SetNameFSM.enterQuery.response(%s, %s)" %
                              (str(dclass), str(fields)))
            if not dclass:
                self.cleanup(False)
                return

            self._dc_class = dclass
            self._fields = fields
            self.request('SetName')

        self.manager.network.database_interface.query_object(
            self.client.channel, types.DATABASE_CHANNEL, self._avatar_id,
            response,
            self.manager.network.dc_loader.dclasses_by_name['DistributedToon'])

    def exitStart(self):
        self.notify.debug("SetNameFSM.exitQuery()")

    def enterSetName(self):
        self.notify.debug("SetNameFSM.enterSetName()")

        # TODO: Parse a check the wish-name for bad names and etc.
        new_fields = {'setName': (self._wish_name, )}

        #self.notify.warning("New fields are \n%s" % (str(self._fields)))

        self.manager.network.database_interface.update_object(
            self.client.channel, types.DATABASE_CHANNEL, self._avatar_id,
            self.manager.network.dc_loader.dclasses_by_name['DistributedToon'],
            new_fields)

        # We're all done
        self.cleanup(True, self._avatar_id, self._wish_name)

    def exitSetName(self):
        self.notify.debug("SetNameFSM.exitSetName()")
Example #5
0
class ClientOperation(FSM):
    notify = notify.new_category('ClientOperation')

    def __init__(self, manager, client, callback):
        self.notify.debug('Starting FSM operation %s!' %
                          (self.__class__.__name__))
        FSM.__init__(self, self.__class__.__name__)

        self._manager = manager
        self._client = client
        self._callback = callback

    @property
    def manager(self):
        return self._manager

    @property
    def client(self):
        return self._client

    @property
    def callback(self):
        return self._callback

    @callback.setter
    def callback(self, callback):
        self._callback = callback

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def defaultFilter(self, request, *args):
        return FSM.defaultFilter(self, request, *args)

    def cleanup(self, success, *args, **kwargs):
        self.notify.debug('Stopping operation %s for %s.' %
                          (self.__class__.__name__, self.client))
        self.ignoreAll()
        self.manager.stop_operation(self.client)
        self.demand('Off')

        # only initiate callback if the cleanup was successful...
        if self._callback and success:
            self._callback(*args, **kwargs)
        elif not success:
            self.notify.debug('Cleanup operation %s was unsuccessful for %s.' %
                              (self.__class__.__name__, self.client))
Example #6
0
class NetworkManager(object):
    notify = notify.new_category('NetworkManager')

    def get_unique_name(self, name):
        return '%s-%s-%s' % (self.__class__.__name__, name, id(self))

    def get_puppet_connection_channel(self, doId):
        return doId + (1001 << 32)

    def get_account_connection_channel(self, doId):
        return doId + (1003 << 32)

    def get_account_id_from_channel_code(self, channel):
        return channel >> 32

    def get_avatar_id_from_connection_channel(self, channel):
        return channel & 0xffffffff
class Participant(io.NetworkHandler):
    notify = notify.new_category('Participant')

    def register_for_channel(self, channel):
        io.NetworkChannelManager.register_for_channel(self, channel)
        self.network.interface.add_participant(channel, self)

    def unregister_for_channel(self, channel):
        self.network.interface.remove_participant(channel)
        io.NetworkChannelManager.unregister_for_channel(self, channel)

    def handle_datagram(self, di):
        channels = di.get_uint8()
        channel = di.get_uint64()
        if channels == 1 and channel == types.CONTROL_MESSAGE:
            message_type = di.get_uint16()
            sender = di.get_uint64()
            if message_type == types.CONTROL_SET_CHANNEL:
                self.register_for_channel(sender)
            elif message_type == types.CONTROL_REMOVE_CHANNEL:
                self.unregister_for_channel(sender)
            elif message_type == types.CONTROL_SET_CON_NAME:
                pass
            elif message_type == types.CONTROL_SET_CON_URL:
                pass
            elif message_type == types.CONTROL_ADD_RANGE:
                pass
            elif message_type == types.CONTROL_REMOVE_RANGE:
                pass
            elif message_type == types.CONTROL_ADD_POST_REMOVE:
                self.network.message_interface.append_post_handle(
                    sender,
                    io.NetworkDatagram(Datagram(di.get_remaining_bytes())))
            elif message_type == types.CONTROL_CLEAR_POST_REMOVE:
                self.network.message_interface.clear_post_handles(sender)
            else:
                self.notify.warning('Failed to handle unknown datagram with '
                                    'message type: %d!' % message_type)
        else:
            self.network.message_interface.append_handle(
                channel, di.get_uint64(), di.get_uint16(),
                io.NetworkDatagram(Datagram(di.get_remaining_bytes())))
Example #8
0
class DatabaseOperationFSM(FSM):
    notify = notify.new_category('DatabaseOperationFSM')

    def __init__(self, network, sender, callback=None):
        FSM.__init__(self, self.__class__.__name__)

        self._network = network
        self._sender = sender
        self._callback = callback

    @property
    def network(self):
        return self._network

    @property
    def sender(self):
        return self._sender

    @property
    def callback(self):
        return self._callback

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterStart(self):
        pass

    def exitStart(self):
        pass

    def cleanup(self, success, *args, **kwargs):
        self.ignoreAll()
        self.demand('Off')

        if self.callback:
            self.callback(success, self._sender, *args, **kwargs)
Example #9
0
class DatabaseOperationManager(object):
    notify = notify.new_category('DatabaseOperationManager')

    def __init__(self):
        self._operations = collections.deque()

        self._flush_timeout = config.GetFloat('database-flush-timeout', 0.001)
        self.__flush_task = None

    @property
    def operations(self):
        return self._operations

    def add_operation(self, fsm_class, *args, **kwargs):
        operation = fsm_class(*args, **kwargs)
        self._operations.append(operation)

    def setup(self):
        self.__flush_task = task_mgr.doMethodLater(self._flush_timeout,
                                                   self.__flush,
                                                   'database-flush')

    def __flush(self, task):
        """
        Gets an database operation from the queue and processes it...
        """

        while len(self._operations) > 0:
            operation = self._operations.popleft()
            operation.request('Start')

        return task.again

    def shutdown(self):
        if self.__flush_task:
            task_mgr.remove(self.__flush_task)
            self.__flush_task = None

        self._operations = None
class ComponentManager(object):
    notify = notify.new_category('ComponentManager')

    def __init__(self):
        self._components = []

    @property
    def components(self):
        return self._components

    def has_component(self, component):
        assert (isinstance(component, Component))
        return component in self._components

    def add_component(self, component):
        assert (isinstance(component, Component))
        if component in self._components:
            return

        self.notify.info('Starting component: %s...' %
                         component.__class__.__name__)
        self._components.append(component)
        component.setup()

    def remove_component(self, component):
        assert (isinstance(component, Component))
        if component not in self._components:
            return

        self.notify.info('Shutting down component: %s...' %
                         component.__class__.__name__)
        self._components.remove(component)
        component.shutdown()

    def shutdown(self):
        assert (len(self._components) > 0)
        for component in list(self._components):
            self.remove_component(component)
class ParticipantInterface(object):
    notify = notify.new_category('ParticipantInterface')

    def __init__(self, network):
        self._network = network
        self._participants = {}

    @property
    def participants(self):
        return self._participants

    def has_participant(self, channel):
        return channel in self._participants

    def add_participant(self, channel, participant):
        if self.has_participant(channel):
            self.notify.warning('Failed to add participant with channel: %d, '
                                'participant already exists!' % channel)

            return

        self._participants[channel] = participant

    def remove_participant(self, channel):
        if not self.has_participant(channel):
            self.notify.warning(
                'Failed to remove participant with channel: %d, '
                'participant does not exist!' % channel)

            return

        self._network.message_interface.flush_post_handles(channel)
        del self._participants[channel]

    def get_participant(self, channel):
        return self._participants.get(channel)
Example #12
0
class LoadAvatarFSM(ClientOperation):
    notify = notify.new_category('LoadAvatarFSM')

    def __init__(self, manager, client, callback, account_id, avatar_id):
        ClientOperation.__init__(self, manager, client, callback)

        self._account_id = account_id
        self._avatar_id = avatar_id

        self._dc_class = None
        self._fields = {}

    def enterStart(self):
        if self._avatar_id == 0:
            self.cleanup(False)
            return

        def response(dclass, fields):
            if not dclass:
                self.cleanup(False)
                return

            self._dc_class = dclass
            self._fields = fields
            self.request('Activate')

        self.manager.network.database_interface.query_object(
            self.client.channel, types.DATABASE_CHANNEL, self._avatar_id,
            response,
            self.manager.network.dc_loader.dclasses_by_name['DistributedToon'])

    def exitStart(self):
        pass

    def _handle_activate_avatar(self, task):
        # setup a post remove message that will delete the
        # client's toon object when they disconnect...
        post_remove = io.NetworkDatagram()
        post_remove.add_header(self._avatar_id, self.client.channel,
                               types.STATESERVER_OBJECT_DELETE_RAM)

        post_remove.add_uint32(self._avatar_id)

        datagram = io.NetworkDatagram()
        datagram.add_control_header(self.client.channel,
                                    types.CONTROL_ADD_POST_REMOVE)

        datagram.append_data(post_remove.get_message())
        self.manager.network.handle_send_connection_datagram(datagram)

        # grant ownership over the distributed object...
        datagram = io.NetworkDatagram()
        datagram.add_header(self._avatar_id, self.client.channel,
                            types.STATESERVER_OBJECT_SET_OWNER)

        datagram.add_uint64(self.client.channel)
        self.manager.network.handle_send_connection_datagram(datagram)

        # we're all done.
        self.cleanup(True, self._avatar_id)
        return task.done

    def enterActivate(self):
        # add them to the avatar channel
        channel = self.client.get_puppet_connection_channel(self._avatar_id)
        self.client.register_for_channel(channel)

        # set their sender channel to represent their account affiliation
        channel = self._account_id << 32 | self._avatar_id
        self.client.handle_set_channel_id(channel)

        datagram = io.NetworkDatagram()
        datagram.add_header(
            types.STATESERVER_CHANNEL, channel,
            types.STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER)

        datagram.add_uint32(self._avatar_id)
        datagram.add_uint32(0)
        datagram.add_uint32(0)
        datagram.add_uint16(self._dc_class.get_number())

        sorted_fields = {}
        for field_name, field_args in self._fields.items():
            field = self._dc_class.get_field_by_name(field_name)
            if not field:
                self.notify.warning('Failed to pack fields for object %d, '
                                    'unknown field: %s!' %
                                    (self._avatar_id, field_name))

                return

            sorted_fields[field.get_number()] = field_args

        sorted_fields = collections.OrderedDict(sorted(sorted_fields.items()))

        field_packer = DCPacker()
        for field_index, field_args in sorted_fields.items():
            field = self._dc_class.get_field_by_index(field_index)
            if not field:
                self.notify.error(
                    'Failed to pack required field: %d for object %d, '
                    'unknown field!' % (field_index, self._avatar_id))

            field_packer.begin_pack(field)
            field.pack_args(field_packer, field_args)
            field_packer.end_pack()

        datagram.append_data(field_packer.get_string())

        # add a few fields so we ensure we actually have some data to append
        # with the generate call, as we must always have some field data in this message:
        other_fields = {
            'setCommonChatFlags': (self._fields.get('setCommonChatFlags',
                                                    0), ),
            'setTrophyScore': (self._fields.get('setTrophyScore', 0), ),
        }

        field_packer = DCPacker()
        for field_name, field_args in other_fields.items():
            field = self._dc_class.get_field_by_name(field_name)

            if not field:
                self.notify.error(
                    'Failed to pack other field: %s for object %d, '
                    'unknown field!' % (field_name, self._avatar_id))

            field_packer.raw_pack_uint16(field.get_number())
            field_packer.begin_pack(field)
            field.pack_args(field_packer, field_args)
            field_packer.end_pack()

        datagram.add_uint16(len(other_fields))
        datagram.append_data(field_packer.get_string())
        self.manager.network.handle_send_connection_datagram(datagram)

        taskMgr.doMethodLater(0.2, self._handle_activate_avatar,
                              'activate-avatar-%d-task' % self._avatar_id)

    def exitActivate(self):
        pass
Example #13
0
class Client(io.NetworkHandler):
    notify = notify.new_category('Client')

    def __init__(self, *args, **kwargs):
        io.NetworkHandler.__init__(self, *args, **kwargs)

        self.channel = self.network.channel_allocator.allocate()
        self._authenticated = False

        self._interest_manager = InterestManager()

        self._location_deferred_callback = None

        self.__interest_timeout_task = None
        self._generate_deferred_callback = None

        self._seen_objects = {}
        self._owned_objects = []
        self._pending_objects = []

        self._dna_stores = {}

    @property
    def authenticated(self):
        return self._authenticated

    @authenticated.setter
    def authenticated(self, authenticated):
        self._authenticated = authenticated

    def has_seen_object(self, do_id):
        for zone_id, seen_objects in list(self._seen_objects.items()):
            if do_id in seen_objects:
                return True

        return False

    def remove_seen_object(self, do_id):
        for zone_id, seen_objects in list(self._seen_objects.items()):
            if do_id in seen_objects:
                seen_objects.remove(do_id)

            if len(seen_objects) == 0:
                del self._seen_objects[zone_id]

    def get_seen_object_zone(self, do_id):
        for zone_id, seen_objects in list(self._seen_objects.items()):
            if do_id in seen_objects:
                return zone_id

        return -1

    def handle_send_disconnect(self, code, reason):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_GO_GET_LOST)
        datagram.add_uint16(code)
        datagram.add_string(reason)

        self.handle_send_datagram(datagram)
        self.handle_disconnect()

    def handle_datagram(self, di):
        try:
            message_type = di.get_uint16()
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                self._channel)

            return

        if message_type == types.CLIENT_HEARTBEAT:
            pass
        elif message_type == types.CLIENT_LOGIN_2:
            self.handle_login(di)
        elif message_type == types.CLIENT_DISCONNECT:
            self.handle_disconnect()
        else:
            if not self._authenticated:
                self.handle_send_disconnect(
                    types.CLIENT_DISCONNECT_ANONYMOUS_VIOLATION,
                    'Cannot send datagram with message type: %d, channel: %d not yet authenticated!'
                    % (message_type, self.channel))

                return
            else:
                self.handle_authenticated_datagram(message_type, di)

    def handle_authenticated_datagram(self, message_type, di):
        if message_type == types.CLIENT_GET_SHARD_LIST:
            self.handle_get_shard_list()
        elif message_type == types.CLIENT_GET_AVATARS:
            self.handle_get_avatars()
        elif message_type == types.CLIENT_GET_AVATAR_DETAILS:
            self.handle_get_avatar_details(di)
        elif message_type == types.CLIENT_CREATE_AVATAR:
            self.handle_create_avatar(di)
        elif message_type == types.CLIENT_SET_AVATAR:
            self.handle_set_avatar(di)
        elif message_type == types.CLIENT_SET_WISHNAME:
            self.handle_set_wishname(di)
        elif message_type == types.CLIENT_SET_NAME_PATTERN:
            self.handle_set_name_pattern(di)
        elif message_type == types.CLIENT_DELETE_AVATAR:
            self.handle_delete_avatar(di)
        elif message_type == types.CLIENT_GET_FRIEND_LIST:
            self.handle_get_friends_list(di)
        elif message_type == types.CLIENT_REMOVE_FRIEND:
            pass
        elif message_type == types.CLIENT_SET_SHARD:
            self.handle_set_shard(di)
        elif message_type == types.CLIENT_SET_ZONE:
            self.handle_set_zone(di)
        elif message_type == types.CLIENT_OBJECT_UPDATE_FIELD:
            self.handle_object_update_field(di)
        else:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_INVALID_MSGTYPE,
                'Unknown datagram: %d from channel: %d!' %
                (message_type, self.channel))

            return

    def handle_internal_datagram(self, message_type, sender, di):
        if message_type == types.CLIENTAGENT_DISCONNECT:
            self.handle_send_disconnect(di.get_uint16(), di.get_string())
        elif message_type == types.CLIENTAGENT_FRIEND_ONLINE:
            self.handle_friend_online(di)
        elif message_type == types.CLIENTAGENT_FRIEND_OFFLINE:
            self.handle_friend_offline(di)
        elif message_type == types.STATESERVER_GET_SHARD_ALL_RESP:
            self.handle_get_shard_list_resp(di)
        elif message_type == types.STATESERVER_SERVER_UP:
            self.handle_server_up(di)
        elif message_type == types.STATESERVER_SERVER_DOWN:
            self.handle_server_down(di)
        elif message_type == types.STATESERVER_OBJECT_LOCATION_ACK:
            self.handle_object_location_ack(di)
        elif message_type == types.STATESERVER_OBJECT_GET_ZONES_OBJECTS_RESP:
            self.handle_object_get_zones_objects_resp(di)
        elif message_type == types.STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED:
            self.handle_object_enter_owner(False, di)
        elif message_type == types.STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER:
            self.handle_object_enter_owner(True, di)
        elif message_type == types.STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED:
            self.handle_object_enter_location(False, di)
        elif message_type == types.STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER:
            self.handle_object_enter_location(True, di)
        elif message_type == types.STATESERVER_OBJECT_CHANGING_LOCATION:
            self.handle_object_changing_location(di)
        elif message_type == types.STATESERVER_OBJECT_UPDATE_FIELD:
            self.handle_object_update_field_resp(di)
        elif message_type == types.STATESERVER_OBJECT_DELETE_RAM:
            self.handle_object_delete_ram(di)
        else:
            self.network.database_interface.handle_datagram(message_type, di)

    def handle_login(self, di):
        try:
            play_token = di.get_string()
            server_version = di.get_string()
            hash_val = di.get_uint32()
            token_type = di.get_int32()
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                self._channel)

            return

        if server_version != self.network.server_version:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_BAD_VERSION,
                'Invalid server version: %s, expected: %s!' %
                (server_version, self.network.server_version))

            return

        if hash_val != self.network.server_hash_val:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_BAD_DCHASH,
                'Got an invalid DC hash value: %d expected: %d!' %
                (hash_val, self.network.server_hash_val))

            return

        if token_type != types.CLIENT_LOGIN_2_BLUE and token_type != CLIENT_LOGIN_2_PLAY_TOKEN:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_INVALID_PLAY_TOKEN_TYPE,
                'Invalid play token type: %d!' % token_type)
            return

        self.network.account_manager.handle_operation(LoadAccountFSM, self,
                                                      self.__handle_login_resp,
                                                      play_token)

    def __handle_login_resp(self, play_token):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_LOGIN_2_RESP)
        datagram.add_uint8(0)
        datagram.add_string('All Ok')
        datagram.add_string(play_token)
        datagram.add_uint8(1)
        datagram.add_uint32(int(time.time()))
        datagram.add_uint32(int(time.clock()))
        datagram.add_uint8(1)
        datagram.add_int32(1000 * 60 * 60)
        self.handle_send_datagram(datagram)

    def handle_get_shard_list(self):
        datagram = io.NetworkDatagram()
        datagram.add_header(types.STATESERVER_CHANNEL, self.channel,
                            types.STATESERVER_GET_SHARD_ALL)

        self.network.handle_send_connection_datagram(datagram)

    def handle_get_shard_list_resp(self, di):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_GET_SHARD_LIST_RESP)
        datagram.append_data(di.get_remaining_bytes())
        self.handle_send_datagram(datagram)

    def handle_server_up(self, di):
        shard_id = di.get_uint32()
        shard_name = di.get_string()

        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_SERVER_UP)
        datagram.add_uint32(shard_id)
        datagram.add_string(shard_name)
        self.handle_send_datagram(datagram)

    def handle_server_down(self, di):
        shard_id = di.get_uint32()

        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_SERVER_DOWN)
        datagram.add_uint32(shard_id)
        self.handle_send_datagram(datagram)

    def handle_get_avatars(self):
        account_id = self.get_account_id_from_channel_code(self.channel)
        self.network.account_manager.handle_operation(
            RetrieveAvatarsFSM, self, self.__handle_retrieve_avatars_resp,
            account_id)

    def __handle_retrieve_avatars_resp(self, avatar_data):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_GET_AVATARS_RESP)
        datagram.add_uint8(0)
        datagram.add_uint16(len(avatar_data))

        for avatar in avatar_data:
            datagram.add_uint32(avatar.do_id)
            datagram.add_string(avatar.name_list[0])
            datagram.add_string(avatar.name_list[1])
            datagram.add_string(avatar.name_list[2])
            datagram.add_string(avatar.name_list[3])
            datagram.add_string(avatar.dna)
            datagram.add_uint8(avatar.position)
            datagram.add_uint8(avatar.name_index)

        self.handle_send_datagram(datagram)

    def handle_create_avatar(self, di):
        try:
            echo_context = di.get_uint16()
            dna_string = di.get_string()
            index = di.get_uint8()
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                self._channel)

            return

        account_id = self.get_account_id_from_channel_code(self.channel)
        self.network.account_manager.handle_operation(
            CreateAvatarFSM, self, self.__handle_create_avatar_resp,
            echo_context, account_id, dna_string, index)

    def __handle_create_avatar_resp(self, echo_context, avatar_id):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_CREATE_AVATAR_RESP)
        datagram.add_uint16(echo_context)
        datagram.add_uint8(0)
        datagram.add_uint32(avatar_id)
        self.handle_send_datagram(datagram)

    def _delete_ownerviews(self):
        account_id = self.get_account_id_from_channel_code(self.channel)
        avatar_id = self.get_avatar_id_from_connection_channel(self.channel)

        # close the avatar channel:
        channel = self.get_puppet_connection_channel(avatar_id)
        self.unregister_for_channel(channel)

        # once again open the account channel, also closing the avatar sender channel:
        channel = account_id << 32
        self.handle_set_channel_id(channel)

        # clear any of the cached distributed objects:
        self._seen_objects = {}
        self._owned_objects = []
        self._pending_objects = []

        self._dna_stores = {}

        # clear our old interest zones:
        self._interest_manager.clear()

    def handle_set_avatar(self, di):
        try:
            avatar_id = di.get_uint32()
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                self._channel)

            return

        # check to see if the client want's to delete their ownerview,
        # we assume the client has logged out to the avatar chooser:
        if avatar_id == 0:
            self._delete_ownerviews()
            return

        # before continuing to activate an avatar with the associated ID sent above,
        # check to make sure the client does not already has an actively generated avatar on the SS:
        if len(self._owned_objects) > 0:
            return

        account_id = self.get_account_id_from_channel_code(self.channel)
        self.network.account_manager.handle_operation(
            LoadAvatarFSM, self, self.__handle_set_avatar_resp, account_id,
            avatar_id)

    def __handle_set_avatar_resp(self, avatar_id):
        pass

    def handle_friend_online(self, di):
        friend_id = di.get_uint32()

        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_FRIEND_ONLINE)
        datagram.add_uint32(friend_id)
        self.handle_send_datagram(datagram)

    def handle_friend_offline(self, di):
        friend_id = di.get_uint32()

        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_FRIEND_OFFLINE)
        datagram.add_uint32(friend_id)
        self.handle_send_datagram(datagram)

    def handle_get_avatar_details(self, di):
        try:
            avatar_id = di.get_uint32()
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                self._channel)

            return

        self.network.account_manager.handle_operation(
            GetAvatarDetailsFSM, self, self.handle_object_enter_owner,
            avatar_id)

    def handle_set_wishname(self, di):
        try:
            avatar_id = di.get_uint32()
            wish_name = di.get_string()
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                self._channel)

            return

        self.network.account_manager.handle_operation(
            SetNameFSM, self, self.__handle_set_wishname_resp, avatar_id,
            wish_name)

    def __handle_set_wishname_resp(self, avatar_id, wish_name):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_SET_WISHNAME_RESP)
        datagram.add_uint32(avatar_id)
        datagram.add_uint16(0)
        datagram.add_string('')
        datagram.add_string(wish_name)
        datagram.add_string('')
        self.handle_send_datagram(datagram)

    def handle_set_name_pattern(self, di):
        try:
            name_indices = []
            name_flags = []
            avatar_id = di.get_uint32()
            name_indices.append(di.get_uint16())
            name_flags.append(di.get_uint16())
            name_indices.append(di.get_uint16())
            name_flags.append(di.get_uint16())
            name_indices.append(di.get_uint16())
            name_flags.append(di.get_uint16())
            name_indices.append(di.get_uint16())
            name_flags.append(di.get_uint16())
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                self._channel)
            return

        pattern = [(name_indices[0], name_flags[0]),
                   (name_indices[1], name_flags[1]),
                   (name_indices[2], name_flags[2]),
                   (name_indices[3], name_flags[3])]

        self.network.account_manager.handle_operation(
            SetNamePatternFSM, self, self.__handle_set_name_pattern_resp,
            avatar_id, pattern)

    def __handle_set_name_pattern_resp(self, avatar_id):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_SET_NAME_PATTERN_ANSWER)
        datagram.add_uint32(avatar_id)
        datagram.add_uint8(0)
        self.handle_send_datagram(datagram)

    def handle_get_friends_list(self, di):
        account_id = self.get_account_id_from_channel_code(self.channel)
        avatar_id = self.get_avatar_id_from_connection_channel(self.channel)

        self.network.account_manager.handle_operation(
            LoadFriendsListFSM, self, self.__handle_get_friends_list_callback,
            account_id, avatar_id)

    def __handle_get_friends_list_callback(self):
        pass

    def handle_delete_avatar(self, di):
        try:
            avatar_id = di.get_uint32()
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                self._channel)
            return

        account_id = self.get_account_id_from_channel_code(self.channel)
        self.network.account_manager.handle_operation(
            DeleteAvatarFSM, self, self.__handle_delete_avatar_resp,
            account_id, avatar_id)

    def __handle_delete_avatar_resp(self, avatar_data):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_DELETE_AVATAR_RESP)
        datagram.add_uint8(0)
        datagram.add_uint16(len(avatar_data))

        for avatar in avatar_data:
            datagram.add_uint32(avatar.do_id)
            datagram.add_string(avatar.name_list[0])
            datagram.add_string(avatar.name_list[1])
            datagram.add_string(avatar.name_list[2])
            datagram.add_string(avatar.name_list[3])
            datagram.add_string(avatar.dna)
            datagram.add_uint8(avatar.position)
            datagram.add_uint8(avatar.name_index)

        self.handle_send_datagram(datagram)

    def handle_set_shard(self, di):
        try:
            shard_id = di.get_uint32()
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                self._channel)

            return

        avatar_id = self.get_avatar_id_from_connection_channel(self.channel)
        self._location_deferred_callback = util.DeferredCallback(
            self.handle_set_shard_callback)

        datagram = io.NetworkDatagram()
        datagram.add_header(avatar_id, self.channel,
                            types.STATESERVER_OBJECT_SET_AI)

        datagram.add_uint64(shard_id)
        self.network.handle_send_connection_datagram(datagram)

    def handle_set_shard_callback(self, do_id, old_parent_id, old_zone_id,
                                  new_parent_id, new_zone_id):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_GET_STATE_RESP)
        self.handle_send_datagram(datagram)

    def handle_set_zone(self, di):
        try:
            zone_id = di.get_uint16()
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                (self._channel))

            return

        avatar_id = self.get_avatar_id_from_connection_channel(self.channel)
        self._location_deferred_callback = util.DeferredCallback(
            self.handle_set_zone_callback)

        datagram = io.NetworkDatagram()
        datagram.add_header(avatar_id, self.channel,
                            types.STATESERVER_OBJECT_SET_ZONE)

        datagram.add_uint32(zone_id)
        self.network.handle_send_connection_datagram(datagram)

    def send_get_zones_objects(self, avatar_id, interest_zones):
        datagram = io.NetworkDatagram()
        datagram.add_header(avatar_id, self.channel,
                            types.STATESERVER_OBJECT_GET_ZONES_OBJECTS)

        # pack the interest zones
        datagram.add_uint16(len(interest_zones))
        for interest_zone in interest_zones:
            datagram.add_uint32(interest_zone)

        self.network.handle_send_connection_datagram(datagram)

    def get_in_street_branch(self, zone_id):
        if not ZoneUtil.isPlayground(zone_id):
            where = ZoneUtil.getWhereName(zone_id, True)
            return where == 'street'

        return False

    def get_vis_branch_zones(self, zone_id):
        branch_zone_id = ZoneUtil.getBranchZone(zone_id)
        dnaStore = self._dna_stores.get(branch_zone_id)
        if not dnaStore:
            dnaStore = DNAStorage()
            dnaFileName = genDNAFileName(branch_zone_id)
            loadDNAFileAI(dnaStore, dnaFileName)
            self._dna_stores[branch_zone_id] = dnaStore

        zoneVisDict = {}
        for i in xrange(dnaStore.getNumDNAVisGroupsAI()):
            groupFullName = dnaStore.getDNAVisGroupName(i)
            visGroup = dnaStore.getDNAVisGroupAI(i)
            visZoneId = int(extractGroupName(groupFullName))
            visZoneId = ZoneUtil.getTrueZoneId(visZoneId, zone_id)
            visibles = []
            for i in xrange(visGroup.getNumVisibles()):
                visibles.append(int(visGroup.visibles[i]))

            visibles.append(ZoneUtil.getBranchZone(visZoneId))
            zoneVisDict[visZoneId] = visibles

        return zoneVisDict[zone_id]

    def handle_set_zone_callback(self, do_id, old_parent_id, old_zone_id,
                                 new_parent_id, new_zone_id):
        if self._location_deferred_callback:
            self._location_deferred_callback.destroy()
            self._location_deferred_callback = None

        old_zone_in_street_branch = self.get_in_street_branch(old_zone_id)
        new_zone_in_street_branch = self.get_in_street_branch(new_zone_id)

        old_vis_zones = set()
        if old_zone_in_street_branch:
            old_branch_zone_id = ZoneUtil.getBranchZone(old_zone_id)
            old_vis_zones.update(self.get_vis_branch_zones(old_zone_id))
            for zone_id in self._interest_manager.interest_zones.difference(
                    old_vis_zones):
                self._interest_manager.remove_interest_zone(zone_id)
        else:
            self._interest_manager.remove_interest_zone(old_zone_id)
            if old_zone_id != OTP_ZONE_ID_OLD_QUIET_ZONE:
                self._interest_manager.remove_interest_zone(
                    OTP_ZONE_ID_OLD_QUIET_ZONE)

        new_vis_zones = set()
        if new_zone_in_street_branch:
            new_branch_zone_id = ZoneUtil.getBranchZone(old_zone_id)
            new_vis_zones.update(self.get_vis_branch_zones(new_zone_id))
            for zone_id in new_vis_zones.difference(old_vis_zones):
                self._interest_manager.add_interest_zone(zone_id)
        else:
            self._interest_manager.add_interest_zone(new_zone_id)
            if new_zone_id != OTP_ZONE_ID_OLD_QUIET_ZONE:
                self._interest_manager.add_interest_zone(
                    OTP_ZONE_ID_OLD_QUIET_ZONE)

        # clear the dna store for this branch zones since
        # they have left the street branch
        if (old_zone_in_street_branch and not new_zone_in_street_branch) or\
            (old_zone_in_street_branch and new_zone_in_street_branch and old_branch_zone_id != new_branch_zone_id):

            # remove the branch dna zone store
            branch_zone_id = ZoneUtil.getBranchZone(old_zone_id)
            del self._dna_stores[branch_zone_id]

            # remove the old street zones
            old_vis_zones = self.get_vis_branch_zones(old_zone_id)
            for zone_id in old_vis_zones:
                self._interest_manager.remove_interest_zone(zone_id)

        # destroy the objects we no longer have interest in
        for zone_id in dict(self._seen_objects):
            if self._interest_manager.has_interest_zone(zone_id):
                continue

            seen_objects = list(self._seen_objects[zone_id])
            for do_id in seen_objects:
                if do_id in self._owned_objects:
                    continue

                self.remove_seen_object(do_id)
                self.send_client_object_delete_resp(do_id)

        # only run a deferred callback if we moved to or from a non street zone
        if not old_zone_in_street_branch or not new_zone_in_street_branch:
            self._generate_deferred_callback = util.DeferredCallback(
                self.handle_set_zone_complete_callback, old_parent_id,
                old_zone_id, new_parent_id, new_zone_id)

        # request all of the objects in the zones we have interest in
        avatar_id = self.get_avatar_id_from_connection_channel(self.channel)
        interest_zones = list(self._interest_manager.interest_zones)
        self.send_get_zones_objects(avatar_id, interest_zones)

    def handle_object_location_ack(self, di):
        do_id = di.get_uint32()

        old_parent_id = di.get_uint32()
        old_zone_id = di.get_uint32()

        new_parent_id = di.get_uint32()
        new_zone_id = di.get_uint32()

        if self._location_deferred_callback:
            self._location_deferred_callback.callback(do_id, old_parent_id,
                                                      old_zone_id,
                                                      new_parent_id,
                                                      new_zone_id)

    def handle_object_get_zones_objects_resp(self, di):
        do_id = di.get_uint64()
        num_objects = di.get_uint16()
        for _ in xrange(num_objects):
            do_id = di.get_uint64()
            if self.has_seen_object(do_id):
                continue

            if do_id in self._owned_objects:
                continue

            self._pending_objects.append(do_id)

        if self._generate_deferred_callback:
            self._generate_deferred_callback.callback(False)

        if not num_objects:
            self._handle_interest_done()
            return

        if self.__interest_timeout_task:
            taskMgr.remove(self.__interest_timeout_task)
            self.__interest_timeout_task = None

        self.__interest_timeout_task = taskMgr.doMethodLater(
            self.network.interest_timeout, self._handle_interest_timeout,
            'interest-timeout-%d' % self.channel)

    def send_client_done_set_zone_resp(self, zone_id):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_DONE_SET_ZONE_RESP)
        datagram.add_uint16(zone_id)
        self.handle_send_datagram(datagram)

    def send_client_get_state_resp(self, zone_id):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_GET_STATE_RESP)
        datagram.pad_bytes(12)
        datagram.add_uint16(zone_id)
        self.handle_send_datagram(datagram)

    def handle_send_quiet_zone_resp(self, old_zone_id, new_zone_id):
        if not old_zone_id:
            self.send_client_done_set_zone_resp(new_zone_id)
        else:
            self.send_client_get_state_resp(new_zone_id)

        if old_zone_id and new_zone_id != OTP_ZONE_ID_OLD_QUIET_ZONE:
            self.send_client_done_set_zone_resp(new_zone_id)

    def handle_send_zone_resp(self, complete, old_zone_id, new_zone_id):
        if not complete:
            if not old_zone_id:
                self.send_client_done_set_zone_resp(new_zone_id)
            else:
                self.send_client_get_state_resp(new_zone_id)

        if old_zone_id and new_zone_id != OTP_ZONE_ID_OLD_QUIET_ZONE and complete:
            self.send_client_done_set_zone_resp(new_zone_id)

    def handle_set_zone_complete_callback(self, complete, old_parent_id,
                                          old_zone_id, new_parent_id,
                                          new_zone_id):
        if new_zone_id == OTP_ZONE_ID_OLD_QUIET_ZONE:
            if not complete:
                return

            self.handle_send_quiet_zone_resp(old_zone_id, new_zone_id)
        else:
            self.handle_send_zone_resp(complete, old_zone_id, new_zone_id)

    def handle_object_enter_owner(self, has_other, di):
        do_id = di.get_uint64()
        parent_id = di.get_uint64()
        zone_id = di.get_uint32()
        dc_id = di.get_uint16()

        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_GET_AVATAR_DETAILS_RESP)
        datagram.add_uint32(do_id)
        datagram.add_uint8(0)
        datagram.append_data(di.get_remaining_bytes())
        self.handle_send_datagram(datagram)

        self._owned_objects.append(do_id)

    def _handle_interest_done(self):
        if self.__interest_timeout_task:
            taskMgr.remove(self.__interest_timeout_task)
            self.__interest_timeout_task = None

        if self._generate_deferred_callback:
            self._generate_deferred_callback.callback(True)
            self._generate_deferred_callback.destroy()
            self._generate_deferred_callback = None

        self._pending_objects = []

    def _handle_interest_timeout(self, task):
        if len(self._pending_objects) > 0:
            self.notify.warning(
                'Interest handle timed out for channel: %d, forcing completion...'
                % self.channel)

        self._handle_interest_done()
        return task.done

    def handle_object_enter_location(self, has_other, di):
        do_id = di.get_uint64()
        parent_id = di.get_uint64()
        zone_id = di.get_uint32()
        dc_id = di.get_uint16()

        if self.has_seen_object(do_id):
            return

        if do_id in self._owned_objects:
            return

        if not self._interest_manager.has_interest_zone(zone_id):
            return

        dclass = self.network.dc_loader.dclasses_by_number[dc_id]
        assert (dclass != None)

        # if there is a generate for the toon and it's zone is the quiet zone,
        # then we never allow that generate through...
        if dclass.get_name(
        ) == 'DistributedToon' and zone_id == OTP_ZONE_ID_OLD_QUIET_ZONE:
            return

        datagram = io.NetworkDatagram()
        if not has_other:
            datagram.add_uint16(types.CLIENT_CREATE_OBJECT_REQUIRED)
        else:
            datagram.add_uint16(types.CLIENT_CREATE_OBJECT_REQUIRED_OTHER)

        datagram.add_uint16(dc_id)
        datagram.add_uint32(do_id)
        datagram.append_data(di.get_remaining_bytes())
        self.handle_send_datagram(datagram)

        seen_objects = self._seen_objects.setdefault(zone_id, [])
        seen_objects.append(do_id)

        # check to see if we have a pending interest handle that is looking
        # to see when this object generate has arrived.
        if do_id in self._pending_objects:
            self._pending_objects.remove(do_id)

            # if we have no more pending objects left to add,
            # then the interest timeout handle is now complete.
            if not self._pending_objects:
                self._handle_interest_done()
                return

    def send_client_object_delete_resp(self, do_id):
        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_OBJECT_DELETE_RESP)
        datagram.add_uint32(do_id)
        self.handle_send_datagram(datagram)

        self.remove_seen_object(do_id)

    def handle_object_update_field(self, di):
        try:
            do_id = di.get_uint32()
            field_id = di.get_uint16()
        except:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_TRUNCATED_DATAGRAM,
                'Received truncated datagram from channel: %d!' %
                self._channel)

            return

        datagram = io.NetworkDatagram()
        datagram.add_header(do_id, self.channel,
                            types.STATESERVER_OBJECT_UPDATE_FIELD)

        datagram.add_uint32(do_id)
        datagram.add_uint16(field_id)

        datagram.append_data(di.get_remaining_bytes())
        self.network.handle_send_connection_datagram(datagram)

    def handle_object_changing_location(self, di):
        do_id = di.get_uint32()
        new_parent_id = di.get_uint32()
        new_zone_id = di.get_uint32()

        # check to see if our owned object is being deleted, in this case
        # the shard our object is on, has been shutdown...
        if do_id in self._owned_objects and new_parent_id == 0 and new_zone_id == 0:
            self.handle_send_disconnect(
                types.CLIENT_DISCONNECT_SHARD_CLOSED,
                'The shard you were playing on has been unexpectedly closed!')
            return

        if not self.has_seen_object(do_id):
            return

        if self._interest_manager.has_interest_zone(new_zone_id):
            return

        self.send_client_object_delete_resp(do_id)

    def handle_object_update_field_resp(self, di):
        do_id = di.get_uint32()
        field_id = di.get_uint16()

        # check to see if we either have seen this object's generate already,
        # or that the object is one of our owned objects...
        can_send_update = self.has_seen_object(
            do_id
        ) or do_id in self._pending_objects or do_id in self._owned_objects
        if not can_send_update:
            return

        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_OBJECT_UPDATE_FIELD_RESP)
        datagram.add_uint32(do_id)
        datagram.add_uint16(field_id)
        datagram.append_data(di.get_remaining_bytes())
        self.handle_send_datagram(datagram)

    def handle_object_delete_ram(self, di):
        do_id = di.get_uint32()
        if not self.has_seen_object(do_id):
            return

        self.send_client_object_delete_resp(do_id)

    def shutdown(self):
        if self.__interest_timeout_task:
            taskMgr.remove(self.__interest_timeout_task)
            self.__interest_timeout_task = None

        if self.network.account_manager.has_fsm(self.allocated_channel):
            self.network.account_manager.stop_operation(self)

        if self.allocated_channel:
            self.network.channel_allocator.free(self.allocated_channel)

        io.NetworkHandler.shutdown(self)
Example #14
0
class LoadFriendsListFSM(ClientOperation):
    notify = notify.new_category('LoadAvatarFSM')

    def __init__(self, manager, client, callback, account_id, avatar_id):
        ClientOperation.__init__(self, manager, client, callback)

        self._account_id = account_id
        self._avatar_id = avatar_id

        self._dc_class = None
        self._fields = {}

        self._friends_list = {}
        self._pending_friends = []

    def enterStart(self):
        def response(dclass, fields):
            if not dclass:
                self.cleanup(False)
                return

            self._dc_class = dclass
            self._fields = fields
            self.request('QueryFriends')

        self.manager.network.database_interface.query_object(
            self.client.channel, types.DATABASE_CHANNEL, self._avatar_id,
            response,
            self.manager.network.dc_loader.dclasses_by_name['DistributedToon'])

    def exitStart(self):
        pass

    def enterQueryFriends(self):
        friends_list, = self._fields['setFriendsList']
        if not friends_list:
            self.cleanup(False)
            return

        self._pending_friends = {
            friend_id: friend_type
            for friend_id, friend_type in friends_list
        }
        for friend_id, friend_type in friends_list:

            def queryFriendCallback(dclass, fields, avatar_id=friend_id):
                if not dclass:
                    self.cleanup(False)
                    return

                self._friends_list[avatar_id] = [dclass, fields]
                del self._pending_friends[avatar_id]
                if not self._pending_friends:
                    self.request('LoadFriends')

            self.manager.network.database_interface.query_object(
                self.client.channel, types.DATABASE_CHANNEL, friend_id,
                queryFriendCallback, self.manager.network.dc_loader.
                dclasses_by_name['DistributedToon'])

    def exitQueryFriends(self):
        pass

    def enterLoadFriends(self):
        our_channel = self.client.get_puppet_connection_channel(
            self._avatar_id)
        for friend_id in self._friends_list:
            friend_channel = self.client.get_puppet_connection_channel(
                friend_id)
            friend_online = self.manager.network.get_handler_from_channel(
                friend_channel) is not None

            # tell us if they are online or not...
            datagram = io.NetworkDatagram()
            if friend_online:
                datagram.add_uint16(types.CLIENT_FRIEND_ONLINE)
            else:
                datagram.add_uint16(types.CLIENT_FRIEND_OFFLINE)

            datagram.add_uint32(friend_id)
            self.client.handle_send_datagram(datagram)

            # tell them that we are online if they are online...
            if friend_online:
                datagram = io.NetworkDatagram()
                datagram.add_header(friend_channel, our_channel,
                                    types.CLIENTAGENT_FRIEND_ONLINE)

                datagram.add_uint32(self._avatar_id)
                self.manager.network.handle_send_connection_datagram(datagram)

            # setup a post remove that will tell all of our friends
            # that we are offline when we disconnect...
            post_remove = io.NetworkDatagram()
            post_remove.add_header(friend_channel, our_channel,
                                   types.CLIENTAGENT_FRIEND_OFFLINE)

            post_remove.add_uint32(self._avatar_id)

            datagram = io.NetworkDatagram()
            datagram.add_control_header(self.client.allocated_channel,
                                        types.CONTROL_ADD_POST_REMOVE)

            datagram.append_data(post_remove.get_message())
            self.manager.network.handle_send_connection_datagram(datagram)

        datagram = io.NetworkDatagram()
        datagram.add_uint16(types.CLIENT_GET_FRIEND_LIST_RESP)
        datagram.add_uint8(0)
        datagram.add_uint16(len(self._friends_list))

        for friend_id in self._friends_list:
            dclass, fields = self._friends_list[friend_id]

            datagram.add_uint32(friend_id)
            datagram.add_string(fields['setName'][0])
            datagram.add_string(fields['setDNAString'][0])

        self.client.handle_send_datagram(datagram)

        # we're all done.
        self.cleanup(True)

    def exitLoadFriends(self):
        pass
Example #15
0
class DatabaseInterface(object):
    notify = notify.new_category('NetworkDatabaseInterface')

    def __init__(self, network):
        self._network = network

        self._context = 0

        self._callbacks = {}
        self._dclasses = {}

    def get_context(self):
        self._context = (self._context + 1) & 0xFFFFFFFF
        return self._context

    def create_object(self, channel_id, database_id, dclass, fields={}, callback=None):
        """
        Create an object in the specified database.
        database_id specifies the control channel of the target database.
        dclass specifies the class of the object to be created.
        fields is a dict with any fields that should be stored in the object on creation.
        callback will be called with callback(do_id) if specified. On failure, do_id is 0.
        """

        # Save the callback:
        ctx = self.get_context()
        self._callbacks[ctx] = callback

        # Pack up/count valid fields.
        field_packer = DCPacker()
        field_count = 0
        for k,v in fields.items():
            field = dclass.get_field_by_name(k)
            if not field:
                self.notify.error('Creation request for %s object contains an invalid field named %s' % (
                    dclass.get_name(), k))

            field_packer.raw_pack_uint16(field.get_number())
            field_packer.begin_pack(field)
            field.pack_args(field_packer, v)
            field_packer.end_pack()
            field_count += 1

        # Now generate and send the datagram:
        dg = io.NetworkDatagram()
        dg.add_header(database_id, channel_id, types.DBSERVER_CREATE_OBJECT)
        dg.add_uint32(ctx)
        dg.add_uint16(dclass.get_number())
        dg.add_uint16(field_count)
        dg.append_data(field_packer.get_string())
        self._network.handle_send_connection_datagram(dg)

    def handle_create_object_resp(self, di):
        ctx = di.get_uint32()
        do_id = di.get_uint32()

        if ctx not in self._callbacks:
            self.notify.warning('Received unexpected DBSERVER_CREATE_OBJECT_RESP (ctx %d, do_id %d)' % (
                ctx, do_id))

            return

        if self._callbacks[ctx]:
            self._callbacks[ctx](do_id)

        del self._callbacks[ctx]

    def query_object(self, channel_id, database_id, do_id, callback, dclass=None, field_names=()):
        """
        Query object `do_id` out of the database.
        On success, the callback will be invoked as callback(dclass, fields)
        where dclass is a DCClass instance and fields is a dict.
        On failure, the callback will be invoked as callback(None, None).
        """

        # Save the callback:
        ctx = self.get_context()
        self._callbacks[ctx] = callback
        self._dclasses[ctx] = dclass

        # Generate and send the datagram:
        dg = io.NetworkDatagram()

        if not field_names:
            dg.add_header(database_id, channel_id, types.DBSERVER_OBJECT_GET_ALL)
        else:
            # We need a dclass in order to convert the field names into field IDs:
            assert dclass is not None

            if len(field_names) > 1:
                dg.add_header(database_id, channel_id, types.DBSERVER_OBJECT_GET_FIELDS)
            else:
                dg.add_header(database_id, channel_id, types.DBSERVER_OBJECT_GET_FIELD)

        dg.add_uint32(ctx)
        dg.add_uint32(do_id)
        if len(field_names) > 1:
            dg.add_uint16(len(field_names))

        for field_name in field_names:
            field = dclass.get_field_by_name(field_name)
            if field is None:
                self.notify.error('Bad field named %s in query for %s object' % (
                    field_name, dclass.get_name()))

            dg.add_uint16(field.get_number())

        self._network.handle_send_connection_datagram(dg)

    def handle_query_object_resp(self, message_type, di):
        ctx = di.get_uint32()
        success = di.get_uint8()

        if ctx not in self._callbacks:
            self.notify.warning('Received unexpected %s (ctx %d)' % (message_type, ctx))
            return

        try:
            if not success:
                if self._callbacks[ctx]:
                    self._callbacks[ctx](None, None)

                return

            if message_type == types.DBSERVER_OBJECT_GET_ALL_RESP:
                dclass_id = di.get_uint16()
                dclass = self._network.dc_loader.dclasses_by_number.get(dclass_id)
            else:
                dclass = self._dclasses[ctx]

            if not dclass:
                self.notify.error('Received bad dclass %d in DBSERVER_OBJECT_GET_ALL_RESP' % (
                    dclass_id))

            if message_type == types.DBSERVER_OBJECT_GET_FIELD_RESP:
                field_count = 1
            else:
                field_count = di.get_uint16()

            field_packer = DCPacker()
            field_packer.set_unpack_data(di.get_remaining_bytes())
            fields = {}
            for x in xrange(field_count):
                field_id = field_packer.raw_unpack_uint16()
                field = dclass.get_field_by_index(field_id)

                if not field:
                    self.notify.error('Received bad field %d in query for %s object' % (
                        field_id, dclass.get_name()))

                field_packer.begin_unpack(field)
                fields[field.get_name()] = field.unpack_args(field_packer)
                field_packer.end_unpack()

            if self._callbacks[ctx]:
                self._callbacks[ctx](dclass, fields)

        finally:
            del self._callbacks[ctx]
            del self._dclasses[ctx]

    def update_object(self, channel_id, database_id, do_id, dclass, new_fields, old_fields=None, callback=None):
        """
        Update field(s) on an object, optionally with the requirement that the
        fields must match some old value.
        database_id and do_id represent the database control channel and object ID
        for the update request.
        new_fields is to be a dict of fieldname->value, representing the fields
        to add/change on the database object.
        old_fields, if specified, is a similarly-formatted dict that contains the
        expected older values. If the values do not match, the database will
        refuse to process the update. This is useful for guarding against race
        conditions.
        On success, the callback is called as callback(None).
        On failure, the callback is called as callback(dict), where dict contains
        the current object values. This is so that the updater can try again,
        basing its updates off of the new values.
        """

        # Ensure that the keys in new_fields and old_fields are the same if
        # old_fields is given...
        if old_fields is not None:
            if set(new_fields.keys()) != set(old_fields.keys()):
                self.notify.error('new_fields and old_fields must contain the same keys!')
                return

        field_packer = DCPacker()
        field_count = 0
        for k,v in new_fields.items():
            field = dclass.get_field_by_name(k)
            if not field:
                self.notify.error('Update for %s(%d) object contains invalid field named %s' % (
                    dclass.get_name(), do_id, k))

            field_packer.raw_pack_uint16(field.get_number())

            if old_fields is not None:
                # Pack the old values:
                field_packer.begin_pack(field)
                field.pack_args(field_packer, old_fields[k])
                field_packer.end_pack()

            field_packer.begin_pack(field)
            field.pack_args(field_packer, v)
            field_packer.end_pack()
            field_count += 1

        # Generate and send the datagram:
        dg = io.NetworkDatagram()
        if old_fields is not None:
            ctx = self.get_context()
            self._callbacks[ctx] = callback
            if field_count == 1:
                dg.add_header(database_id, channel_id, types.DBSERVER_OBJECT_SET_FIELD_IF_EQUALS)
            else:
                dg.add_header(database_id, channel_id, types.DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS)

            dg.add_uint32(ctx)
        else:
            if field_count == 1:
                dg.add_header(database_id, channel_id, types.DBSERVER_OBJECT_SET_FIELD)
            else:
                dg.add_header(database_id, channel_id, types.DBSERVER_OBJECT_SET_FIELDS)

        dg.add_uint32(do_id)
        if field_count != 1:
            dg.add_uint16(field_count)

        dg.append_data(field_packer.getString())
        self._network.handle_send_connection_datagram(dg)

        if old_fields is None and callback is not None:
            # Why oh why did they ask for a callback if there's no old_fields?
            # Oh well, better honor their request:
            callback(None)

    def handle_update_object_resp(self, di, multi):
        ctx = di.get_uint32()
        success = di.get_uint8()

        if ctx not in self._callbacks:
            self.notify.warning('Received unexpected DBSERVER_OBJECT_SET_FIELD(S)_IF_EQUALS_RESP (ctx %d)' % (
                ctx))

            return

        try:
            if success:
                if self._callbacks[ctx]:
                    self._callbacks[ctx](None)

                return

            if not di.get_remaining_size():
                # We failed due to other reasons.
                if self._callbacks[ctx]:
                    return self._callbacks[ctx]({})

            if multi:
                field_count = di.get_uint16()
            else:
                field_count = 1

            field_packer = DCPacker()
            field_packer.set_unpack_data(di.get_remaining_bytes())
            fields = {}
            for x in xrange(field_count):
                fieldId = field_packer.raw_pack_uint16()
                field = self._network.dc_loader.dc_file.get_field_by_index(fieldId)

                if not field:
                    self.notify.error('Received bad field %d in update failure response message' % (
                        fieldId))

                field_packer.begin_unpack(field)
                fields[field.get_name()] = field.unpack_args(field_packer)
                field_packer.end_unpack()

            if self._callbacks[ctx]:
                self._callbacks[ctx](fields)

        finally:
            del self._callbacks[ctx]

    def handle_datagram(self, message_type, di):
        if message_type == types.DBSERVER_CREATE_OBJECT_RESP:
            self.handle_create_object_resp(di)
        elif message_type in (types.DBSERVER_OBJECT_GET_ALL_RESP,
                              types.DBSERVER_OBJECT_GET_FIELDS_RESP,
                              types.DBSERVER_OBJECT_GET_FIELD_RESP):
            self.handle_query_object_resp(message_type, di)
        elif message_type == types.DBSERVER_OBJECT_SET_FIELD_IF_EQUALS_RESP:
            self.handle_update_object_resp(di, False)
        elif message_type == types.DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS_RESP:
            self.handle_update_object_resp(di, True)
class StateObjectManager(object):
    notify = notify.new_category('StateObjectManager')

    def __init__(self):
        self.objects = {}

    def has_object(self, do_id):
        return do_id in self.objects

    def add_object(self, state_object):
        if self.has_object(state_object.do_id):
            return

        self.objects[state_object.do_id] = state_object
        state_object.setup()

    def remove_object(self, state_object):
        if not self.has_object(state_object.do_id):
            return

        # kill off all of our child objects, in this case we do not want
        # to recurse our children's zone objects because this will be done
        # automatically here when we call `remove_object`...
        for child_object in state_object.get_all_zone_objects(
                recurse_children=False):
            self.remove_object(child_object)

        state_object.destroy()
        del self.objects[state_object.do_id]

    def get_object(self, do_id):
        return self.objects.get(do_id)

    def get_object_by_owner(self, owner_id):
        for state_object in list(self.objects.values()):
            if state_object.owner_id == owner_id:
                return state_object

        return None

    def handle_changing_location(self, state_object):
        assert (state_object != None)
        # tell the object's previous parent that we've moved away from under
        # them and are no longer in the previous location...
        if state_object.old_parent_id:
            state_object.handle_send_changing_location(
                state_object.old_parent_id)

        # also tell the object's new parent that they have changed locations under
        # them in a zone of their's...
        if state_object.parent_id:
            state_object.handle_send_changing_location(state_object.parent_id)

    def handle_updating_field(self,
                              state_object,
                              sender,
                              field,
                              field_args,
                              excludes=[]):
        assert (state_object != None)
        if not state_object.parent_id:
            self.notify.warning('Cannot handle updating field for object: %d, '
                                'object has no parent!' % state_object.do_id)

            return

        parent_object = self.get_object(state_object.parent_id)
        if not parent_object:
            self.notify.warning('Cannot handle updating field for object: %d, '
                                'object has no parent!' % state_object.do_id)

            return

        if not parent_object.has_child(state_object.do_id):
            self.notify.debug('Cannot handle updating field for object: %d, '
                              'we: %d are not this objects parent!' %
                              (state_object.do_id, parent_object.do_id))

            return

        child_zone_id = parent_object.get_zone_from_child(state_object.do_id)
        for zone_object in itertools.ifilter(
                lambda x: x.owner_id > 0 and x.do_id not in excludes,
                parent_object.get_all_zone_objects()):
            state_object.handle_send_update_field(zone_object.owner_id,
                                                  state_object.do_id, field,
                                                  field_args)
class StateServer(io.NetworkConnector, component.Component):
    notify = notify.new_category('StateServer')

    def __init__(self, dc_loader):
        connect_address = config.GetString('stateserver-connect-address',
                                           '127.0.0.1')
        connect_port = config.GetInt('stateserver-connect-port', 7100)

        io.NetworkConnector.__init__(self, dc_loader, connect_address,
                                     connect_port)
        self._channel = config.GetInt('stateserver-channel',
                                      types.STATESERVER_CHANNEL)

        self.shard_manager = ShardManager()
        self.object_manager = StateObjectManager()

    def handle_datagram(self, channel, sender, message_type, di):
        if message_type == types.STATESERVER_ADD_SHARD:
            self.handle_add_shard(sender, di)
        elif message_type == types.STATESERVER_UPDATE_SHARD:
            self.handle_update_shard(sender, di)
        elif message_type == types.STATESERVER_REMOVE_SHARD:
            self.handle_remove_shard(sender)
        elif message_type == types.STATESERVER_GET_SHARD_ALL:
            self.handle_send_shard_list(sender)
        elif message_type == types.STATESERVER_OBJECT_GENERATE_WITH_REQUIRED:
            self.handle_generate(sender, False, di)
        elif message_type == types.STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER:
            self.handle_generate(sender, True, di)
        elif message_type == types.STATESERVER_OBJECT_UPDATE_FIELD:
            self.handle_object_update_field(channel, sender, di)
        elif message_type == types.STATESERVER_OBJECT_DELETE_RAM:
            self.handle_delete_object(sender, di)
        else:
            self.handle_object_datagram(channel, sender, message_type, di)

    def handle_object_datagram(self, channel, sender, message_type, di):
        state_object = self.object_manager.get_object(channel)
        if not state_object:
            self.notify.warning('Received an unknown message type: '
                                '%d from channel: %d!' %
                                (message_type, sender))

            return

        state_object.handle_internal_datagram(sender, message_type, di)

    def handle_add_shard(self, ai_channel, di):
        shard = self.shard_manager.add_shard(ai_channel, di.get_uint32(),
                                             di.get_string(), di.get_uint32())

        # tell everyone that this shard is up and then update the shard info
        self.handle_broadcast_server_up(shard)
        self.handle_broadcast_shard_info()

        # setup a post remove to remove the shard when the AI disconnects
        post_remove = io.NetworkDatagram()
        post_remove.add_header(self._channel, shard.channel,
                               types.STATESERVER_REMOVE_SHARD)

        datagram = io.NetworkDatagram()
        datagram.add_control_header(shard.channel,
                                    types.CONTROL_ADD_POST_REMOVE)

        datagram.append_data(post_remove.get_message())
        self.handle_send_connection_datagram(datagram)

    def handle_update_shard(self, ai_channel, di):
        shard = self.shard_manager.get_shard(ai_channel)
        if not shard:
            self.notify.warning(
                'Cannot update shard: %d, shard does not exist!' % ai_channel)
            return

        shard.name = di.get_string()
        shard.population = di.get_uint32()

        # tell everyone about the shard's new info
        self.handle_broadcast_shard_info()

    def handle_remove_shard(self, ai_channel):
        shard = self.shard_manager.get_shard(ai_channel)
        if not shard:
            self.notify.debug(
                'Cannot remove shard: %d, shard does not exist!' % ai_channel)
            return

        shard_object = self.object_manager.get_object(shard.district_id)
        if not shard_object:
            self.notify.warning(
                'Failed to delete all shard objects for shard: '
                '%d with unknown district object: %d!' %
                (shard.channel, shard.district_id))

            return

        self.object_manager.remove_object(shard_object)
        self.shard_manager.remove_shard(shard)

        # tell everyone that this shard has been closed
        self.handle_broadcast_server_down(shard)

    def handle_send_shard_list(self, channel):
        datagram = io.NetworkDatagram()
        datagram.add_header(channel, self.channel,
                            types.STATESERVER_GET_SHARD_ALL_RESP)

        datagram.add_uint16(len(self.shard_manager.shards))
        for shard in list(self.shard_manager.shards.values()):
            datagram.add_uint32(shard.channel)
            datagram.add_string(shard.name)
            datagram.add_uint32(shard.population)

        self.handle_send_connection_datagram(datagram)

    def handle_send_server_up(self, channel, shard):
        assert (shard is not None)
        datagram = io.NetworkDatagram()
        datagram.add_header(channel, self.channel, types.STATESERVER_SERVER_UP)

        datagram.add_uint32(shard.channel)
        datagram.add_string(shard.name)
        self.handle_send_connection_datagram(datagram)

    def handle_send_server_down(self, channel, shard):
        assert (shard is not None)
        datagram = io.NetworkDatagram()
        datagram.add_header(channel, self.channel,
                            types.STATESERVER_SERVER_DOWN)

        datagram.add_uint32(shard.channel)
        self.handle_send_connection_datagram(datagram)

    def handle_broadcast_server_up(self, shard):
        for state_object in itertools.ifilter(
                lambda x: x.owner_id > 0,
                list(self.object_manager.objects.values())):
            self.handle_send_server_up(state_object.owner_id, shard)

    def handle_broadcast_server_down(self, shard):
        for state_object in itertools.ifilter(
                lambda x: x.owner_id > 0,
                list(self.object_manager.objects.values())):
            self.handle_send_server_down(state_object.owner_id, shard)

    def handle_broadcast_shard_info(self):
        for state_object in itertools.ifilter(
                lambda x: x.owner_id > 0,
                list(self.object_manager.objects.values())):
            self.handle_send_shard_list(state_object.owner_id)

    def handle_generate(self, sender, has_other, di):
        do_id = di.get_uint32()
        parent_id = di.get_uint32()
        zone_id = di.get_uint32()
        dc_id = di.get_uint16()

        if self.object_manager.has_object(do_id):
            self.notify.info('Failed to generate an already existing '
                             'object with do_id: %d!' % do_id)

            return

        dc_class = self.dc_loader.dclasses_by_number.get(dc_id)
        if not dc_class:
            self.notify.warning(
                'Failed to generate an object with do_id: %d, '
                'no dclass found for dc_id: %d!' % do_id, dc_id)

            return

        state_object = StateObject(self, self.object_manager, sender, do_id,
                                   parent_id, zone_id, dc_class, has_other, di)
        self.object_manager.add_object(state_object)

    def handle_object_update_field(self, channel, sender, di):
        do_id = di.get_uint32()
        if not di.get_remaining_size():
            self.notify.warning(
                'Cannot handle an field update for object: %d, '
                'truncated datagram!' % do_id)

            return

        state_object = self.object_manager.get_object(do_id)
        if not state_object:
            self.notify.debug('Cannot handle an field update for object: %d, '
                              'unknown object!' % do_id)

            return

        state_object.handle_update_field(channel, sender, di)

    def handle_delete_object(self, sender, di):
        do_id = di.get_uint32()
        state_object = self.object_manager.get_object(do_id)
        if not state_object:
            self.notify.debug(
                'Failed to delete object: %d, object does not exist!' % do_id)
            return

        self.object_manager.remove_object(state_object)
Example #18
0
class LoadAccountFSM(ClientOperation):
    notify = notify.new_category('LoadAccountFSM')

    def __init__(self, manager, client, callback, play_token):
        ClientOperation.__init__(self, manager, client, callback)

        self._play_token = play_token
        self._account_id = None

    def enterStart(self):
        if self._play_token not in self.manager.dbm:
            self.demand('Create')
            return

        self._account_id = int(self.manager.dbm[self._play_token])
        self.manager.network.database_interface.query_object(
            self.client.channel, types.DATABASE_CHANNEL, self._account_id,
            self.__account_loaded,
            self.manager.network.dc_loader.dclasses_by_name['Account'])

    def __account_loaded(self, dclass, fields):
        if not dclass:
            self.notify.warning(
                'Failed to load account: %d for channel: '
                '%d playtoken: %s!' %
                (self._account_id, self._client.channel, self._play_token))

            self.cleanup(False)
            return

        self.request('SetAccount')

    def exitStart(self):
        pass

    def enterCreate(self):
        fields = {
            'ACCOUNT_AV_SET': ([0] * 6, ),
            'BIRTH_DATE': ('', ),
            'BLAST_NAME': (self._play_token, ),
            'CREATED': (time.ctime(), ),
            'FIRST_NAME': ('', ),
            'LAST_LOGIN': ('', ),
            'LAST_NAME': ('', ),
            'PLAYED_MINUTES': ('', ),
            'PLAYED_MINUTES_PERIOD': ('', ),
            'HOUSE_ID_SET': ([0] * 6, ),
            'ESTATE_ID': (0, )
        }

        self.manager.network.database_interface.create_object(
            self.client.channel,
            types.DATABASE_CHANNEL,
            self.manager.network.dc_loader.dclasses_by_name['Account'],
            fields=fields,
            callback=self.__account_created)

    def __account_created(self, account_id):
        self._account_id = account_id
        if not self._account_id:
            self.notify.warning('Failed to create account for channel: '
                                '%d playtoken: %s!' %
                                (self._client.channel, self._play_token))

            self.cleanup(False)
            return

        self.manager.dbm[self._play_token] = str(self._account_id)
        self.manager.dbm.sync()

        self.request('SetAccount')

    def exitCreate(self):
        pass

    def enterSetAccount(self):
        # mark them as been authenticated
        self._client.authenticated = True

        # add this connection to the account channel
        channel = self.client.get_account_connection_channel(self._account_id)
        self.client.register_for_channel(channel)

        # add them to the account channel
        channel = self._account_id << 32
        self.client.handle_set_channel_id(channel)

        # we're all done.
        self.cleanup(True, self._play_token)

    def exitSetAccount(self):
        pass
class StateObject(object):
    notify = notify.new_category('StateObject')

    def __init__(self, network, object_manager, ai_channel, do_id, parent_id,
                 zone_id, dc_class, has_other, di):
        self._network = network
        self.object_manager = object_manager

        self._do_id = do_id

        self._old_ai_channel = 0
        self._ai_channel = ai_channel

        self._old_owner_id = 0
        self._owner_id = 0

        self._old_parent_id = 0
        self._parent_id = parent_id

        self._old_zone_id = 0
        self._zone_id = zone_id

        self._dc_class = dc_class
        self._has_other = has_other

        self._required_fields = {}
        self._other_fields = {}

        self._zone_objects = {}

        field_packer = DCPacker()
        field_packer.set_unpack_data(di.get_remaining_bytes())

        # unpack all of the initial "required" fields
        for field_index in xrange(self._dc_class.get_num_inherited_fields()):
            field = self._dc_class.get_inherited_field(field_index)
            if not field:
                self.notify.error('Failed to unpack required field: %d '
                                  'dclass: %s, unknown field!' %
                                  (field_index, self._dc_class.get_name()))

            if not field.is_required():
                continue

            field_packer.begin_unpack(field)
            field_args = field.unpack_args(field_packer)
            field_packer.end_unpack()

            self._required_fields[field.get_number()] = field_args

        # unpack all of the initial "other" fields, if they are specified
        if self._has_other:
            num_fields = field_packer.raw_unpack_uint16()
            for _ in xrange(num_fields):
                field_id = field_packer.raw_unpack_uint16()
                field = self._dc_class.get_field_by_index(field_id)
                if not field:
                    self.notify.error('Failed to unpack other field: %d '
                                      'dclass: %s, unknown field!' %
                                      (field_id, self._dc_class.get_name()))

                if not field.is_ram():
                    continue

                field_packer.begin_unpack(field)
                field_args = field.unpack_args(field_packer)
                field_packer.end_unpack()

                self._other_fields[field.get_number()] = field_args

        self._network.register_for_channel(self._do_id)

    @property
    def do_id(self):
        return self._do_id

    @property
    def old_ai_channel(self):
        return self._old_ai_channel

    @property
    def ai_channel(self):
        return self._ai_channel

    @ai_channel.setter
    def ai_channel(self, ai_channel):
        self._old_ai_channel = self._ai_channel
        self._ai_channel = ai_channel

    @property
    def old_owner_id(self):
        return self._old_owner_id

    @property
    def owner_id(self):
        return self._owner_id

    @owner_id.setter
    def owner_id(self, owner_id):
        self._old_owner_id = self._owner_id
        self._owner_id = owner_id

    @property
    def old_parent_id(self):
        return self._old_parent_id

    @property
    def parent_id(self):
        return self._parent_id

    @parent_id.setter
    def parent_id(self, parent_id):
        self._old_parent_id = self._parent_id
        self._parent_id = parent_id

    @property
    def old_zone_id(self):
        return self._old_zone_id

    @property
    def zone_id(self):
        return self._zone_id

    @zone_id.setter
    def zone_id(self, zone_id):
        self._old_zone_id = self._zone_id
        self._zone_id = zone_id

    @property
    def dc_class(self):
        return self._dc_class

    @property
    def has_other(self):
        return self._has_other

    def __repr__(self):
        return '%s(%d): ai_channel: %d, owner_id: %d, parent_id: %d, zone_id: %d' % (
            self._dc_class.get_name(), self._do_id, self._ai_channel,
            self._owner_id, self._parent_id, self._zone_id)

    def has_child(self, child_do_id):
        return self.get_zone_from_child(child_do_id) is not None

    def has_child_in_zone(self, child_do_id, zone_id):
        return self.get_zone_from_child(child_do_id) == zone_id

    def add_child_in_zone(self, child_do_id, zone_id):
        zone_objects = self._zone_objects.setdefault(zone_id, [])
        zone_objects.append(child_do_id)

    def remove_child_from_zone(self, child_do_id, zone_id):
        zone_objects = self._zone_objects.get(zone_id, None)
        assert (zone_objects != None)
        if child_do_id in zone_objects:
            zone_objects.remove(child_do_id)

        if not len(zone_objects):
            del self._zone_objects[zone_id]

    def get_zone_from_child(self, child_do_id):
        for zone_id, zone_objects in list(self._zone_objects.items()):
            if child_do_id in zone_objects:
                return zone_id

        return None

    def get_zone_objects(self, zone_id):
        if zone_id not in self._zone_objects:
            return []

        zone_objects = []
        for do_id in list(self._zone_objects[zone_id]):
            zone_object = self._network.object_manager.get_object(do_id)
            if not zone_object:
                continue

            zone_objects.append(zone_object)

        return zone_objects

    def get_all_zone_objects(self, recurse_children=True):
        zone_objects = []
        for zone_id in list(self._zone_objects):
            zone_objects.extend(self.get_zone_objects(zone_id))

        # also get all of our children's zone objects
        if recurse_children:
            for zone_object in list(zone_objects):
                zone_objects.extend(zone_object.get_all_zone_objects())

        return zone_objects

    def get_zones_objects(self, zone_ids):
        zone_objects = []
        for zone_id in zone_ids:
            zone_objects.extend(self.get_zone_objects(zone_id))

        return zone_objects

    def append_required_data(self, datagram, broadcast_only=True):
        sorted_fields = collections.OrderedDict(
            sorted(self._required_fields.items()))

        field_packer = DCPacker()
        for field_index, field_args in sorted_fields.items():
            field = self._dc_class.get_field_by_index(field_index)
            if not field:
                self.notify.error(
                    'Failed to append required data for field: %d '
                    'dclass: %s, unknown field!' %
                    (field_index, self._dc_class.get_name()))

            if broadcast_only and not field.is_broadcast():
                continue

            field_packer.begin_pack(field)
            field.pack_args(field_packer, field_args)
            field_packer.end_pack()

        datagram.append_data(field_packer.get_string())

    def append_other_data(self, datagram):
        field_packer = DCPacker()
        for field_index, field_args in list(self._other_fields.items()):
            field = self._dc_class.get_field_by_index(field_index)
            if not field:
                self.notify.error('Failed to append other data for field: %d '
                                  'dclass: %s, unknown field!' %
                                  (field_index, self._dc_class.get_name()))

            field_packer.raw_pack_uint16(field.get_number())
            field_packer.begin_pack(field)
            field.pack_args(field_packer, field_args)
            field_packer.end_pack()

        datagram.add_uint16(len(self._other_fields))
        datagram.append_data(field_packer.get_string())

    def setup(self):
        self.object_manager.handle_changing_location(self)

    def handle_internal_datagram(self, sender, message_type, di):
        if message_type == types.STATESERVER_OBJECT_SET_OWNER:
            self.handle_set_owner(sender, di)
        elif message_type == types.STATESERVER_OBJECT_SET_AI:
            self.handle_set_ai(sender, di)
        elif message_type == types.STATESERVER_OBJECT_SET_ZONE:
            self.handle_set_zone(sender, di)
        elif message_type == types.STATESERVER_OBJECT_CHANGING_LOCATION:
            self.handle_changing_location(di.get_uint32(), di.get_uint32(),
                                          di.get_uint32())
        elif message_type == types.STATESERVER_OBJECT_SET_LOCATION:
            self.handle_set_location(sender, di)
        elif message_type == types.STATESERVER_OBJECT_GET_ZONES_OBJECTS:
            self.handle_get_zones_objects(sender, di)
        else:
            self.notify.warning('Received unknown message type: %d '
                                'for object %d!' % (message_type, self._do_id))

            return

    def handle_send_changing_owner(self, channel, old_owner_id, new_owner_id):
        datagram = io.NetworkDatagram()
        datagram.add_header(channel, self._do_id,
                            types.STATESERVER_OBJECT_CHANGING_OWNER)

        datagram.add_uint64(self._do_id)
        datagram.add_uint64(new_owner_id)
        datagram.add_uint64(old_owner_id)
        self._network.handle_send_connection_datagram(datagram)

    def handle_send_owner_entry(self, channel):
        datagram = io.NetworkDatagram()
        if not self._has_other:
            datagram.add_header(
                channel, self._do_id,
                types.STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED)
        else:
            datagram.add_header(
                channel, self._do_id,
                types.STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER)

        datagram.add_uint64(self._do_id)
        datagram.add_uint64(self._parent_id)
        datagram.add_uint32(self._zone_id)
        datagram.add_uint16(self._dc_class.get_number())

        self.append_required_data(datagram, broadcast_only=False)
        if self._has_other:
            self.append_other_data(datagram)

        self._network.handle_send_connection_datagram(datagram)

    def handle_set_owner(self, sender, di):
        new_owner_id = di.get_uint64()
        if new_owner_id == self._owner_id:
            return

        self.owner_id = new_owner_id
        self.handle_send_owner_entry(self._owner_id)
        self.handle_send_changing_owner(self._old_owner_id, self._old_owner_id,
                                        self._owner_id)

    def handle_send_changing_ai(self, channel):
        datagram = io.NetworkDatagram()
        datagram.add_header(channel, self._do_id,
                            types.STATESERVER_OBJECT_CHANGING_AI)

        datagram.add_uint64(self._do_id)
        datagram.add_uint64(self._old_ai_channel)
        datagram.add_uint64(self._ai_channel)
        self._network.handle_send_connection_datagram(datagram)

    def handle_send_ai_entry(self, ai_channel):
        datagram = io.NetworkDatagram()
        if not self._has_other:
            datagram.add_header(
                ai_channel, self._do_id,
                types.STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED)
        else:
            datagram.add_header(
                ai_channel, self._do_id,
                types.STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED_OTHER)

        datagram.add_uint64(self._do_id)
        datagram.add_uint64(self._parent_id)
        datagram.add_uint32(self._zone_id)
        datagram.add_uint16(self._dc_class.get_number())

        self.append_required_data(datagram, broadcast_only=not self._owner_id)
        if self._has_other:
            self.append_other_data(datagram)

        self._network.handle_send_connection_datagram(datagram)

    def handle_set_ai(self, sender, di):
        new_ai_channel = di.get_uint64()
        if new_ai_channel == self._ai_channel:
            return

        shard = self._network.shard_manager.get_shard(new_ai_channel)
        if not shard:
            self.notify.warning('Failed to set new AI: %d for object %d, '
                                'no AI was found with that channel!' %
                                (new_ai_channel, self._do_id))

            return

        self.ai_channel = new_ai_channel
        if self._owner_id:
            self.parent_id = shard.district_id

        self.handle_send_ai_entry(self._ai_channel)
        self.handle_send_changing_ai(self.old_ai_channel)
        self.object_manager.handle_changing_location(self)

    def handle_send_changing_location(self, channel):
        datagram = io.NetworkDatagram()
        datagram.add_header(channel, self._do_id,
                            types.STATESERVER_OBJECT_CHANGING_LOCATION)

        datagram.add_uint32(self._do_id)
        datagram.add_uint32(self._parent_id)
        datagram.add_uint32(self._zone_id)
        self._network.handle_send_connection_datagram(datagram)

    def handle_set_zone(self, sender, di):
        new_zone_id = di.get_uint32()
        self.zone_id = new_zone_id
        self.handle_send_changing_location(self._ai_channel)
        self.object_manager.handle_changing_location(self)

    def handle_send_location_entry(self, channel):
        datagram = io.NetworkDatagram()
        if not self._has_other:
            datagram.add_header(
                channel, self._do_id,
                types.STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED)
        else:
            datagram.add_header(
                channel, self._do_id,
                types.STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER)

        datagram.add_uint64(self._do_id)
        datagram.add_uint64(self._parent_id)
        datagram.add_uint32(self._zone_id)
        datagram.add_uint16(self._dc_class.get_number())

        self.append_required_data(datagram)
        if self._has_other:
            self.append_other_data(datagram)

        self._network.handle_send_connection_datagram(datagram)

    def handle_send_departure(self, channel):
        datagram = io.NetworkDatagram()
        datagram.add_header(channel, self._do_id,
                            types.STATESERVER_OBJECT_DELETE_RAM)

        datagram.add_uint32(self._do_id)
        self._network.handle_send_connection_datagram(datagram)

    def handle_send_object_location_ack(self, channel):
        datagram = io.NetworkDatagram()
        datagram.add_header(channel, self._do_id,
                            types.STATESERVER_OBJECT_LOCATION_ACK)

        datagram.add_uint32(self._do_id)
        datagram.add_uint32(self._old_parent_id)
        datagram.add_uint32(self._old_zone_id)
        datagram.add_uint32(self._parent_id)
        datagram.add_uint32(self._zone_id)
        self._network.handle_send_connection_datagram(datagram)

    def handle_changing_location(self, child_do_id, new_parent_id,
                                 new_zone_id):
        # retrieve this object from it's do_id, if we cannot find this object in the do_id to do
        # dictionary, then this is an invalid object...
        child_object = self.object_manager.get_object(child_do_id)
        if not child_object:
            return

        send_location_departure = False
        send_location_entry = False
        child_zone_id = 0
        if self.has_child(child_object.do_id):
            child_zone_id = self.get_zone_from_child(child_object.do_id)
            if new_parent_id != self._do_id:
                self.remove_child_from_zone(child_object.do_id, child_zone_id)
                send_location_departure = True
            elif new_zone_id != child_zone_id:
                self.remove_child_from_zone(child_object.do_id, child_zone_id)
                self.add_child_in_zone(child_object.do_id, new_zone_id)
                send_location_departure = True
                send_location_entry = True
        else:
            self.add_child_in_zone(child_object.do_id, new_zone_id)
            send_location_entry = True

        for zone_object in itertools.ifilter(lambda x: x.owner_id > 0,
                                             self.get_all_zone_objects()):
            if send_location_departure:
                child_object.handle_send_changing_location(
                    zone_object.owner_id)

            if send_location_entry:
                child_object.handle_send_location_entry(zone_object.owner_id)

        # acknowledge the object's location change
        if child_object.owner_id:
            child_object.handle_send_object_location_ack(child_object.owner_id)

    def handle_set_location(self, sender, di):
        new_parent_id = di.get_uint32()
        new_zone_id = di.get_uint32()
        if new_parent_id == self._parent_id and new_zone_id == self._zone_id:
            return

        self.parent_id = new_parent_id
        self.zone_id = new_zone_id
        self.object_manager.handle_changing_location(self)

    def handle_get_zones_objects(self, sender, di):
        zone_ids = [di.get_uint32() for _ in xrange(di.get_uint16())]
        if not self._owner_id:
            self.notify.warning('Cannot get zone objects for object: %d, '
                                'object does not have an owner!' % self._do_id)

            return

        parent_object = self._network.object_manager.get_object(
            self._parent_id)
        if not parent_object:
            self.notify.warning('Cannot get zone objects for object: %d, '
                                'object has no parent!' % self._do_id)

            return

        # filter out our own object from the zone list, as we do not want
        # to send our own object because we have reference to it locally...
        zone_objects = list(
            itertools.ifilter(
                lambda x: x.do_id != self._do_id and x.owner_id != self.
                _owner_id, parent_object.get_zones_objects(zone_ids)))

        # tell the Client Agent that the they should expect this
        # many objects to have been generated before completing the zone change...
        datagram = io.NetworkDatagram()
        datagram.add_header(self._owner_id, self._do_id,
                            types.STATESERVER_OBJECT_GET_ZONES_OBJECTS_RESP)

        datagram.add_uint64(self._do_id)
        datagram.add_uint16(len(zone_objects))
        for zone_object in zone_objects:
            datagram.add_uint64(zone_object.do_id)

        self._network.handle_send_connection_datagram(datagram)

        # finally once we've sent the objects we expect the client,
        # to see before completing the interest change, start sending object generates...
        for zone_object in zone_objects:
            zone_object.handle_send_location_entry(self._owner_id)

    def handle_send_update_field(self, channel, sender, field, field_args):
        datagram = io.NetworkDatagram()
        datagram.add_header(channel, sender,
                            types.STATESERVER_OBJECT_UPDATE_FIELD)

        datagram.add_uint32(self._do_id)
        datagram.add_uint16(field.get_number())

        field_packer = DCPacker()
        field_packer.begin_pack(field)
        if field_args is not None:
            field.pack_args(field_packer, field_args)

        field_packer.end_pack()

        datagram.append_data(field_packer.get_string())
        self._network.handle_send_connection_datagram(datagram)

    def handle_send_save_field(self, field, field_args):
        datagram = io.NetworkDatagram()
        datagram.add_header(types.DATABASE_CHANNEL, self._do_id,
                            types.DBSERVER_OBJECT_SET_FIELD)

        datagram.add_uint32(self._do_id)

        field_packer = DCPacker()
        field_packer.raw_pack_uint16(field.get_number())
        field_packer.begin_pack(field)
        field.pack_args(field_packer, field_args)
        field_packer.end_pack()

        datagram.append_data(field_packer.get_string())
        self._network.handle_send_connection_datagram(datagram)

    def handle_update_field(self, channel, sender, di):
        field_id = di.get_uint16()
        field = self._dc_class.get_field_by_index(field_id)
        if not field:
            self.notify.warning('Failed to update field: %d dclass: %s, '
                                'unknown field!' %
                                (field_id, self._dc_class.get_name()))

            return

        datagram = io.NetworkDatagram()
        datagram.append_data(di.get_remaining_bytes())
        di = io.NetworkDatagramIterator(datagram)

        # if the iterator is empty, this means that the field
        # has no arguents and that we should not attempt to update it...
        if di.get_remaining_size():
            field_packer = DCPacker()
            field_packer.set_unpack_data(di.get_remaining_bytes())

            try:
                field_packer.begin_unpack(field)
                field_args = field.unpack_args(field_packer)
                field_packer.end_unpack()
            except RuntimeError:
                # apparently we failed to unpack the arguments for
                # this field we recieved, ignore the update...
                return
        else:
            field_args = None

        di = io.NetworkDatagramIterator(datagram)

        # check to ensure that the sender of this update is either an owner
        # of an existing object, or that they are an AI shard channel:
        state_object = self._network.object_manager.get_object_by_owner(sender)
        if state_object is not None:
            avatar_id = self._network.get_avatar_id_from_connection_channel(
                sender)
            if not avatar_id:
                self.notify.warning(
                    'Cannot handle field update for field: %s dclass: %s, '
                    'unknown avatar: %d!' %
                    (field.get_name(), self._dc_class.get_name(), avatar_id))

                return

            # check to ensure the client can send this field, if the field
            # is marked ownsend; the field can only be sent by the owner of the object,
            # if the field is marked clsend the field is sendable always. Otherwise
            # if the client sends the field and it is not marked either of these,
            # the field update is invalid and the field is not sendable by a client...
            if field.is_ownsend():
                if sender != self._owner_id:
                    self.notify.warning(
                        'Cannot handle field update for field: %s '
                        'dclass: %s, field not sendable!' %
                        (field.get_name(), self._dc_class.get_name()))

                    return
            else:
                if not field.is_clsend():
                    self.notify.warning(
                        'Cannot handle field update for field: %s '
                        'dclass: %s, field not sendable!' %
                        (field.get_name(), self._dc_class.get_name()))

                    return

            # we must always send this update to the other receiver,
            # so that they get the field update always even if the field
            # is broadcasted to other objects in the same interest...
            self.handle_send_update_field(self._ai_channel, sender, field,
                                          field_args)

            # if the field is marked broadcast, then we can proceed to broadcast
            # this field to any other objects in our interest.
            if field.is_broadcast():
                self.object_manager.handle_updating_field(self,
                                                          sender,
                                                          field,
                                                          field_args,
                                                          excludes=[avatar_id])

            if field_args is not None:
                # the client has sent an broadcast field that is marked ram,
                # store this field since it passes both the is clsend or is ownsend tests...
                if field.is_ram():
                    # ensure the object the client sent the field update for
                    # has other fields...
                    if not self._has_other:
                        return

                    # check to see if this field is a required field, if it is then
                    # this means it should be stored as a required field....
                    if field.is_required():
                        self._required_fields[field.get_number()] = field_args
                    else:
                        self._other_fields[field.get_number()] = field_args
        else:
            if not self._network.shard_manager.has_shard(sender):
                self.notify.warning(
                    'Cannot handle field update for field: %s dclass: %s, '
                    'unknown sender with channel: %d!' %
                    (field.get_name(), self._dc_class.get_name(), sender))

                return

            # we must always send this update to the other receiver,
            # so that they get the field update always even if the field
            # is broadcasted to other objects in the same interest...
            self.handle_send_update_field(self._owner_id, self._ai_channel,
                                          field, field_args)

            # if the field is marked broadcast, then we can proceed to broadcast
            # this field to any other objects in our interest.
            if field.is_broadcast():
                self.object_manager.handle_updating_field(
                    self,
                    self._parent_id,
                    field,
                    field_args,
                    excludes=[self.do_id])

            if field_args is not None:
                # if the AI object sends specifically other (ram) fields for this object,
                # this means the object now has other fields...
                if field.is_ram():
                    # check to see if this field is a required field, if it is then
                    # this means it should be stored as a required field....
                    if field.is_required():
                        self._required_fields[field.get_number()] = field_args
                    else:
                        self._other_fields[field.get_number()] = field_args

                    # the object now has other fields, let's update the object's has_other
                    # value so that generates will be sent including the other fields...
                    self._has_other = True

                # check to see if the field is marked db, this means that we send the field
                # to the database to override any current fields with that value...
                if field.is_db():
                    self.handle_send_save_field(field, field_args)

    def destroy(self):
        self.parent_id = 0
        self.zone_id = 0

        self.handle_send_departure(self._ai_channel)
        old_parent = self.object_manager.get_object(self._old_parent_id)
        if old_parent is not None:
            old_parent.handle_changing_location(self._do_id, self._parent_id,
                                                self._zone_id)

        # kill our object on the ClientAgent if we are a client owned object
        if self._owner_id:
            self.handle_send_changing_location(self._owner_id)

        self._network.unregister_for_channel(self._do_id)

        self.owner_id = 0

        self._required_fields = {}
        self._other_fields = {}

        self._zone_objects = {}
Example #20
0
class DatabaseCreateFSM(DatabaseOperationFSM):
    notify = notify.new_category('DatabaseCreateFSM')

    def __init__(self, *args, **kwargs):
        self._context = kwargs.pop('context', 0)
        self._dc_id = kwargs.pop('dc_id', 0)
        self._field_count = kwargs.pop('field_count', 0)
        self._field_data = kwargs.pop('field_data', None)

        DatabaseOperationFSM.__init__(self, *args, **kwargs)

    def enterStart(self):
        dc_class = self.network.dc_loader.dclasses_by_number.get(self._dc_id)
        if not dc_class:
            self.notify.error('Failed to create object: %d context: %d, '
                              'unknown dclass!' % (self._dc_id, self._context))

        self._do_id = self.network.backend.allocator.allocate()
        if not self._do_id:
            self.notify.warning('Failed to create object: %d context: %d, '
                                'could not allocate new do_id!' %
                                (self._dc_class, self._context))

            self.cleanup(False, self._context, 0)
            return

        if self.network.backend.has_file('%d' % self._do_id):
            self.notify.warning(
                'Failed to create object: %d context: %d, '
                'could not create new entry with same do_id: %d!' %
                (self._dc_class, self._context, self._do_id))

            self.cleanup(False, self._context, 0)
            return

        file_object = self.network.backend.open_file('%d' % self._do_id)
        file_object.save()

        file_object.set_value('dclass', dc_class.get_name())
        file_object.set_value('do_id', self._do_id)

        fields = {}
        field_packer = DCPacker()
        field_packer.set_unpack_data(self._field_data)

        for _ in xrange(self._field_count):
            field_id = field_packer.raw_unpack_uint16()
            field = dc_class.get_field_by_index(field_id)
            if not field:
                self.notify.error('Failed to unpack field: %d dclass: %s, '
                                  'invalid field!' %
                                  (field_id, dc_class.get_name()))

            field_packer.begin_unpack(field)
            field_args = field.unpack_args(field_packer)
            field_packer.end_unpack()
            if not field_args:
                self.notify.error(
                    'Failed to unpack field args for field: %d dclass: %s, '
                    'invalid result!' %
                    (field.get_name(), dc_class.get_name()))

            fields[field.get_name()] = field_args

        for field_index in xrange(dc_class.get_num_inherited_fields()):
            field_packer = DCPacker()
            field = dc_class.get_inherited_field(field_index)

            if not field or field.get_name() in fields:
                continue

            if not field.is_db() or not field.has_default_value():
                continue

            field_packer.set_unpack_data(field.get_default_value())
            field_packer.begin_unpack(field)
            field_args = field.unpack_args(field_packer)
            field_packer.end_unpack()
            if not field_args:
                self.notify.error(
                    'Failed to unpack field args for field: %d dclass: %s, '
                    'invalid result!' %
                    (field.get_name(), dc_class.get_name()))

            fields[field.get_name()] = field_args

        file_object.set_value('fields', fields)
        self.network.backend.close_file(file_object)
        self.network.backend.tracker.set_value('next', self._do_id + 1)
        self.cleanup(True, self._context, self._do_id)

    def exitStart(self):
        pass
Example #21
0
class DatabaseSetFieldFSM(DatabaseOperationFSM):
    notify = notify.new_category('DatabaseSetFieldFSM')

    def __init__(self, *args, **kwargs):
        self._do_id = kwargs.pop('do_id', 0)
        self._context = kwargs.pop('context', 0)
        self._field_data = kwargs.pop('field_data', None)

        DatabaseOperationFSM.__init__(self, *args, **kwargs)

    def enterStart(self):
        file_object = self.network.backend.open_file('%d' % self._do_id)
        if not file_object:
            self.notify.warning('Failed to set fields for object: %d, '
                                'unknown object!' % self._do_id)

            self.cleanup(False, self._context, None, None)
            return

        dc_name = file_object.get_value('dclass')
        dc_class = self.network.dc_loader.dclasses_by_name.get(dc_name)
        if not dc_class:
            self.notify.warning('Failed to set fields for object: %d, '
                                'unknown dclass: %s!' % (self._do_id, dc_name))

            self.cleanup(False, self._context, None, None)
            return

        field_packer = DCPacker()
        field_packer.set_unpack_data(self._field_data)
        field_id = field_packer.raw_unpack_uint16()
        field = dc_class.get_field_by_index(field_id)
        if not field:
            self.notify.error('Failed to unpack field: %d dclass: %s, '
                              'invalid field!' %
                              (field_id, dc_class.get_name()))

        fields = file_object.get_value('fields')
        if not fields:
            self.notify.warning('Failed to set fields for object: %d, '
                                'invalid fields!' % self._do_id)

            self.cleanup(False, self._context, field_id, self._field_data)
            return

        field_packer.begin_unpack(field)
        field_args = field.unpack_args(field_packer)
        field_packer.end_unpack()
        if not field_args:
            self.notify.error(
                'Failed to unpack field args for field: %d dclass: %s, '
                'invalid result!' % (field.get_name(), dc_class.get_name()))

        fields[field.get_name()] = field_args
        file_object.set_value('fields', fields)

        self.network.backend.close_file(file_object)
        self.cleanup(True, 0, None)

    def exitStart(self):
        pass
Example #22
0
class NetworkDCLoader(object):
    notify = notify.new_category('NetworkDCLoader')

    def __init__(self):
        self._dc_file = DCFile()
        self._dc_suffix = ''

        self._dclasses_by_name = {}
        self._dclasses_by_number = {}

        self._hash_value = 0

    @property
    def dc_file(self):
        return self._dc_file

    @property
    def dc_suffix(self):
        return self._dc_suffix

    @property
    def dclasses_by_name(self):
        return self._dclasses_by_name

    @property
    def dclasses_by_number(self):
        return self._dclasses_by_number

    @property
    def hash_value(self):
        return self._hash_value

    def read_dc_files(self, dc_file_names=None):
        dc_imports = {}
        if dc_file_names == None:
            read_result = self._dc_file.read_all()
            if not read_result:
                self.notify.error('Could not read dc file.')
        else:
            for dc_fileName in dc_file_names:
                pathname = Filename(dc_fileName)
                read_result = self._dc_file.read(pathname)
                if not read_result:
                    self.notify.error('Could not read dc file: %s' % pathname)

        self._hash_value = self._dc_file.get_hash()

        # Now get the class definition for the classes named in the DC
        # file.
        for i in range(self._dc_file.get_num_classes()):
            dclass = self._dc_file.get_class(i)
            number = dclass.get_number()
            class_name = dclass.get_name() + self._dc_suffix

            # Does the class have a definition defined in the newly
            # imported namespace?
            class_def = dc_imports.get(class_name)

            # Also try it without the dc_suffix.
            if class_def == None:
                class_name = dclass.get_name()
                class_def = dc_imports.get(class_name)

            if class_def == None:
                self.notify.debug('No class definition for %s.' % class_name)
            else:
                if inspect.ismodule(class_def):
                    if not hasattr(class_def, class_name):
                        self.notify.error(
                            'Module %s does not define class %s.' %
                            (class_name, class_name))

                    class_def = getattr(class_def, class_name)

                if not inspect.isclass(class_def):
                    self.notify.error('Symbol %s is not a class name.' %
                                      class_name)
                else:
                    dclass.set_class_def(class_def)

            self._dclasses_by_name[class_name] = dclass
            if number >= 0:
                self._dclasses_by_number[number] = dclass
Example #23
0
class ClientOperationManager(object):
    notify = notify.new_category('ClientOperationManager')

    def __init__(self, network):
        self._network = network
        self._channel2fsm = {}

    @property
    def network(self):
        return self._network

    @property
    def channel2fsm(self):
        return self._channel2fsm

    def has_fsm(self, channel):
        if channel in self._channel2fsm:
            #print('%s, %s' % (self._channel2fsm[channel] != None, self._channel2fsm[channel].__class__.__name__))
            return self._channel2fsm[channel] != None

        return channel in self._channel2fsm

    def add_fsm(self, channel, fsm):
        if self.has_fsm(channel):
            self.notify.warning(
                'Cannot add FSM for channel %s, '
                'FSM %s is already running!' %
                (channel, self._channel2fsm[channel].__class__.__name__))

            return

        self._channel2fsm[channel] = fsm

    def remove_fsm(self, channel):
        if not self.has_fsm(channel):
            self.notify.warning(
                'Cannot cancel FSM for channel %s, no FSM running!' % channel)
            return

        del self._channel2fsm[channel]
        self._channel2fsm[channel] = None

    def get_fsm(self, channel):
        return self._channel2fsm.get(channel)

    def run_operation(self, fsm, client, callback, *args, **kwargs):
        if self.has_fsm(client.allocated_channel):
            self.notify.warning(
                'Cannot run operation: %s for channel %d, operation already running: %s!'
                % (fsm.__name__, client.allocated_channel,
                   self.get_fsm(client.allocated_channel).__class__.__name__))

            return None

        operation = fsm(self, client, callback, *args, **kwargs)
        self.add_fsm(client.allocated_channel, operation)
        return operation

    def stop_operation(self, client):
        if not self.has_fsm(client.allocated_channel):
            self.notify.warning('Cannot stop operation for channel %d, '
                                'unknown operation!' % client.channel)

            return

        operation = self.get_fsm(client.allocated_channel)
        operation.demand('Off')

        self.remove_fsm(client.allocated_channel)
Example #24
0
class GetAvatarDetailsFSM(ClientOperation):
    notify = notify.new_category('GetAvatarDetailsFSM')

    def __init__(self, manager, client, callback, avatar_id):
        ClientOperation.__init__(self, manager, client, callback)

        self._avatar_id = avatar_id
        self._callback = callback
        self._dc_class = None
        self._fields = {}

    def enterStart(self):
        def response(dclass, fields):
            if not dclass:
                self.cleanup(False)
                return

            self._dc_class = dclass
            self._fields = fields
            self.request('SendDetails')

        self.manager.network.database_interface.query_object(
            self.client.channel, types.DATABASE_CHANNEL, self._avatar_id,
            response,
            self.manager.network.dc_loader.dclasses_by_name['DistributedToon'])

    def exitStart(self):
        pass

    def enterSendDetails(self):
        datagram = io.NetworkDatagram()
        datagram.add_uint64(self._avatar_id)
        datagram.add_uint64(0)
        datagram.add_uint32(0)
        datagram.add_uint16(0)

        sorted_fields = {}
        for field_name, field_args in self._fields.items():
            field = self._dc_class.get_field_by_name(field_name)
            if not field:
                self.notify.warning('Failed to pack fields for object %d, '
                                    'unknown field: %s!' %
                                    (self._avatar_id, field_name))

                self.cleanup(False)
                return

            sorted_fields[field.get_number()] = field_args

        sorted_fields = collections.OrderedDict(sorted(sorted_fields.items()))

        field_packer = DCPacker()
        for field_index, field_args in sorted_fields.items():
            field = self._dc_class.get_field_by_index(field_index)
            if not field:
                self.notify.warning(
                    'Failed to pack required field: %d for object %d, '
                    'unknown field!' % (field_index, self._avatar_id))

                self.cleanup(False)
                return

            field_packer.begin_pack(field)
            field.pack_args(field_packer, field_args)
            field_packer.end_pack()

        datagram.append_data(field_packer.get_string())
        di = io.NetworkDatagramIterator(datagram)

        # We're all done
        self.cleanup(True, False, di)

    def exitSendDetails(self):
        pass
Example #25
0
class SetNamePatternFSM(ClientOperation):
    notify = notify.new_category('SetNamePatternFSM')

    def __init__(self, manager, client, callback, avatar_id, pattern):
        self.notify.debug("SetNamePatternFSM.__init__(%s, %s, %s, %s, %s)" %
                          (str(manager), str(client), str(callback),
                           str(avatar_id), str(pattern)))

        ClientOperation.__init__(self, manager, client, callback)

        self._avatar_id = avatar_id
        self._pattern = pattern
        self._callback = callback
        self._dc_class = None
        self._fields = {}

    def enterStart(self):
        self.notify.debug("SetNamePatternFSM.enterQuery()")

        def response(dclass, fields):
            self.notify.debug("SetNamePatternFSM.enterQuery.response(%s, %s)" %
                              (str(dclass), str(fields)))
            if not dclass:
                self.cleanup(False)
                return

            self._dc_class = dclass
            self._fields = fields
            self.request('SetPatternName')

        self.manager.network.database_interface.query_object(
            self.client.channel, types.DATABASE_CHANNEL, self._avatar_id,
            response,
            self.manager.network.dc_loader.dclasses_by_name['DistributedToon'])

    def exitStart(self):
        self.notify.debug("SetNamePatternFSM.exitQuery()")

    def enterSetPatternName(self):
        self.notify.debug("SetNamePatternFSM.enterSetPatternName()")

        nameGenerator = NameGenerator()

        # Render the pattern into a string:
        parts = []
        for p, f in self._pattern:
            part = nameGenerator.nameDictionary.get(p, ('', ''))[1]
            if f:
                part = part[:1].upper() + part[1:]
            else:
                part = part.lower()
            parts.append(part)

        parts[2] += parts.pop(
            3)  # Merge 2&3 (the last name) as there should be no space.
        while '' in parts:
            parts.remove('')
        name = ' '.join(parts)

        del nameGenerator

        new_fields = {'setName': (name, )}

        #self.notify.warning("New fields are \n%s" % (str(self._fields)))

        self.manager.network.database_interface.update_object(
            self.client.channel, types.DATABASE_CHANNEL, self._avatar_id,
            self.manager.network.dc_loader.dclasses_by_name['DistributedToon'],
            new_fields)

        # We're all done
        self.cleanup(True, self._avatar_id)

    def exitSetPatternName(self):
        self.notify.debug("SetNamePatternFSM.exitSetPatternName()")
Example #26
0
class ClientAgent(io.NetworkConnector, io.NetworkListener,
                  component.Component):
    notify = notify.new_category('ClientAgent')

    def __init__(self, dc_loader):
        address = config.GetString('clientagent-address', '0.0.0.0')
        port = config.GetInt('clientagent-port', 6667)
        connect_address = config.GetString('clientagent-connect-address',
                                           '127.0.0.1')
        connect_port = config.GetInt('clientagent-connect-port', 7100)

        io.NetworkConnector.__init__(self, dc_loader, connect_address,
                                     connect_port)
        io.NetworkListener.__init__(self, address, port, Client)
        self._channel = config.GetInt('clientagent-channel',
                                      types.CLIENTAGENT_CHANNEL)

        min_channels = config.GetInt('clientagent-min-channels', 1000000000)
        max_channels = config.GetInt('clientagent-max-channels', 1009999999)

        self._channel_allocator = UniqueIdAllocator(min_channels,
                                                    max_channels - 1)
        self._server_version = config.GetString('clientagent-version',
                                                'no-version')
        self._server_hash_val = int(
            config.GetString('clientagent-hash-val', '0'))

        self._interest_timeout = config.GetFloat(
            'clientagent-interest-timeout', 2.5)

        self._database_interface = util.DatabaseInterface(self)
        self._account_manager = ClientAccountManager(self)

    @property
    def channel_allocator(self):
        return self._channel_allocator

    @property
    def server_version(self):
        return self._server_version

    @property
    def server_hash_val(self):
        return self._server_hash_val

    @property
    def interest_timeout(self):
        return self._interest_timeout

    @property
    def database_interface(self):
        return self._database_interface

    @property
    def account_manager(self):
        return self._account_manager

    def setup(self):
        io.NetworkListener.setup(self)
        io.NetworkConnector.setup(self)

    def handle_datagram(self, channel, sender, message_type, di):
        handler = self.get_handler_from_channel(channel)
        if not handler:
            self.notify.debug('Cannot handle message type: %d '
                              'for unknown channel: %d!' %
                              (message_type, channel))

            return

        handler.handle_internal_datagram(message_type, sender, di)

    def shutdown(self):
        io.NetworkListener.shutdown(self)
        io.NetworkConnector.shutdown(self)
Example #27
0
class DatabaseRetrieveFSM(DatabaseOperationFSM):
    notify = notify.new_category('DatabaseRetrieveFSM')

    def __init__(self, *args, **kwargs):
        self._context = kwargs.pop('context', 0)
        self._do_id = kwargs.pop('do_id', 0)

        DatabaseOperationFSM.__init__(self, *args, **kwargs)

    def enterStart(self):
        if not self._do_id:
            self.notify.warning(
                'Failed to get fields for object: %d context: %d, '
                'unknown object!' % (self._do_id, self._context))

            self.cleanup(False, self._context, 0, 0, None)
            return

        file_object = self.network.backend.open_file('%d' % self._do_id)
        if not file_object:
            self.notify.warning(
                'Failed to get fields for object: %d context: %d, '
                'unknown object!' % (self._do_id, self._context))

            self.cleanup(False, self._context, 0, 0, None)
            return

        dc_name = file_object.get_value('dclass')
        self._dc_class = self.network.dc_loader.dclasses_by_name.get(dc_name)
        if not self._dc_class:
            self.notify.warning('Failed to query object: %d context: %d, '
                                'unknown dclass: %s!' %
                                (self._do_id, self._context, dc_name))

            self.cleanup(False, self._context, 0, 0, None)
            return

        self._fields = file_object.get_value('fields')
        if not self._fields:
            self.notify.warning('Failed to query object: %d context %d, '
                                'invalid fields!' %
                                (self._do_id, self._context))

            self.cleanup(False, self._context, 0, 0, None)
            return

        self.network.backend.close_file(file_object)
        field_packer = DCPacker()
        for field_name, field_args in self._fields.items():
            field = self._dc_class.get_field_by_name(field_name)
            if not field:
                self.notify.warning('Failed to query object %d context: %d, '
                                    'unknown field: %s' %
                                    (do_id, self._context, field_name))

                return

            field_packer.raw_pack_uint16(field.get_number())
            field_packer.begin_pack(field)
            field.pack_args(field_packer, field_args)
            field_packer.end_pack()

        self.cleanup(True, self._context, self._dc_class.get_number(),
                     len(self._fields), field_packer)

    def exitStart(self):
        pass
class MessageInterface(object):
    notify = notify.new_category('MessageInterface')

    def __init__(self, network):
        self._network = network
        self._flush_timeout = config.GetFloat('messagedirector-flush-timeout',
                                              0.001)
        self._route_usequeue = config.GetBool('messagedirector-route-usequeue',
                                              False)
        self._messages = collections.deque()
        self._post_messages = {}

    @property
    def messages(self):
        return self._messages

    @property
    def post_messages(self):
        return self._post_messages

    def append_handle(self, channel, sender, message_type, datagram):
        if not channel:
            return

        #if not datagram.get_length():
        #    self.notify.warning('Failed to append messenger handle from sender: '
        #        '%d to channel: %d, invalid datagram!' % (sender, channel))
        #
        #    return

        message_handle = MessageHandle(channel, sender, message_type, datagram)
        if not self._route_usequeue:
            self.route_datagram_noqueue(message_handle)
            return

        self._messages.append(message_handle)

    def remove_handle(self, message_handle):
        if not isinstance(message_handle, MessageHandle):
            raise MessageError('Failed to remove message handle of '
                               'invalid type: %r!' % message_handle)

        self._messages.remove(message_handle)

    def append_post_handle(self, channel, datagram):
        message_handle = PostMessageHandle(channel, datagram)
        messages = self._post_messages.setdefault(channel, collections.deque())
        messages.append(message_handle)

    def remove_post_handle(self, message_handle):
        if not isinstance(message_handle, PostMessageHandle):
            raise MessageError('Failed to remove post message handle of '
                               'invalid type: %r!' % message_handle)

        messages = self._post_messages.get(message_handle.channel)
        if not messages:
            self.notify.debug('Failed to remove post message handle, '
                              'unknown channel: %d!' % channel)

            return

        messages.remove(message_handle)

    def clear_post_handles(self, channel):
        messages = self._post_messages.get(channel)
        if not messages:
            self.notify.debug('Failed to flush post message handles, '
                              'unknown channel: %d!' % channel)

            return

        del self._post_messages[channel]

    def setup(self):
        self.__flush_task = task_mgr.doMethodLater(
            self._flush_timeout, self.__flush,
            self._network.get_unique_name('flush-queue'))

    def route_datagram(self, message_handle, participant):
        assert (message_handle != None)

        datagram = io.NetworkDatagram()
        datagram.add_header(message_handle.channel, message_handle.sender,
                            message_handle.message_type)

        other_datagram = message_handle.datagram
        datagram.append_data(other_datagram.get_message())
        participant.handle_send_datagram(datagram)

        # destroy the datagram and message handle objects since they are
        # no longer needed in this scope...
        other_datagram.clear()
        datagram.clear()

        del other_datagram
        del datagram

        message_handle.destroy()
        del message_handle

    def route_datagram_noqueue(self, message_handle):
        participant = self._network.interface.get_participant(
            message_handle.channel)
        if not participant:
            return

        self.route_datagram(message_handle, participant)

    def __flush(self, task):
        for _ in xrange(len(self._messages)):
            # pull a message handle object off the top of the queue,
            # then attempt to route it to its appropiate channel...
            message_handle = self._messages.popleft()
            assert (message_handle != None)

            participant = self._network.interface.get_participant(
                message_handle.channel)
            if not participant:
                #self.notify.warning('Cannot flush message for unknown channel: %d!' %  message_handle.channel)
                continue

            self.route_datagram(message_handle, participant)

        return task.again

    def flush_post_handles(self, channel):
        messages = self._post_messages.get(channel)
        if not messages:
            self.notify.debug('Failed to flush post message handles, '
                              'unknown channel: %d!' % channel)

            return

        participant = self._network.interface.get_participant(channel)
        if not participant:
            self.notify.warning('Failed to flush post message handles, '
                                'unknown participant with channel: %d!' %
                                channel)

            return

        for _ in xrange(len(messages)):
            message_handle = messages.popleft()

            # in order for us to properly handle post remove messages,
            # we need to unpack and process them like we would normally...
            datagram = message_handle.datagram
            participant.handle_datagram(io.NetworkDatagramIterator(datagram))

            # destroy the datagram and message handle objects since they are
            # no longer needed in this scope...
            datagram.clear()
            del datagram

            message_handle.destroy()
            del message_handle

        # finally clear our channel from the post removes
        # dictionary which held the message handle objects...
        self.clear_post_handles(channel)

    def flush_all_post_handles(self):
        for channel in list(self._post_messages.keys()):
            self.flush_post_handles(channel)

    def shutdown(self):
        if self.__flush_task:
            task_mgr.remove(self.__flush_task)
            self.__flush_task = None
Example #29
0
class DatabaseServer(io.NetworkConnector, component.Component):
    notify = notify.new_category('DatabaseServer')

    def __init__(self, dc_loader):
        connect_address = config.GetString('database-connect-address',
                                           '127.0.0.1')
        connect_port = config.GetInt('database-connect-port', 7100)

        io.NetworkConnector.__init__(self, dc_loader, connect_address,
                                     connect_port)
        self._channel = config.GetInt('database-channel',
                                      types.DATABASE_CHANNEL)

        self._backend = DatabaseJSONBackend()
        self._operation_manager = DatabaseOperationManager()

    @property
    def backend(self):
        return self._backend

    @property
    def operation_manager(self):
        return self._operation_manager

    def setup(self):
        io.NetworkConnector.setup(self)

        self._backend.setup()
        self._operation_manager.setup()

    def handle_datagram(self, channel, sender, message_type, di):
        if message_type == types.DBSERVER_CREATE_OBJECT:
            self.handle_create_object(sender, di)
        elif message_type == types.DBSERVER_OBJECT_GET_ALL:
            self.handle_object_get_all(sender, di)
        elif message_type == types.DBSERVER_OBJECT_SET_FIELD:
            self.handle_object_set_field(sender, di)
        elif message_type == types.DBSERVER_OBJECT_SET_FIELD_IF_EQUALS:
            self.handle_object_set_field_if_equals(sender, di)
        else:
            self.notify.warning('Received unknown message type: %d' %
                                message_type)

    def handle_create_object(self, sender, di):
        self._operation_manager.add_operation(
            DatabaseCreateFSM,
            self,
            sender,
            context=di.get_uint32(),
            dc_id=di.get_uint16(),
            field_count=di.get_uint16(),
            field_data=di.get_remaining_bytes(),
            callback=self._create_object_callback)

    def _create_object_callback(self, success, sender, context, do_id):
        datagram = io.NetworkDatagram()
        datagram.add_header(sender, self.channel,
                            types.DBSERVER_CREATE_OBJECT_RESP)

        datagram.add_uint32(context)
        datagram.add_uint32(do_id)

        self.handle_send_connection_datagram(datagram)

    def handle_object_get_all(self, sender, di):
        self._operation_manager.add_operation(
            DatabaseRetrieveFSM,
            self,
            sender,
            context=di.get_uint32(),
            do_id=di.get_uint32(),
            callback=self._object_get_all_callback)

    def _object_get_all_callback(self, success, sender, context, dc_id,
                                 num_fields, field_packer):
        datagram = io.NetworkDatagram()
        datagram.add_header(sender, self.channel,
                            types.DBSERVER_OBJECT_GET_ALL_RESP)

        datagram.add_uint32(context)
        datagram.add_uint8(success)
        if success:
            datagram.add_uint16(dc_id)
            datagram.add_uint16(num_fields)

            assert (field_packer is not None)
            datagram.append_data(field_packer.get_string())

        self.handle_send_connection_datagram(datagram)

    def handle_object_set_field(self, sender, di):
        self._operation_manager.add_operation(
            DatabaseSetFieldFSM,
            self,
            sender,
            do_id=di.get_uint32(),
            field_data=di.get_remaining_bytes())

    def handle_object_set_field_if_equals(self, sender, di):
        self._operation_manager.add_operation(
            DatabaseSetFieldFSM,
            self,
            sender,
            do_id=di.get_uint32(),
            context=di.get_uint32(),
            field_data=di.get_remaining_bytes(),
            callback=self._object_set_field_if_equals_callback)

    def _object_set_field_if_equals_callback(self, success, sender, context,
                                             field_id, field_data):
        datagram = io.NetworkDatagram()
        datagram.add_header(sender, self.channel,
                            types.DBSERVER_OBJECT_SET_FIELD_IF_EQUALS_RESP)

        datagram.add_uint32(context)
        datagram.add_uint8(success)
        if not success:
            if field_id is not None:
                field_packer = DCPacker()
                field = self._dc_class.get_field_by_index(field_id)
                assert (field is not None)

                field_packer.raw_pack_uint16(field_id)
                field_packer.begin_pack(field)
                field.pack_args(field_packer, field_data)
                field_packer.end_pack()

                datagram.add_uint16(1)
                datagram.append_data(field_packer.get_string())
            else:
                datagram.add_uint16(0)

        self.handle_send_connection_datagram(datagram)

    def shutdown(self):
        self._backend.shutdown()
        self._operation_manager.shutdown()

        io.NetworkConnector.shutdown(self)
Example #30
0
class CreateAvatarFSM(ClientOperation):
    notify = notify.new_category('CreateAvatarFSM')

    def __init__(self, manager, client, callback, echo_context, account_id,
                 dna_string, index):
        ClientOperation.__init__(self, manager, client, callback)

        self._account_id = account_id
        self._dna_string = dna_string
        self._callback = callback
        self._echo_context = echo_context
        self._index = index

    def enterStart(self):
        fields = {
            'setDNAString': (self._dna_string, ),
            'setPosIndex': (self._index, ),
            'setName': ('Toon', ),
            'setFriendsList': ([], )
        }

        self.manager.network.database_interface.create_object(
            self.client.channel,
            types.DATABASE_CHANNEL,
            self.manager.network.dc_loader.dclasses_by_name['DistributedToon'],
            fields=fields,
            callback=lambda avatar_id: self.__avatar_created(
                avatar_id, self._index))

    def __avatar_created(self, avatar_id, index):
        if not avatar_id:
            self.notify.warning('Failed to create avatar with index: '
                                '%d for account with do_id: %d!' %
                                (index, self._account_id))

            self.cleanup(False)
            return

        self.manager.network.database_interface.query_object(
            self.client.channel, types.DATABASE_CHANNEL, self._account_id,
            lambda dclass, fields: self.__account_loaded(
                dclass, fields, avatar_id, index),
            self.manager.network.dc_loader.dclasses_by_name['Account'])

    def __account_loaded(self, dclass, fields, avatar_id, index):
        if not dclass:
            self.cleanup(False)
            return

        avatar_list = fields['ACCOUNT_AV_SET'][0]
        avatar_list[index] = avatar_id

        new_fields = {'ACCOUNT_AV_SET': (avatar_list, )}

        self.manager.network.database_interface.update_object(
            self.client.channel, types.DATABASE_CHANNEL, self._account_id,
            self.manager.network.dc_loader.dclasses_by_name['Account'],
            new_fields)

        # We're all done
        self.cleanup(True, self._echo_context, avatar_id)

    def exitStart(self):
        pass