aboutsummaryrefslogtreecommitdiff
path: root/diplomacy/server/server.py
diff options
context:
space:
mode:
Diffstat (limited to 'diplomacy/server/server.py')
-rw-r--r--diplomacy/server/server.py47
1 files changed, 35 insertions, 12 deletions
diff --git a/diplomacy/server/server.py b/diplomacy/server/server.py
index 4a1647c..ee585c3 100644
--- a/diplomacy/server/server.py
+++ b/diplomacy/server/server.py
@@ -53,6 +53,7 @@ import os
from random import randint
import socket
import signal
+from typing import Dict, Set
import tornado
import tornado.web
@@ -227,13 +228,13 @@ class Server():
self.backup_delay_seconds = constants.DEFAULT_BACKUP_DELAY_SECONDS
self.ping_seconds = constants.DEFAULT_PING_SECONDS
self.users = None # type: Users # Users and administrators usernames.
- self.available_maps = {} # type: dict{str, set()} # {"map_name" => set("map_power")}
+ self.available_maps = {} # type: Dict[str, Set[str]] # {"map_name" => set("map_power")}
self.maps_mtime = 0 # Latest maps modification date (used to manage maps cache in server object).
# Server games loaded on memory (stored on disk).
# Saved separately (each game in one JSON file).
# Each game also stores tokens connected (player tokens, observer tokens, omniscient tokens).
- self.games = {} # type: dict{str, ServerGame}
+ self.games = {} # type: Dict[str, ServerGame]
# Dictionary mapping game IDs to dummy power names.
self.games_with_dummy_powers = {} # type: dict{str, set}
@@ -404,6 +405,12 @@ class Server():
# Game was processed normally.
# Send game updates to powers, observers and omniscient observers.
yield notifier.notify_game_processed(server_game, previous_phase_data, current_phase_data)
+
+ # If game is completed, we must close associated DAIDE port.
+ if server_game.is_game_done:
+ self.stop_daide_server(server_game.game_id)
+
+ # Game must be stopped if not active.
return not server_game.is_game_active
@gen.coroutine
@@ -596,7 +603,7 @@ class Server():
If such game is already stored in server object, return it.
Else, load it from disk but ** does not store it in server object **.
To load and immediately store a game object in server object, please use method get_game().
- Method load_game() is convenient where you want to iterate over all games in server database
+ Method load_game() is convenient when you want to iterate over all games in server database
without taking memory space.
:param game_id: ID of game to load.
:return: a ServerGame object
@@ -621,15 +628,22 @@ class Server():
# This should be an internal server error.
raise exc
+ #
def add_new_game(self, server_game):
- """ Add a new game data on server in memory. This does not save the game on disk.
+ """ Add a new game data on server in memory and perform any addition processing.
+ This does not save the game on disk.
:type server_game: ServerGame
"""
+ # Register game on memory.
self.games[server_game.game_id] = server_game
+ # Start DAIDE server for this game.
+ self.start_new_daide_server(server_game.game_id)
+ #
def get_game(self, game_id):
""" Return game saved on server matching given game ID. Raise an exception if game ID not found.
- Return game if already loaded on memory, else load it from disk, store it and return it.
+ Return game if already loaded on memory, else load it from disk, store it,
+ perform any loading/addition processing and return it.
:param game_id: ID of game to load.
:return: a ServerGame object.
:rtype: ServerGame
@@ -639,35 +653,43 @@ class Server():
LOGGER.debug('Game loaded: %s', game_id)
# Check dummy powers for this game as soon as it's loaded from disk.
self.register_dummy_power_names(server_game)
+ # Register game on memory.
self.games[server_game.game_id] = server_game
+ # Start DAIDE server for this game.
+ self.start_new_daide_server(server_game.game_id)
# We have just loaded game from disk. Start it if necessary.
if not server_game.start_master and server_game.has_expected_controls_count():
# We may have to start game.
- stop = False
if server_game.does_not_wait():
# We must process game.
- process_result = server_game.process()
- stop = process_result is None or process_result[-1]
+ server_game.process()
self.save_game(server_game)
- if not stop:
+ # Game must be scheduled only if active.
+ if server_game.is_game_active:
LOGGER.debug('Game loaded and scheduled: %s', server_game.game_id)
self.schedule_game(server_game)
return server_game
+ #
def delete_game(self, server_game):
- """ Delete given game from server (both from memory and disk).
+ """ Delete given game from server (both from memory and disk) and perform any post-deletion processing.
:param server_game: game to delete
:type server_game: ServerGame
"""
if not (server_game.is_game_canceled or server_game.is_game_completed):
server_game.set_status(strings.CANCELED)
game_filename = os.path.join(self.games_path, '%s.json' % server_game.game_id)
+ backup_game_filename = get_backup_filename(game_filename)
if os.path.isfile(game_filename):
os.remove(game_filename)
+ if os.path.isfile(backup_game_filename):
+ os.remove(backup_game_filename)
self.games.pop(server_game.game_id, None)
self.backup_games.pop(server_game.game_id, None)
self.games_with_dummy_powers.pop(server_game.game_id, None)
self.dispatched_dummy_powers.pop(server_game.game_id, None)
+ # Stop DAIDE server associated to this game.
+ self.stop_daide_server(server_game.game_id)
@gen.coroutine
def schedule_game(self, server_game):
@@ -821,7 +843,7 @@ class Server():
def start_new_daide_server(self, game_id, port=None):
""" Start a new DAIDE TCP server to handle DAIDE clients connections
:param game_id: game id to pass to the DAIDE server
- :param port: the port to use. If None, an available random prot will be used
+ :param port: the port to use. If None, an available random port will be used
"""
if port in self.daide_servers:
raise RuntimeError('Port already in used by a DAIDE server')
@@ -837,12 +859,13 @@ class Server():
daide_server = DaideServer(self, game_id)
daide_server.listen(port)
self.daide_servers[port] = daide_server
- LOGGER.info('DAIDE server running on port %d', port)
+ LOGGER.info('DAIDE server running for game %s on port %d', game_id, port)
return port
def stop_daide_server(self, game_id):
""" Stop one or all DAIDE TCP server
:param game_id: game id of the DAIDE server. If None, all servers will be stopped
+ :type game_id: str
"""
for port in list(self.daide_servers.keys()):
server = self.daide_servers[port]