diff options
Diffstat (limited to 'server/tsuserver.py')
| -rw-r--r-- | server/tsuserver.py | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/server/tsuserver.py b/server/tsuserver.py new file mode 100644 index 00000000..14ad60b6 --- /dev/null +++ b/server/tsuserver.py @@ -0,0 +1,263 @@ +# 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 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import asyncio + +import yaml +import json + +from server import logger +from server.aoprotocol import AOProtocol +from server.area_manager import AreaManager +from server.ban_manager import BanManager +from server.client_manager import ClientManager +from server.districtclient import DistrictClient +from server.exceptions import ServerError +from server.masterserverclient import MasterServerClient + +class TsuServer3: + def __init__(self): + self.config = None + self.allowed_iniswaps = None + self.load_config() + self.load_iniswaps() + self.client_manager = ClientManager(self) + self.area_manager = AreaManager(self) + self.ban_manager = BanManager() + self.software = 'tsuserver3' + self.version = 'tsuserver3dev' + self.release = 3 + self.major_version = 1 + self.minor_version = 1 + self.ipid_list = {} + self.hdid_list = {} + self.char_list = None + self.char_pages_ao1 = None + self.music_list = None + self.music_list_ao2 = None + self.music_pages_ao1 = None + self.backgrounds = None + self.load_characters() + self.load_music() + self.load_backgrounds() + self.load_ids() + self.district_client = None + self.ms_client = None + self.rp_mode = False + logger.setup_logger(debug=self.config['debug']) + + def start(self): + loop = asyncio.get_event_loop() + + bound_ip = '0.0.0.0' + if self.config['local']: + bound_ip = '127.0.0.1' + + ao_server_crt = loop.create_server(lambda: AOProtocol(self), bound_ip, self.config['port']) + ao_server = loop.run_until_complete(ao_server_crt) + + if self.config['use_district']: + self.district_client = DistrictClient(self) + asyncio.ensure_future(self.district_client.connect(), loop=loop) + + if self.config['use_masterserver']: + self.ms_client = MasterServerClient(self) + asyncio.ensure_future(self.ms_client.connect(), loop=loop) + + logger.log_debug('Server started.') + + try: + loop.run_forever() + except KeyboardInterrupt: + pass + + logger.log_debug('Server shutting down.') + + ao_server.close() + loop.run_until_complete(ao_server.wait_closed()) + loop.close() + + def get_version_string(self): + return str(self.release) + '.' + str(self.major_version) + '.' + str(self.minor_version) + + def new_client(self, transport): + c = self.client_manager.new_client(transport) + if self.rp_mode: + c.in_rp = True + c.server = self + c.area = self.area_manager.default_area() + c.area.new_client(c) + return c + + def remove_client(self, client): + client.area.remove_client(client) + self.client_manager.remove_client(client) + + def get_player_count(self): + return len(self.client_manager.clients) + + def load_config(self): + with open('config/config.yaml', 'r', encoding = 'utf-8') as cfg: + self.config = yaml.load(cfg) + self.config['motd'] = self.config['motd'].replace('\\n', ' \n') + if 'music_change_floodguard' not in self.config: + self.config['music_change_floodguard'] = {'times_per_interval': 1, 'interval_length': 0, 'mute_length': 0} + if 'wtce_floodguard' not in self.config: + self.config['wtce_floodguard'] = {'times_per_interval': 1, 'interval_length': 0, 'mute_length': 0} + + def load_characters(self): + with open('config/characters.yaml', 'r', encoding = 'utf-8') as chars: + self.char_list = yaml.load(chars) + self.build_char_pages_ao1() + + def load_music(self): + with open('config/music.yaml', 'r', encoding = 'utf-8') as music: + self.music_list = yaml.load(music) + self.build_music_pages_ao1() + self.build_music_list_ao2() + + def load_ids(self): + self.ipid_list = {} + self.hdid_list = {} + #load ipids + try: + with open('storage/ip_ids.json', 'r', encoding = 'utf-8') as whole_list: + self.ipid_list = json.loads(whole_list.read()) + except: + logger.log_debug('Failed to load ip_ids.json from ./storage. If ip_ids.json is exist then remove it.') + #load hdids + try: + with open('storage/hd_ids.json', 'r', encoding = 'utf-8') as whole_list: + self.hdid_list = json.loads(whole_list.read()) + except: + logger.log_debug('Failed to load hd_ids.json from ./storage. If hd_ids.json is exist then remove it.') + + def dump_ipids(self): + with open('storage/ip_ids.json', 'w') as whole_list: + json.dump(self.ipid_list, whole_list) + + def dump_hdids(self): + with open('storage/hd_ids.json', 'w') as whole_list: + json.dump(self.hdid_list, whole_list) + + def get_ipid(self, ip): + if not (ip in self.ipid_list): + self.ipid_list[ip] = len(self.ipid_list) + self.dump_ipids() + return self.ipid_list[ip] + + def load_backgrounds(self): + with open('config/backgrounds.yaml', 'r', encoding = 'utf-8') as bgs: + self.backgrounds = yaml.load(bgs) + + def load_iniswaps(self): + try: + with open('config/iniswaps.yaml', 'r', encoding = 'utf-8') as iniswaps: + self.allowed_iniswaps = yaml.load(iniswaps) + except: + logger.log_debug('cannot find iniswaps.yaml') + + + def build_char_pages_ao1(self): + self.char_pages_ao1 = [self.char_list[x:x + 10] for x in range(0, len(self.char_list), 10)] + for i in range(len(self.char_list)): + self.char_pages_ao1[i // 10][i % 10] = '{}#{}&&0&&&0&'.format(i, self.char_list[i]) + + def build_music_pages_ao1(self): + self.music_pages_ao1 = [] + index = 0 + # add areas first + for area in self.area_manager.areas: + self.music_pages_ao1.append('{}#{}'.format(index, area.name)) + index += 1 + # then add music + for item in self.music_list: + self.music_pages_ao1.append('{}#{}'.format(index, item['category'])) + index += 1 + for song in item['songs']: + self.music_pages_ao1.append('{}#{}'.format(index, song['name'])) + index += 1 + self.music_pages_ao1 = [self.music_pages_ao1[x:x + 10] for x in range(0, len(self.music_pages_ao1), 10)] + + def build_music_list_ao2(self): + self.music_list_ao2 = [] + # add areas first + for area in self.area_manager.areas: + self.music_list_ao2.append(area.name) + # then add music + for item in self.music_list: + self.music_list_ao2.append(item['category']) + for song in item['songs']: + self.music_list_ao2.append(song['name']) + + def is_valid_char_id(self, char_id): + return len(self.char_list) > char_id >= 0 + + def get_char_id_by_name(self, name): + for i, ch in enumerate(self.char_list): + if ch.lower() == name.lower(): + return i + raise ServerError('Character not found.') + + def get_song_data(self, music): + for item in self.music_list: + if item['category'] == music: + return item['category'], -1 + for song in item['songs']: + if song['name'] == music: + try: + return song['name'], song['length'] + except KeyError: + return song['name'], -1 + raise ServerError('Music not found.') + + def send_all_cmd_pred(self, cmd, *args, pred=lambda x: True): + for client in self.client_manager.clients: + if pred(client): + client.send_command(cmd, *args) + + def broadcast_global(self, client, msg, as_mod=False): + char_name = client.get_char_name() + ooc_name = '{}[{}][{}]'.format('<dollar>G', client.area.id, char_name) + if as_mod: + ooc_name += '[M]' + self.send_all_cmd_pred('CT', ooc_name, msg, pred=lambda x: not x.muted_global) + if self.config['use_district']: + self.district_client.send_raw_message( + 'GLOBAL#{}#{}#{}#{}'.format(int(as_mod), client.area.id, char_name, msg)) + + def broadcast_need(self, client, msg): + char_name = client.get_char_name() + area_name = client.area.name + area_id = client.area.id + self.send_all_cmd_pred('CT', '{}'.format(self.config['hostname']), + '=== Advert ===\r\n{} in {} [{}] needs {}\r\n===============' + .format(char_name, area_name, area_id, msg), pred=lambda x: not x.muted_adverts) + if self.config['use_district']: + self.district_client.send_raw_message('NEED#{}#{}#{}#{}'.format(char_name, area_name, area_id, msg)) + + def refresh(self): + with open('config/config.yaml', 'r') as cfg: + self.config['motd'] = yaml.load(cfg)['motd'].replace('\\n', ' \n') + with open('config/characters.yaml', 'r') as chars: + self.char_list = yaml.load(chars) + with open('config/music.yaml', 'r') as music: + self.music_list = yaml.load(music) + self.build_music_pages_ao1() + self.build_music_list_ao2() + with open('config/backgrounds.yaml', 'r') as bgs: + self.backgrounds = yaml.load(bgs) |
