def get_running_mission() -> typing.Union['MissionPath', str]: """ Returns: currently running mission as a MissionPath instance """ mission = None if core.Status.mission_file and core.Status.mission_file != 'unknown': mission_path = Path(core.Status.mission_file) if mission_path.parent == 'AUTO': mission_path = Path(mission_path.parent.parent, mission_path.name) mission = MissionPath(mission_path) else: try: dcs_settings = _get_settings_file_path().read_text() except FileNotFoundError: LOGGER.error( 'please start a DCS server at least once before using ESST') sys.exit(1) else: for line in dcs_settings.split('\n'): if '[1]' in line: mission = MissionPath(line.split('"')[1]) break if mission: LOGGER.debug('returning active mission: %s', mission.name) return mission LOGGER.error('current mission is "%s", but that file does not exist', mission) return ''
def _command(): if self.app.nice() != self.valid_priorities[ DCSConfig.DCS_CPU_PRIORITY()]: LOGGER.debug('setting DCS process priority to: %s', DCSConfig.DCS_CPU_PRIORITY()) self.app.nice( self.valid_priorities[DCSConfig.DCS_CPU_PRIORITY()])
async def on_ready(self): """ Triggers when the bot is ready. """ if not self.ready: self._user = self.client.user await self._update_profile() LOGGER.debug('Logged in as: %s', self.client.user.name) try: self._server = set(self.client.servers).pop() except KeyError: LOGGER.error('Your discord bot has not server to connect to\n' 'Go to https://discordapp.com/developers/applications/me to create a bot, and note ' 'the client ID.\n' 'Use the client ID in the following URL to join you bot to your Discord server:\n' 'https://discordapp.com/oauth2/authorize?client_id=CLIENT_ID&scope=bot') else: self._member = self.server.get_member(self.user.id) if self.user.display_name != DiscordBotConfig.DISCORD_BOT_NAME(): await self.client.change_nickname(self.member, DiscordBotConfig.DISCORD_BOT_NAME()) await self._update_presence() await self.get_channel() self._ready = True
def _write_dedi_config(): dedi_cfg_path = Path(FS.variant_saved_games_path, 'Config/dedicated.lua') if not dedi_cfg_path.exists(): LOGGER.info('writing %s', dedi_cfg_path) dedi_cfg_path.write_text(DEDI_CFG) else: LOGGER.debug('file already exists: %s', dedi_cfg_path)
async def _run(self): if CTX.discord_can_start: LOGGER.debug('starting Discord client') self._create_client() await self.client.start(DiscordBotConfig.DISCORD_TOKEN()) else: await asyncio.sleep(1)
async def run(self): """ Infinite loop that manages a UDP socket and does two things: 1. Retrieve incoming messages from DCS and update :py:class:`esst.core.status.status` 2. Sends command to the DCS application via the socket """ if not CTX.start_listener_loop: LOGGER.debug('skipping startup of socket loop') return try: self.sock.bind(self.server_address) except socket.error as exc: if exc.errno == 10048: LOGGER.error( 'cannot bind socket, maybe another instance of ESST is already running?' ) sys.exit(-1) self.sock.settimeout(1) while not CTX.exit: await self._read_socket() await self._parse_commands() await self._monitor_server_startup() await self._monitor_server() await asyncio.sleep(0.1) self.sock.close() LOGGER.debug('end of listener loop')
def captureException(self, exc_info=None, **kwargs): """Captures an exception""" self.set_context() LOGGER.debug('capturing exception') for k, context_provider in self.registered_contexts.items(): self.extra_context({k: context_provider.get_context()}) super(Sentry, self).captureException(exc_info, **kwargs)
def write_settings_file(self): """ Writes currently known station to UR settings file """ LOGGER.debug('writing UR settings to: %s', FS.ur_voice_settings_file) stations = '\n'.join(self._stations) full_text = f'Start of VSS DB\n{stations}\nEnd of VSS DB' FS.ur_voice_settings_file.write_text(full_text)
def _get_ur_install_path_from_registry() -> typing.Union[Path, None]: LOGGER.debug('searching for base "Saved Games" folder') try: with winreg.OpenKey(A_REG, r"Software\sSoft\UniversRadio") as key: # noinspection SpellCheckingInspection return Path(winreg.QueryValueEx(key, "Install_Dir")[0]) except FileNotFoundError: return None
def poll(): """ Checks that UR voice service is running """ LOGGER.debug('polling UR voice service') proc = psutil.Process(URVoiceService.pid) if not proc.status() == psutil.STATUS_RUNNING: raise RuntimeError('UR voice service stopped')
async def _try_to_connect_to_existing_dcs_application(self): if self.app and self.app.is_running(): return LOGGER.debug('connecting to existing DCS application') await self._check_if_dcs_is_running() if self.process_pid: self._app = psutil.Process(self.process_pid) await self._wait_for_dcs_to_start()
def _queue_kill(queue: Queue): while DCS.there_are_connected_players(): if not queue.empty(): queue.get_nowait() LOGGER.debug('queued DCS kill has been cancelled') return time.sleep(5) LOGGER.info('executing planned DCS restart') DCS.kill()
def send_file(file_path: str): """ Sends a file to the active channel Args: file_path: path to the file to send """ LOGGER.debug('sending file to Discord: %s', file_path) CTX.discord_file_queue.put(file_path)
async def run(self): """ Entry point of the loop """ if not CTX.start_server_loop: LOGGER.debug('skipping server loop') return CTX.loop.run_in_executor(None, self._update_status) LOGGER.debug('end of Server computer loop')
async def _parse_commands(self): await asyncio.sleep(0.1) if not CTX.listener_cmd_queue.empty(): command = CTX.listener_cmd_queue.get_nowait() if command not in KNOWN_COMMANDS: raise ValueError(f'unknown command: {command}') else: command = {'cmd': command} command = json.dumps(command) + '\n' LOGGER.debug('sending command via socket: %s', command) self.cmd_sock.sendto(command.encode(), self.cmd_address)
def init_atis_module(): """ Initialize the ATIS module """ LOGGER.info('initializing ATIS module') discover_ur_install_path() if CTX.sentry: LOGGER.debug('registering ATIS contexts for Sentry') CTX.sentry.register_context(context_name='ATIS', context_provider=_ATISStatus) CTX.sentry.register_context(context_name='UR', context_provider=_URStatus) elib_wx.Config.dummy_icao_code = ATISConfig.DEFAULT_ICAO()
def install_game_gui_hooks(): """ Installs the GameGUI hooks in DCS Scripts folder """ _remove_old_file() if CTX.dcs_install_hooks: LOGGER.debug('installing GameGUI hooks') _install_hook() else: LOGGER.debug('skipping installation of GameGUI hooks')
async def _wait_for_dcs_to_start(self): async def _wait_for_process(): while True: if core.CTX.exit: return await asyncio.sleep(0.1) if self.app.is_running(): break LOGGER.debug('waiting for DCS to spool up') await _wait_for_process() LOGGER.debug('process is ready')
def there_are_connected_players() -> bool: """ Returns: bool indicating if there are connected players """ connected_players = bool(Status.players) if connected_players: LOGGER.debug('there are %s connected player(s)', len(Status.players)) else: LOGGER.debug('there is no connected players') return connected_players
async def _no_more_mr_nice_guy(): if not self.app or not self.app.is_running(): return True LOGGER.debug('killing dcs.exe application') self.app.kill() now_ = utils.now() while self.app.is_running(): await asyncio.sleep(1) if utils.now() - now_ > 10: return False return True
def _make_request(endpoint): url = f'{BASE_URL}{endpoint}' LOGGER.debug(url) req = requests.get(url) if req.ok: return json.loads(req.text or req.content) if req.text or req.content: resp = json.loads(req.text or req.content) if resp['message']: raise ConnectionError( f'Request failed: {url}\nMessage: {resp["message"]}') raise ConnectionError(f'Request failed: {url}')
def set_active_mission(mission_path_as_str: str, metar: str = None): """ Sets the mission as active in "serverSettings.lua" Args: mission_path_as_str: path or name of the MIZ file metar: METAR string for this mission """ LOGGER.debug('setting active mission: %s', mission_path_as_str) if metar: LOGGER.debug('using METAR: %s', metar) mission_path = MissionPath(mission_path_as_str) mission_path.set_as_active(metar)
async def _get_dcs_version_from_executable(self): # noinspection PyBroadException core.Status.dcs_version = utils.get_product_version(str(FS.dcs_exe)) LOGGER.debug('DCS version: %s', core.Status.dcs_version) simplified_version = int(''.join( core.Status.dcs_version.split('.')[:3])) LOGGER.debug('simplified version: %s', simplified_version) if simplified_version <= 157: pass elif simplified_version >= 158: mission_editor_lua.inject_mission_editor_code() autoexec_cfg.inject_silent_crash_report() setup_config_for_dedicated_run() return True
def __init__(self): if not CTX.start_server_loop: LOGGER.debug('skipping server loop init') return ServerStatus.logical_cpus = psutil.cpu_count() ServerStatus.physical_cpus = psutil.cpu_count(logical=False) ServerStatus.cpu_frequency = psutil.cpu_freq().max ServerStatus.total_memory = psutil.virtual_memory().total ServerStatus.swap_size = psutil.swap_memory().total ServerStatus.boot_time = datetime.datetime.fromtimestamp( psutil.boot_time() ).strftime("%Y-%m-%d %H:%M:%S")
def inject_silent_crash_report() -> bool: """ Injects code needed for the new login method in MissionEditor.lua :return: success of the operation :rtype: bool """ FS.ensure_path(FS.saved_games_path, 'saved games') autoexec_path = FS.ensure_path(FS.dcs_autoexec_file, 'dcs autoexec file', must_exist=False) utils.create_versioned_backup(autoexec_path, file_must_exist=False) if autoexec_path.exists(): LOGGER.debug('autoexec.cfg already exists, reading') content = autoexec_path.read_text(encoding='utf8') else: LOGGER.debug('autoexec.cfg does not exist, creating') content = '' if _SILENT_CRASH_REPORT in content: LOGGER.debug('silent crash report already enabled') return True content = f'{content}{_SILENT_CRASH_REPORT}' LOGGER.debug('writing new "autoexec.cfg" content: %s', content) autoexec_path.write_text(content) return True
def get_latest_mission_from_github(): """ Downloads the latest mission from a Github repository The repository needs to have releases (tagged) The function will download the first MIZ file found in the latest release """ if core.CTX.dcs_auto_mission: LOGGER.debug('getting latest mission from Github') commands.DCS.block_start('loading mission') if DCSConfig.DCS_AUTO_MISSION_GH_OWNER( ) and DCSConfig.DCS_AUTO_MISSION_GH_REPO(): LOGGER.debug('looking for newer mission file') latest_version, asset_name, download_url = utils.get_latest_release( DCSConfig.DCS_AUTO_MISSION_GH_OWNER(), DCSConfig.DCS_AUTO_MISSION_GH_REPO()) LOGGER.debug('latest release: %s', latest_version) local_file = MissionPath(Path(_get_mission_folder(), asset_name)) if not local_file: LOGGER.info('downloading new mission: %s', asset_name) req = requests.get(download_url) if req.ok: local_file.path.write_bytes(req.content) local_file.set_as_active() else: LOGGER.error('failed to download latest mission') else: LOGGER.warning('no config values given for [auto mission]') commands.DCS.unblock_start('loading mission') else: LOGGER.debug('skipping mission update')
def initial_setup(): """ Runs at the start of the DCS loop, to initialize the first mission """ LOGGER.debug('initializing first mission') mission = get_running_mission() if isinstance(mission, MissionPath): LOGGER.info('building METAR for initial mission: %s', mission.orig_name) weather = elib_wx.Weather(str(mission.path)) core.Status.metar = weather esst.atis.create.generate_atis(weather) else: LOGGER.error('no initial mission found')
def write_server_settings(mission_file_path: typing.Optional[str] = None) -> None: """ Write "serverSettings.lua" :param mission_file_path: path to the mission file to set as active :type mission_file_path: str or None """ LOGGER.debug('writing server settings') if mission_file_path is None: LOGGER.debug('no mission file given, using current mission') _mission_file_path = _get_current_mission_path() else: _mission_file_path = mission_file_path LOGGER.debug('mission file path: %s', _mission_file_path) template_option = dict( mission_file_path=_mission_file_path, is_public=str(DCSServerConfig.public()).lower(), description=DCSServerConfig.description(), require_pure_textures=str(DCSServerConfig.requires_pure_textures()).lower(), allow_change_tailno=str(DCSServerConfig.allow_change_tail_number()).lower(), allow_ownship_export=str(DCSServerConfig.allow_export_own_ship()).lower(), allow_object_export=str(DCSServerConfig.allow_export_objects()).lower(), pause_on_load=str(DCSServerConfig.pause_on_load()).lower(), allow_sensor_export=str(DCSServerConfig.allow_export_sensors()).lower(), event_Takeoff=str(DCSServerConfig.report_takeoff()).lower(), pause_without_clients=str(DCSServerConfig.pause_without_client()).lower(), client_outbound_limit=DCSServerConfig.outbound_limit(), client_inbound_limit=DCSServerConfig.inbound_limit(), event_Role=str(DCSServerConfig.report_role_change()).lower(), allow_change_skin=str(DCSServerConfig.allow_change_skin()).lower(), event_Connect=str(DCSServerConfig.report_connect()).lower(), event_Ejecting=str(DCSServerConfig.report_eject()).lower(), event_Kill=str(DCSServerConfig.report_kill()).lower(), event_Crash=str(DCSServerConfig.report_crash()).lower(), resume_mode=DCSServerConfig.pause_resume_mode(), maxPing=DCSServerConfig.max_ping(), require_pure_models=str(DCSServerConfig.requires_pure_models()).lower(), require_pure_clients=str(DCSServerConfig.requires_pure_clients()).lower(), name=DCSServerConfig.name(), port=DCSServerConfig.port(), password=DCSServerConfig.password(), bind_address=DCSServerConfig.bind_address(), maxPlayers=DCSServerConfig.max_players() ) LOGGER.debug('rendering settings.lua template with options\n%s', pprint.pformat(template_option)) content = Template(utils.read_template('settings.lua')).render(**template_option) server_settings = _get_server_settings_path() LOGGER.debug('settings file path: %s', server_settings) utils.create_versioned_backup(server_settings) server_settings.write_text(content)
def restart(force: bool = False): """ Sets the context to restart the DCS application Returns: None if restart is OK, err as a str otherwise """ CANCEL_QUEUED_KILL.put(1) if DCS.there_are_connected_players() and not force: LOGGER.error( 'there are connected players; cannot restart the server now ' ' (use "--force" to kill anyway)') return LOGGER.debug('setting context for DCS restart') CTX.dcs_do_restart = True
async def _start_new_dcs_application_if_needed(self): async def _start_dcs_app(): LOGGER.debug('starting DCS application process: %s', FS.dcs_exe) self._app = psutil.Popen(str(FS.dcs_exe)) if self.app and self.app.is_running(): return LOGGER.debug('starting new DCS application') cmd_chain = [ _start_dcs_app, self._wait_for_dcs_to_start, ] # rotate_dcs_log() await self._execute_cmd_chain(cmd_chain) await self._check_if_dcs_is_running()