def add(self, game):
        """ Add given game.

            :param game: a NetworkGame object.
            :type game: diplomacy.client.network_game.NetworkGame
        """
        assert self.game_id == game.game_id
        if Game.is_player_game(game):
            if game.role in self.games:
                raise exceptions.DiplomacyException(
                    'Power name %s already in game instances set.' % game.role)
        elif Game.is_observer_game(game):
            if self.current_observer_type is not None:
                raise exceptions.DiplomacyException(
                    'Previous special game %s must be removed before adding new one.'
                    % self.current_observer_type)
            self.current_observer_type = game.role
        else:
            assert Game.is_omniscient_game(game)
            if self.current_observer_type is not None:
                raise exceptions.DiplomacyException(
                    'Previous special game %s must be removed before adding new one.'
                    % self.current_observer_type)
            self.current_observer_type = game.role
        self.games[game.role] = game
Exemple #2
0
    def start(self, port=None, io_loop=None):
        """ Start server if not yet started. Raise an exception if server is already started.

            :param port: (optional) port where server must run. If not provided,
                try to start on a random selected port. Use property `port` to get current server port.
            :param io_loop: (optional) tornado IO lopp where server must run. If not provided, get
                default IO loop instance (tornado.ioloop.IOLoop.instance()).
        """
        if self.backend is not None:
            raise exceptions.DiplomacyException(
                'Server is already running on port %s.' % self.backend.port)
        if port is None:
            port = 8432
        if io_loop is None:
            io_loop = tornado.ioloop.IOLoop.instance()
        handlers = [
            tornado.web.url(r"/", ConnectionHandler, {'server': self}),
        ]
        settings = {
            'cookie_secret': common.generate_token(),
            'xsrf_cookies': True,
            'websocket_ping_interval': self.ping_seconds,
            'websocket_ping_timeout': 2 * self.ping_seconds,
            'websocket_max_message_size': 64 * 1024 * 1024
        }
        self.backend = _ServerBackend()
        self.backend.application = tornado.web.Application(
            handlers, **settings)
        self.backend.http_server = self.backend.application.listen(port)
        self.backend.io_loop = io_loop
        self.backend.port = port
        self.set_tasks(io_loop)
        LOGGER.info('Running on port %d', self.backend.port)
        if not io_loop.asyncio_loop.is_running():
            io_loop.start()
Exemple #3
0
 def get_special_token_role(self, token):
     """ Return role name (either OBSERVER_TYPE or OMNISCIENT_TYPE) for given special token. """
     if self.has_omniscient_token(token):
         return strings.OMNISCIENT_TYPE
     if self.has_observer_token(token):
         return strings.OBSERVER_TYPE
     raise exceptions.DiplomacyException(
         'Unknown special token in game %s' % self.game_id)
Exemple #4
0
def handle_response(context, response):
    """ Call appropriate handler for given response with given request context.

        :param context: request context.
        :param response: response received.
        :return: value returned by handler.
    """
    handler = MAPPING.get(type(context.request), None)
    if not handler:
        raise exceptions.DiplomacyException(
            'No response handler available for request class %s' % type(context.request).__name__)
    return handler(context, response)
Exemple #5
0
    def __init__(self, **kwargs):
        self.name = None  # type: str

        # Setting default values
        kwargs[strings.NAME] = kwargs.get(strings.NAME, None) or self.get_class_name()
        kwargs[self.id_field] = kwargs.get(self.id_field, None) or str(uuid.uuid4())
        if kwargs[strings.NAME] != self.get_class_name():
            raise exceptions.DiplomacyException('Expected request name %s, got %s' %
                                                (self.get_class_name(), kwargs[strings.NAME]))

        # Building
        super(NetworkData, self).__init__(**kwargs)
