-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Add timer that calls a function to get the next interval Can be used to make intervals changable without restart * Refactor message broadcasting to a service * Add broadcasted messages to existing rabbitmq exchange * Remove unused import * Publish messages as non-mandatory This aviods the "unhandled message" error * Get dirties and clear them at the same time * Use default connection filter * Try to fix flaky test * Add docstring for report_dirties
- Loading branch information
Showing
18 changed files
with
361 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -95,14 +95,14 @@ | |
|
||
from .api.api_accessor import ApiAccessor | ||
from .asyncio_extensions import synchronizedmethod | ||
from .broadcast_service import BroadcastService | ||
from .config import TRACE, config | ||
from .configuration_service import ConfigurationService | ||
from .control import run_control_server | ||
from .core import Service, create_services | ||
from .db import FAFDatabase | ||
from .game_service import GameService | ||
from .gameconnection import GameConnection | ||
from .games import GameState | ||
from .geoip_service import GeoIpService | ||
from .ice_servers.nts import TwilioNTS | ||
from .ladder_service import LadderService | ||
|
@@ -114,14 +114,14 @@ | |
from .rating_service.rating_service import RatingService | ||
from .servercontext import ServerContext | ||
from .stats.game_stats_service import GameStatsService | ||
from .timing import at_interval | ||
|
||
__author__ = "Askaholic, Chris Kitching, Dragonfire, Gael Honorez, Jeroen De Dauw, Crotalus, Michael Søndergaard, Michel Jung" | ||
__contact__ = "[email protected]" | ||
__license__ = "GPLv3" | ||
__copyright__ = "Copyright (c) 2011-2015 " + __author__ | ||
|
||
__all__ = ( | ||
"BroadcastService", | ||
"ConfigurationService", | ||
"GameConnection", | ||
"GameService", | ||
|
@@ -130,6 +130,7 @@ | |
"LadderService", | ||
"MessageQueueService", | ||
"PartyService", | ||
"PlayerService", | ||
"RatingService", | ||
"RatingService", | ||
"ServerInstance", | ||
|
@@ -139,7 +140,6 @@ | |
"run_control_server", | ||
) | ||
|
||
DIRTY_REPORT_INTERVAL = 1 # Seconds | ||
logger = logging.getLogger("server") | ||
|
||
if config.ENABLE_METRICS: | ||
|
@@ -176,6 +176,7 @@ def __init__( | |
self.contexts: Set[ServerContext] = set() | ||
|
||
self.services = _override_services or create_services({ | ||
"server": self, | ||
"database": self.database, | ||
"api_accessor": self.api_accessor, | ||
"loop": self.loop, | ||
|
@@ -220,54 +221,6 @@ async def _start_services(self) -> None: | |
service.initialize() for service in self.services.values() | ||
]) | ||
|
||
game_service: GameService = self.services["game_service"] | ||
player_service: PlayerService = self.services["player_service"] | ||
|
||
@at_interval(DIRTY_REPORT_INTERVAL, loop=self.loop) | ||
def do_report_dirties(): | ||
game_service.update_active_game_metrics() | ||
dirty_games = game_service.dirty_games | ||
dirty_queues = game_service.dirty_queues | ||
dirty_players = player_service.dirty_players | ||
game_service.clear_dirty() | ||
player_service.clear_dirty() | ||
|
||
if dirty_queues: | ||
self.write_broadcast({ | ||
"command": "matchmaker_info", | ||
"queues": [queue.to_dict() for queue in dirty_queues] | ||
}) | ||
|
||
if dirty_players: | ||
self.write_broadcast( | ||
{ | ||
"command": "player_info", | ||
"players": [player.to_dict() for player in dirty_players] | ||
}, | ||
lambda lobby_conn: lobby_conn.authenticated | ||
) | ||
|
||
# TODO: This spams squillions of messages: we should implement per- | ||
# connection message aggregation at the next abstraction layer down :P | ||
for game in dirty_games: | ||
if game.state == GameState.ENDED: | ||
game_service.remove_game(game) | ||
|
||
# So we're going to be broadcasting this to _somebody_... | ||
message = game.to_dict() | ||
|
||
self.write_broadcast( | ||
message, | ||
lambda conn: ( | ||
conn.authenticated | ||
and game.is_visible_to_player(conn.player) | ||
) | ||
) | ||
|
||
@at_interval(45, loop=self.loop) | ||
def ping_broadcast(): | ||
self.write_broadcast({"command": "ping"}) | ||
|
||
self.started = True | ||
|
||
async def listen( | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
from aio_pika import DeliveryMode | ||
|
||
from .config import config | ||
from .core import Service | ||
from .decorators import with_logger | ||
from .game_service import GameService | ||
from .games import GameState | ||
from .message_queue_service import MessageQueueService | ||
from .player_service import PlayerService | ||
from .timing import LazyIntervalTimer | ||
|
||
|
||
@with_logger | ||
class BroadcastService(Service): | ||
""" | ||
Broadcast updates about changed entities. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
server: "ServerInstance", | ||
message_queue_service: MessageQueueService, | ||
game_service: GameService, | ||
player_service: PlayerService, | ||
): | ||
self.server = server | ||
self.message_queue_service = message_queue_service | ||
self.game_service = game_service | ||
self.player_service = player_service | ||
|
||
async def initialize(self): | ||
await self.message_queue_service.declare_exchange( | ||
config.MQ_EXCHANGE_NAME | ||
) | ||
|
||
# Using a lazy interval timer so that the intervals can be changed | ||
# without restarting the server. | ||
self._broadcast_dirties_timer = LazyIntervalTimer( | ||
lambda: config.DIRTY_REPORT_INTERVAL, | ||
self.report_dirties, | ||
start=True | ||
) | ||
self._broadcast_ping_timer = LazyIntervalTimer( | ||
lambda: config.PING_INTERVAL, | ||
self.broadcast_ping, | ||
start=True | ||
) | ||
self._logger.debug("Broadcast service initialized") | ||
|
||
async def report_dirties(self): | ||
""" | ||
Send updates about any dirty (changed) entities to connected players. | ||
This function is called at a fixed interval, which guarantees that any | ||
given object will not be written out to the clients more than once per | ||
interval. | ||
""" | ||
self.game_service.update_active_game_metrics() | ||
dirty_games = self.game_service.pop_dirty_games() | ||
dirty_queues = self.game_service.pop_dirty_queues() | ||
dirty_players = self.player_service.pop_dirty_players() | ||
|
||
if dirty_queues: | ||
matchmaker_info = { | ||
"command": "matchmaker_info", | ||
"queues": [queue.to_dict() for queue in dirty_queues] | ||
} | ||
self.server.write_broadcast(matchmaker_info) | ||
|
||
if dirty_players: | ||
player_info = { | ||
"command": "player_info", | ||
"players": [player.to_dict() for player in dirty_players] | ||
} | ||
self.server.write_broadcast(player_info) | ||
|
||
game_info = { | ||
"command": "game_info", | ||
"games": [] | ||
} | ||
# TODO: This spams squillions of messages: we should implement per- | ||
# connection message aggregation at the next abstraction layer down :P | ||
for game in dirty_games: | ||
if game.state == GameState.ENDED: | ||
self.game_service.remove_game(game) | ||
|
||
# So we're going to be broadcasting this to _somebody_... | ||
message = game.to_dict() | ||
game_info["games"].append(message) | ||
|
||
self.server.write_broadcast( | ||
message, | ||
lambda conn: ( | ||
conn.authenticated | ||
and game.is_visible_to_player(conn.player) | ||
) | ||
) | ||
|
||
if dirty_queues: | ||
await self.message_queue_service.publish( | ||
config.MQ_EXCHANGE_NAME, | ||
"broadcast.matchmakerInfo.update", | ||
matchmaker_info, | ||
delivery_mode=DeliveryMode.NOT_PERSISTENT | ||
) | ||
|
||
if dirty_players: | ||
await self.message_queue_service.publish( | ||
config.MQ_EXCHANGE_NAME, | ||
"broadcast.playerInfo.update", | ||
player_info, | ||
delivery_mode=DeliveryMode.NOT_PERSISTENT | ||
) | ||
|
||
if dirty_games: | ||
await self.message_queue_service.publish( | ||
config.MQ_EXCHANGE_NAME, | ||
"broadcast.gameInfo.update", | ||
game_info, | ||
delivery_mode=DeliveryMode.NOT_PERSISTENT | ||
) | ||
|
||
def broadcast_ping(self): | ||
self.server.write_broadcast({"command": "ping"}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.