mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-19 12:58:09 +00:00
184 lines
9 KiB
Python
184 lines
9 KiB
Python
# ==============================================================================
|
|
# Copyright (C) 2019 - Philip Paquette, Steven Bocco
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
# later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License along
|
|
# with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
# ==============================================================================
|
|
""" Channel
|
|
|
|
- The channel object represents an authenticated connection over a socket.
|
|
- It has a token that it sends with every request to authenticate itself.
|
|
"""
|
|
import logging
|
|
|
|
from tornado import gen
|
|
|
|
from diplomacy.communication import requests
|
|
from diplomacy.utils import strings, common
|
|
|
|
LOGGER = logging.getLogger(__name__)
|
|
|
|
def _req_fn(request_class, local_req_fn=None, **request_args):
|
|
""" Create channel request method that sends request with channel token.
|
|
|
|
:param request_class: class of request to send with channel request method.
|
|
:param local_req_fn: (optional) Channel method to use locally to try retrieving a data
|
|
instead of sending a request. If provided, local_req_fn is called with request args:
|
|
|
|
- if it returns anything else than None, then returned data is returned by channel request method.
|
|
- else, request class is still sent and channel request method follows standard path
|
|
(request sent, response received, response handler called and final handler result returned).
|
|
|
|
:param request_args: arguments to pass to request class to create the request object.
|
|
:return: a Channel method.
|
|
"""
|
|
str_params = (', '.join('%s=%s' % (key, common.to_string(value))
|
|
for (key, value) in sorted(request_args.items()))) if request_args else ''
|
|
|
|
@gen.coroutine
|
|
def func(self, game=None, **kwargs):
|
|
""" Send an instance of request_class with given kwargs and game object.
|
|
:param self: Channel object who sends the request.
|
|
:param game: (optional) a NetworkGame object (required for game requests).
|
|
:param kwargs: request arguments.
|
|
:return: Data returned after response is received and handled by associated response manager.
|
|
See module diplomacy.client.response_managers about responses management.
|
|
:type game: diplomacy.client.network_game.NetworkGame
|
|
"""
|
|
kwargs.update(request_args)
|
|
if request_class.level == strings.GAME:
|
|
assert game is not None
|
|
kwargs[strings.TOKEN] = self.token
|
|
kwargs[strings.GAME_ID] = game.game_id
|
|
kwargs[strings.GAME_ROLE] = game.role
|
|
kwargs[strings.PHASE] = game.current_short_phase
|
|
else:
|
|
assert game is None
|
|
if request_class.level == strings.CHANNEL:
|
|
kwargs[strings.TOKEN] = self.token
|
|
if local_req_fn is not None:
|
|
local_ret = local_req_fn(self, **kwargs)
|
|
if local_ret is not None:
|
|
return local_ret
|
|
request = request_class(**kwargs)
|
|
return (yield self.connection.send(request, game))
|
|
|
|
func.__request_name__ = request_class.__name__
|
|
func.__request_params__ = str_params
|
|
func.__doc__ = """
|
|
Send request :class:`.%(request_name)s`%(with_params)s``kwargs``.
|
|
Return response data returned by server for this request.
|
|
See :class:`.%(request_name)s` about request parameters and response.
|
|
""" % {'request_name': request_class.__name__,
|
|
'with_params': ' with forced parameters ``(%s)`` and additional request parameters '
|
|
% str_params if request_args else ' with request parameters '}
|
|
return func
|
|
|
|
class Channel:
|
|
""" Channel - Represents an authenticated connection over a physical socket """
|
|
# pylint: disable=too-few-public-methods
|
|
__slots__ = ['connection', 'token', 'game_id_to_instances', '__weakref__']
|
|
|
|
def __init__(self, connection, token):
|
|
""" Initialize a channel.
|
|
|
|
Properties:
|
|
|
|
- **connection**: :class:`.Connection` object from which this channel originated.
|
|
- **token**: Channel token, used to identify channel on server.
|
|
- **game_id_to_instances**: Dictionary mapping a game ID to :class:`.NetworkGame` objects loaded for this
|
|
game. Each :class:`.NetworkGame` has a specific role, which is either an observer role, an omniscient
|
|
role, or a power (player) role. Network games for a specific game ID are managed within a
|
|
:class:`.GameInstancesSet`, which makes sure that there will be at most 1 :class:`.NetworkGame` instance
|
|
per possible role.
|
|
|
|
:param connection: a Connection object.
|
|
:param token: Channel token.
|
|
:type connection: diplomacy.client.connection.Connection
|
|
:type token: str
|
|
"""
|
|
self.connection = connection
|
|
self.token = token
|
|
self.game_id_to_instances = {} # {game id => GameInstances}
|
|
|
|
def _local_join_game(self, **kwargs):
|
|
""" Look for a local game with given kwargs intended to be used to build a JoinGame request.
|
|
Return None if no local game found, else local game found.
|
|
Game is identified with game ID **(required)** and power name *(optional)*.
|
|
If power name is None, we look for a "special" game (observer or omniscient game)
|
|
loaded locally. Note that there is at most 1 special game per (channel + game ID)
|
|
couple: either observer or omniscient, not both.
|
|
"""
|
|
game_id = kwargs[strings.GAME_ID]
|
|
power_name = kwargs.get(strings.POWER_NAME, None)
|
|
if game_id in self.game_id_to_instances:
|
|
if power_name is not None:
|
|
return self.game_id_to_instances[game_id].get(power_name)
|
|
return self.game_id_to_instances[game_id].get_special()
|
|
return None
|
|
|
|
# ===================
|
|
# Public channel API.
|
|
# ===================
|
|
|
|
create_game = _req_fn(requests.CreateGame)
|
|
get_available_maps = _req_fn(requests.GetAvailableMaps)
|
|
get_playable_powers = _req_fn(requests.GetPlayablePowers)
|
|
join_game = _req_fn(requests.JoinGame, local_req_fn=_local_join_game)
|
|
join_powers = _req_fn(requests.JoinPowers)
|
|
list_games = _req_fn(requests.ListGames)
|
|
get_games_info = _req_fn(requests.GetGamesInfo)
|
|
get_dummy_waiting_powers = _req_fn(requests.GetDummyWaitingPowers)
|
|
|
|
# User Account API.
|
|
delete_account = _req_fn(requests.DeleteAccount)
|
|
logout = _req_fn(requests.Logout)
|
|
|
|
# Admin / Moderator API.
|
|
make_omniscient = _req_fn(requests.SetGrade, grade=strings.OMNISCIENT, grade_update=strings.PROMOTE)
|
|
remove_omniscient = _req_fn(requests.SetGrade, grade=strings.OMNISCIENT, grade_update=strings.DEMOTE)
|
|
promote_administrator = _req_fn(requests.SetGrade, grade=strings.ADMIN, grade_update=strings.PROMOTE)
|
|
demote_administrator = _req_fn(requests.SetGrade, grade=strings.ADMIN, grade_update=strings.DEMOTE)
|
|
promote_moderator = _req_fn(requests.SetGrade, grade=strings.MODERATOR, grade_update=strings.PROMOTE)
|
|
demote_moderator = _req_fn(requests.SetGrade, grade=strings.MODERATOR, grade_update=strings.DEMOTE)
|
|
|
|
# ====================================================================
|
|
# Game API. Intended to be called by NetworkGame object, not directly.
|
|
# ====================================================================
|
|
|
|
_get_phase_history = _req_fn(requests.GetPhaseHistory)
|
|
_leave_game = _req_fn(requests.LeaveGame)
|
|
_send_game_message = _req_fn(requests.SendGameMessage)
|
|
_set_orders = _req_fn(requests.SetOrders)
|
|
|
|
_clear_centers = _req_fn(requests.ClearCenters)
|
|
_clear_orders = _req_fn(requests.ClearOrders)
|
|
_clear_units = _req_fn(requests.ClearUnits)
|
|
|
|
_wait = _req_fn(requests.SetWaitFlag, wait=True)
|
|
_no_wait = _req_fn(requests.SetWaitFlag, wait=False)
|
|
_vote = _req_fn(requests.Vote)
|
|
_save = _req_fn(requests.SaveGame)
|
|
_synchronize = _req_fn(requests.Synchronize)
|
|
|
|
# Admin / Moderator API.
|
|
_delete_game = _req_fn(requests.DeleteGame)
|
|
_kick_powers = _req_fn(requests.SetDummyPowers)
|
|
_set_state = _req_fn(requests.SetGameState)
|
|
_process = _req_fn(requests.ProcessGame)
|
|
_query_schedule = _req_fn(requests.QuerySchedule)
|
|
_start = _req_fn(requests.SetGameStatus, status=strings.ACTIVE)
|
|
_pause = _req_fn(requests.SetGameStatus, status=strings.PAUSED)
|
|
_resume = _req_fn(requests.SetGameStatus, status=strings.ACTIVE)
|
|
_cancel = _req_fn(requests.SetGameStatus, status=strings.CANCELED)
|
|
_draw = _req_fn(requests.SetGameStatus, status=strings.COMPLETED)
|