Exemple #6
0
    def sync_done(self):
        """ Final reconnection work. Remove obsolete game requests and send remaining requests. """

        # All sync requests sent have finished.
        # Remove all obsolete game requests from connection.
        # A game request is obsolete if it's phase-dependent and if its phase does not match current game phase.

        request_to_send_updated = {}
        for context in self.connection.requests_to_send.values(
        ):  # type: RequestFutureContext
            keep = True
            if context.request.level == strings.GAME and context.request.phase_dependent:
                request_phase = context.request.phase
                server_phase = self.games_phases[context.request.game_id][
                    context.request.game_role].phase
                if request_phase != server_phase:
                    # Request is obsolete.
                    context.future.set_exception(
                        exceptions.DiplomacyException(
                            'Game %s: request %s: request phase %s does not match current server game phase %s.'
                            % (context.request.game_id, context.request.name,
                               request_phase, server_phase)))
                    keep = False
            if keep:
                request_to_send_updated[context.request.request_id] = context

        LOGGER.debug('Keep %d/%d old requests to send.',
                     len(request_to_send_updated),
                     len(self.connection.requests_to_send))

        # All requests to send are stored in request_to_send_updated.
        # Then we can empty connection.requests_to_send.
        # If we fail to send a request, it will be re-added again.
        self.connection.requests_to_send.clear()

        # Send requests.
        for request_to_send in request_to_send_updated.values(
        ):  # type: RequestFutureContext
            self.connection.write_request(request_to_send).add_done_callback(
                _MessageWrittenCallback(request_to_send).callback)

        # We are reconnected.
        self.connection.is_reconnecting.set()

        LOGGER.info('Done reconnection work.')
Exemple #7
0
    def reconnect(self):
        """ Perform concrete reconnection work. """

        # Mark all waiting responses as `re-sent` and move them back to responses_to_send.
        for waiting_context in self.connection.requests_waiting_responses.values(
        ):  # type: RequestFutureContext
            waiting_context.request.re_sent = True
        self.connection.requests_to_send.update(
            self.connection.requests_waiting_responses)
        self.connection.requests_waiting_responses.clear()

        # Remove all previous synchronization requests.
        requests_to_send_updated = {}
        for context in self.connection.requests_to_send.values(
        ):  # type: RequestFutureContext
            if isinstance(context.request, requests.Synchronize):
                context.future.set_exception(
                    exceptions.DiplomacyException(
                        'Sync request invalidated for game ID %s.' %
                        context.request.game_id))
            else:
                requests_to_send_updated[context.request.request_id] = context
        self.connection.requests_to_send = requests_to_send_updated

        # Count games to synchronize.
        for channel in self.connection.channels.values():
            for game_instance_set in channel.game_id_to_instances.values():
                for game in game_instance_set.get_games():
                    self.games_phases.setdefault(game.game_id,
                                                 {})[game.role] = None
                    self.n_expected_games += 1

        if self.n_expected_games:
            # Synchronize games.
            for channel in self.connection.channels.values():
                for game_instance_set in channel.game_id_to_instances.values():
                    for game in game_instance_set.get_games():
                        game.synchronize().add_done_callback(
                            self.generate_sync_callback(game))
        else:
            # No game to sync, finish sync now.
            self.sync_done()
def handle_notification(connection, notification):
    """ Call appropriate handler for given notification received by given connection.
        :param connection: recipient connection.
        :param notification: received notification.
        :type connection: diplomacy.Connection
        :type notification: notifications._AbstractNotification | notifications._GameNotification
    """
    if notification.level == strings.CHANNEL:
        object_to_notify = connection.channels.get(notification.token, None)
    else:
        object_to_notify = _get_game_to_notify(connection, notification)
    if object_to_notify is None:
        LOGGER.error('Unknown notification: %s', notification.name)
    else:
        handler = MAPPING.get(type(notification), None)
        if not handler:
            raise exceptions.DiplomacyException(
                'No handler available for notification class %s' %
                type(notification).__name__)
        handler(object_to_notify, notification)
        if notification.level == strings.GAME:
            object_to_notify.notify(notification)