# tsuserver3, an Attorney Online server # # Copyright (C) 2016 argoneus <argoneuscze@gmail.com> # # 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/>. import asyncio import time import logging logger = logging.getLogger('debug') class MasterServerClient: """Advertises information about this server to the master server.""" def __init__(self, server): self.server = server self.reader = None self.writer = None async def connect(self): loop = asyncio.get_event_loop() while True: try: self.reader, self.writer = await asyncio.open_connection( self.server.config['masterserver_ip'], self.server.config['masterserver_port'], loop=loop) await self.handle_connection() except (ConnectionRefusedError, TimeoutError, ConnectionResetError, asyncio.IncompleteReadError): logger.debug('Connection error occurred.') self.writer = None self.reader = None finally: logger.debug('Retrying MS connection in 30 seconds.') await asyncio.sleep(30) async def handle_connection(self): logger.debug('Master server connected.') print('Master server connected ({}:{})'.format( self.server.config['masterserver_ip'], self.server.config['masterserver_port'])) await self.send_server_info() ping_timeout = False last_ping = time.time() - 20 while True: self.reader.feed_data(b'END') full_data = await self.reader.readuntil(b'END') full_data = full_data[:-3] if len(full_data) > 0: data_list = list(full_data.split(b'#%'))[:-1] for data in data_list: raw_msg = data.decode() cmd, *args = raw_msg.split('#') if cmd != 'CHECK' and cmd != 'PONG': logger.debug(f'Incoming: {raw_msg}') elif cmd == 'CHECK': await self.send_raw_message('PING#%') elif cmd == 'PONG': ping_timeout = False elif cmd == 'NOSERV': logger.debug('MS does not have our server. Readvertising.') await self.send_server_info() if time.time() - last_ping > 10: if ping_timeout: self.writer.close() return last_ping = time.time() ping_timeout = True await self.send_raw_message('PING#%') await asyncio.sleep(1) async def send_server_info(self): logger.debug('Advertising to MS') cfg = self.server.config port = str(cfg['port']) if cfg['use_websockets']: port += '&{}'.format(cfg['websocket_port']) msg = 'SCC#{}#{}#{}#{}#%'.format(port, cfg['masterserver_name'], cfg['masterserver_description'], self.server.software) await self.send_raw_message(msg) async def send_raw_message(self, msg): self.writer.write(msg.encode()) await self.writer.drain()