def handle_frame(): """This will be called every frame. To allow threads to call stuff from the main thread, tasks can be scheduled using the :func:`minqlx.next_frame` decorator and have it be executed here. """ while True: # This will run all tasks that are currently scheduled. # If one of the tasks throw an exception, it'll log it # and continue execution of the next tasks if any. try: frame_tasks.run(blocking=False) break except: minqlx.log_exception() continue try: minqlx.EVENT_DISPATCHERS["frame"].dispatch() except: minqlx.log_exception() return True try: while True: func, args, kwargs = next_frame_tasks.popleft() frame_tasks.enter(0, 0, func, args, kwargs) except IndexError: pass
def handle_server_command(client_id: int, cmd: str): # noinspection PyBroadException try: # Dispatch the "server_command" event before further processing. try: player = minqlx.Player(client_id) if client_id >= 0 else None except minqlx.NonexistentPlayerError: return True retval = minqlx.EVENT_DISPATCHERS["server_command"].dispatch( player, cmd) if retval is False: return False if isinstance(retval, str): cmd = retval res = _re_vote_ended.match(cmd) if res: if res.group("result") == "passed": minqlx.EVENT_DISPATCHERS["vote_ended"].dispatch(True) else: minqlx.EVENT_DISPATCHERS["vote_ended"].dispatch(False) return cmd except: # pylint: disable=bare-except minqlx.log_exception() return True
def handle_server_command(client_id, cmd): try: # Dispatch the "server_command" event before further processing. try: player = minqlx.Player(client_id) if client_id >= 0 else None except minqlx.NonexistentPlayerError: return True retval = minqlx.EVENT_DISPATCHERS["server_command"].dispatch(player, cmd) if retval == False: return False elif isinstance(retval, str): cmd = retval res = _re_vote_ended.match(cmd) if res: if res.group("result") == "passed": minqlx.EVENT_DISPATCHERS["vote_ended"].dispatch(True) else: minqlx.EVENT_DISPATCHERS["vote_ended"].dispatch(False) return cmd except: minqlx.log_exception() return True
def handle_frame(): """This will be called every frame. To allow threads to call stuff from the main thread, tasks can be scheduled using the :func:`minqlx.next_frame` decorator and have it be executed here. """ while True: # This will run all tasks that are currently scheduled. # If one of the tasks throw an exception, it'll log it # and continue execution of the next tasks if any. # noinspection PyBroadException try: frame_tasks.run(blocking=False) break except: # pylint: disable=bare-except minqlx.log_exception() continue # noinspection PyBroadException try: minqlx.EVENT_DISPATCHERS["frame"].dispatch() except: # pylint: disable=bare-except minqlx.log_exception() return True try: while True: func, args, kwargs = next_frame_tasks.popleft() frame_tasks.enter(0, 0, func, args, kwargs) except IndexError: pass
def handle_console_print(text: Optional[str]): # pylint: disable=inconsistent-return-statements """Called whenever the server prints something to the console and when rcon is used.""" if not text: return # noinspection PyBroadException try: # Log console output. Removes the need to have stdout logs in addition to minqlx.log. minqlx.get_logger().debug(text.rstrip("\n")) res = minqlx.EVENT_DISPATCHERS["console_print"].dispatch(text) if res is False: return False if _print_redirection: global _print_buffer # pylint: disable=global-statement _print_buffer += text # pylint: disable=undefined-variable if isinstance(res, str): return res return text except: # pylint: disable=bare-except minqlx.log_exception() return True
def parse_mappool(self, path): """Read and parse the map pool file into a dictionary. Structure as follows: {'campgrounds': ['ca', 'ffa'], 'overkill': ['ca']} """ mappool = {} try: with open(path, "r") as f: lines = f.readlines() except: minqlx.log_exception() return None for line in lines: li = line.lstrip() # Ignore commented lines. if not li.startswith("#") and "|" in li: key, value = line.split('|', 1) # Maps are case-insensitive, but not factories. key = key.lower() if key in mappool: mappool[key].append(value.strip()) else: mappool[key] = [value.strip()] return mappool
def handle_new_game(is_restart): # This is called early in the launch process, so it's a good place to initialize # minqlx stuff that needs QLDS to be initialized. global _first_game if _first_game: minqlx.late_init() _first_game = False # A good place to warn the owner if ZMQ stats are disabled. global _zmq_warning_issued if not bool(int(minqlx.get_cvar( "zmq_stats_enable"))) and not _zmq_warning_issued: logger = minqlx.get_logger() logger.warning( "Some events will not work because ZMQ stats is not enabled. " "Launch the server with \"zmq_stats_enable 1\"") _zmq_warning_issued = True minqlx.set_map_subtitles() if not is_restart: try: minqlx.EVENT_DISPATCHERS["map"].dispatch( minqlx.get_cvar("mapname"), minqlx.get_cvar("g_factory")) except: minqlx.log_exception() return True try: minqlx.EVENT_DISPATCHERS["new_game"].dispatch() except: minqlx.log_exception() return True
def handle_new_game(is_restart): # This is called early in the launch process, so it's a good place to initialize # minqlx stuff that needs QLDS to be initialized. global _first_game if _first_game: minqlx.late_init() _first_game = False # A good place to warn the owner if ZMQ stats are disabled. global _zmq_warning_issued if not bool(int(minqlx.get_cvar("zmq_stats_enable"))) and not _zmq_warning_issued: logger = minqlx.get_logger() logger.warning("Some events will not work because ZMQ stats is not enabled. " "Launch the server with \"zmq_stats_enable 1\"") _zmq_warning_issued = True minqlx.set_map_subtitles() if not is_restart: try: minqlx.EVENT_DISPATCHERS["map"].dispatch( minqlx.get_cvar("mapname"), minqlx.get_cvar("g_factory")) except: minqlx.log_exception() return True try: minqlx.EVENT_DISPATCHERS["new_game"].dispatch() except: minqlx.log_exception() return True
def dispatch(self, *args, **kwargs): """Calls all the handlers that have been registered when hooking this event. The recommended way to use this for events that inherit this class is to override the method with explicit arguments (as opposed to this one's) and call this method by using ``super().dispatch()``. Handlers have several options for return values that can affect the flow: - minqlx.RET_NONE or None -- Continue execution normally. - minqlx.RET_STOP -- Stop any further handlers from being called. - minqlx.RET_STOP_EVENT -- Let handlers process it, but stop the event at the engine-level. - minqlx.RET_STOP_ALL -- Stop handlers **and** the event. - Any other value -- Passed on to :func:`self.handle_return`, which will by default simply send a warning to the logger about an unknown value being returned. Can be overridden so that events can have their own special return values. :param: args: Any arguments. :param: kwargs: Any keyword arguments. """ # Allow subclasses of this to edit the arguments without having # to reimplement this method. Whenever an unknown return value # is returned, we pass it on to handle_return. self.args = args self.kwargs = kwargs logger = minqlx.get_logger() # Log the events as they come in. if self.name not in self.no_debug: dbgstr = f"{self.name}{args}" if len(dbgstr) > 100: dbgstr = dbgstr[0:99] + ")" logger.debug(dbgstr) plugins = self.plugins.copy() self.return_value = True for i in range(5): for plugin in plugins: for handler in plugins[plugin][i]: # noinspection PyBroadException try: res = handler(*self.args, **self.kwargs) if res == minqlx.RET_NONE or res is None: continue if res == minqlx.RET_STOP: return True if res == minqlx.RET_STOP_EVENT: self.return_value = False if res == minqlx.RET_STOP_ALL: return False # Got an unknown return value. return_handler = self.handle_return(handler, res) # pylint: disable=assignment-from-no-return if return_handler is not None: return return_handler except: # pylint: disable=bare-except minqlx.log_exception(plugin) continue return self.return_value
def remove_from_spec(self, player): try: self._spec.remove_from_spec(player.steam_id) except Exception as e: if self.logging_enabled: minqlx.log_exception(self) minqlx.console_print( "Battle Royale Remove from Spec error: {}".format(e))
def cmd_reload_config(self, player, msg, channel): try: minqlx.reload_config() channel.reply("^7The config file was reloaded successfully.") except Exception as e: channel.reply("^7The config file has failed to reload: {} - {}" .format(e.__class__.__name__, e)) minqlx.log_exception(self)
def dispatch(self, *args, **kwargs): """Calls all the handlers that have been registered when hooking this event. The recommended way to use this for events that inherit this class is to override the method with explicit arguments (as opposed to the this one's) and call this method by using ``super().dispatch()``. Handlers have several options for return values that can affect the flow: - minqlx.RET_NONE or None -- Continue execution normally. - minqlx.RET_STOP -- Stop any further handlers from being called. - minqlx.RET_STOP_EVENT -- Let handlers process it, but stop the event at the engine-level. - minqlx.RET_STOP_ALL -- Stop handlers **and** the event. - Any other value -- Passed on to :func:`self.handle_return`, which will by default simply send a warning to the logger about an unknown value being returned. Can be overridden so that events can have their own special return values. :param args: Any arguments. :param kwargs: Any keyword arguments. """ # Allow subclasses of this to edit the arguments without having # to reimplement this method. Whenever an unknown return value # is returned, we pass it on to handle_return. self.args = args self.kwargs = kwargs logger = minqlx.get_logger() # Log the events as they come in. if self.name not in self.no_debug: dbgstr = "{}{}".format(self.name, args) if len(dbgstr) > 100: dbgstr = dbgstr[0:99] + ")" logger.debug(dbgstr) plugins = self.plugins.copy() self.return_value = True for i in range(5): for plugin in plugins: for handler in plugins[plugin][i]: try: res = handler(*self.args, **self.kwargs) if res == minqlx.RET_NONE or res is None: continue elif res == minqlx.RET_STOP: return True elif res == minqlx.RET_STOP_EVENT: self.return_value = False elif res == minqlx.RET_STOP_ALL: return False else: # Got an unknown return value. return_handler = self.handle_return(handler, res) if return_handler is not None: return return_handler except: minqlx.log_exception(plugin) continue return self.return_value
def handle_set_configstring(index, value): """Called whenever the server tries to set a configstring. Can return False to stop the event. """ try: res = minqlx.EVENT_DISPATCHERS["set_configstring"].dispatch(index, value) if res == False: return False elif isinstance(res, str): value = res # GAME STATE CHANGES if index == 0: old_cs = minqlx.parse_variables(minqlx.get_configstring(index)) if not old_cs: return new_cs = minqlx.parse_variables(value) old_state = old_cs["g_gameState"] new_state = new_cs["g_gameState"] if old_state != new_state: if old_state == "PRE_GAME" and new_state == "IN_PROGRESS": minqlx.EVENT_DISPATCHERS["vote_ended"].cancel() # Cancel current vote if any. # minqlx.EVENT_DISPATCHERS["game_start"].dispatch() elif old_state == "PRE_GAME" and new_state == "COUNT_DOWN": minqlx.EVENT_DISPATCHERS["game_countdown"].dispatch() elif old_state == "COUNT_DOWN" and new_state == "IN_PROGRESS": minqlx.EVENT_DISPATCHERS["vote_ended"].cancel() # Cancel current vote if any. # minqlx.EVENT_DISPATCHERS["game_start"].dispatch() elif old_state == "IN_PROGRESS" and new_state == "PRE_GAME": pass elif old_state == "COUNT_DOWN" and new_state == "PRE_GAME": pass else: logger = minqlx.get_logger() logger.warning("UNKNOWN GAME STATES: {} - {}".format(old_state, new_state)) # ROUND COUNTDOWN AND START if index == 661: cvars = minqlx.parse_variables(value) if cvars: round_number = int(cvars["round"]) if round_number and "time" in cvars: if round_number == 1: # This is the case when the first countdown starts. minqlx.EVENT_DISPATCHERS["round_countdown"].dispatch(round_number) return minqlx.EVENT_DISPATCHERS["round_countdown"].dispatch(round_number) return elif round_number: minqlx.EVENT_DISPATCHERS["round_start"].dispatch(round_number) return return res except: minqlx.log_exception() return True
def handle_client_command(client_id, cmd): """Client commands are commands such as "say", "say_team", "scores", "disconnect" and so on. This function parses those and passes it on to the event dispatcher. :param client_id: The client identifier. :type client_id: int :param cmd: The command being ran by the client. :type cmd: str """ try: # Dispatch the "client_command" event before further processing. player = minqlx.Player(client_id) if minqlx.EVENT_DISPATCHERS["client_command"].dispatch(player, cmd) == False: return False # Beyond this point, we deal with events that don't overlap, # meaning we can safely return the result of the dispatch method. res = _re_say.match(cmd) if res: msg = res.group("msg").replace("\"", "") channel = minqlx.CHAT_CHANNEL return minqlx.EVENT_DISPATCHERS["chat"].dispatch( player, msg, channel) res = _re_say_team.match(cmd) if res: msg = res.group("msg").replace("\"", "") if player.team == "free": # I haven't tried this, but I don't think it's even possible. channel = minqlx.FREE_CHAT_CHANNEL elif player.team == "red": channel = minqlx.RED_TEAM_CHAT_CHANNEL elif player.team == "blue": channel = minqlx.BLUE_TEAM_CHAT_CHANNEL else: channel = minqlx.SPECTATOR_CHAT_CHANNEL return minqlx.EVENT_DISPATCHERS["chat"].dispatch( player, msg, channel) res = _re_callvote.match(cmd) if res: vote = res.group("cmd") args = res.group("args") if res.group("args") else "" return minqlx.EVENT_DISPATCHERS["vote_called"].dispatch( player, vote, args) res = _re_vote.match(cmd) if res and minqlx.Plugin.is_vote_active(): arg = res.group("arg") if arg.lower() == "y" or arg == "1": return minqlx.EVENT_DISPATCHERS["vote"].dispatch(True) elif arg.lower() == "n" or arg == "1": return minqlx.EVENT_DISPATCHERS["vote"].dispatch(False) except: minqlx.log_exception() return True
def f(): try: minqlx.load_preset_plugins() except Exception as e: channel.reply("Plugins failed to load: {} - {}" .format(e.__class__.__name__, e)) minqlx.log_exception(self) channel.reply("Successfully loaded all plugins in ^6qlx_plugins^7.")
def add_to_queue(self, player): try: self.remove_from_spec(player) self._queue.add_to_queue(player.steam_id, player) except Exception as e: if self.logging_enabled: minqlx.log_exception(self) minqlx.console_print( "Battle Royale Add to Queue error: {}".format(e))
def f(): try: minqlx.reload_plugin(msg[1]) channel.reply("Plugin ^6{} ^7has been successfully reloaded." .format(msg[1])) except Exception as e: channel.reply("Plugin ^6{} ^7has failed to reload: {} - {}" .format(msg[1], e.__class__.__name__, e)) minqlx.log_exception(self)
def remove_from_queue(self, player): try: if player in self._queue: self._queue.remove_from_queue(player.steam_id, player) except Exception as e: if self.logging_enabled: minqlx.log_exception(self) minqlx.console_print( "Battle Royale Remove from Queue error: {}".format(e))
def add_spectators(self): try: for player in self.teams()["spectator"]: self.add_to_spec(player) except Exception as e: if self.logging_enabled: minqlx.log_exception(self) minqlx.console_print( "Battle Royale Add Spectators error: {}".format(e))
def f(): try: minqlx.COMMANDS.handle_input( IrcDummyPlayer(self.irc, user[0]), " ".join(msg[1:]), IrcChannel(self.irc, user[0])) except Exception as e: irc.msg(channel, "{}: {}".format(e.__class__.__name__, e)) minqlx.log_exception()
def keep_receiving(self): """Receives until self.done is set to True. Works by scheduling this to be called every 0.25 seconds. If we get an exception, we try to reconnect and continue. """ try: if self.done: return while True: # Will throw an expcetion if no more data to get. stats = self.socket.recv_json(zmq.NOBLOCK) minqlx.EVENT_DISPATCHERS["stats"].dispatch(stats) if stats["TYPE"] == "MATCH_STARTED": self._in_progress = True minqlx.EVENT_DISPATCHERS["game_start"].dispatch(stats["DATA"]) elif stats["TYPE"] == "ROUND_OVER": minqlx.EVENT_DISPATCHERS["round_end"].dispatch(stats["DATA"]) elif stats["TYPE"] == "MATCH_REPORT": global _in_progress # MATCH_REPORT event goes off with a map change and map_restart, # but we really only want it for when the game actually ends. # We use a variable instead of Game().state because by the # time we get the event, the game is probably gone. if self._in_progress: minqlx.EVENT_DISPATCHERS["game_end"].dispatch(stats["DATA"]) self._in_progress = False elif stats["TYPE"] == "PLAYER_DEATH": # Dead player. sid = int(stats["DATA"]["VICTIM"]["STEAM_ID"]) if sid: player = minqlx.Plugin.player(sid) else: # It's a bot. Forced to use name as an identifier. player = minqlx.Plugin.player(stats["DATA"]["VICTIM"]["NAME"]) # Killer player. if not stats["DATA"]["KILLER"]: player_killer = None else: sid_killer = int(stats["DATA"]["KILLER"]["STEAM_ID"]) if sid_killer: player_killer = minqlx.Plugin.player(sid_killer) else: # It's a bot. Forced to use name as an identifier. player_killer = minqlx.Plugin.player(stats["DATA"]["KILLER"]["NAME"]) minqlx.EVENT_DISPATCHERS["death"].dispatch(player, player_killer, stats["DATA"]) if player_killer: minqlx.EVENT_DISPATCHERS["kill"].dispatch(player, player_killer, stats["DATA"]) except zmq.error.Again: pass except Exception: minqlx.log_exception() # Reconnect, just in case. GC will clean up for us. self.__init__() self.keep_receiving()
def add_to_spec(self, player): try: self._spec.add_to_spec(player.steam_id) if self.get_cvar("qlx_brTellWhenInSpecOnly", bool): player.center_print( "^6Spectate Mode\n^7Type ^4!s ^7to show spectators.") except Exception as e: if self.logging_enabled: minqlx.log_exception(self) minqlx.console_print( "Battle Royale Add to Spec error: {}".format(e))
def return_players_to_game(self): try: time.sleep(0.5) self.place_in_team(0) if self._rounds > 0: self.allready() except Exception as e: if self.logging_enabled: minqlx.log_exception(self) minqlx.console_print( "Battle Royale Return Players to Game error: {}".format(e))
def handle_client_command(client_id, cmd): """Client commands are commands such as "say", "say_team", "scores", "disconnect" and so on. This function parses those and passes it on to the event dispatcher. :param client_id: The client identifier. :type client_id: int :param cmd: The command being ran by the client. :type cmd: str """ try: # Dispatch the "client_command" event before further processing. player = minqlx.Player(client_id) if minqlx.EVENT_DISPATCHERS["client_command"].dispatch(player, cmd) == False: return False # Beyond this point, we deal with events that don't overlap, # meaning we can safely return the result of the dispatch method. res = _re_say.match(cmd) if res: msg = res.group("msg").replace('"', "") channel = minqlx.CHAT_CHANNEL return minqlx.EVENT_DISPATCHERS["chat"].dispatch(player, msg, channel) res = _re_say_team.match(cmd) if res: msg = res.group("msg").replace('"', "") if player.team == "free": # I haven't tried this, but I don't think it's even possible. channel = minqlx.FREE_CHAT_CHANNEL elif player.team == "red": channel = minqlx.RED_TEAM_CHAT_CHANNEL elif player.team == "blue": channel = minqlx.BLUE_TEAM_CHAT_CHANNEL else: channel = minqlx.SPECTATOR_CHAT_CHANNEL return minqlx.EVENT_DISPATCHERS["chat"].dispatch(player, msg, channel) res = _re_callvote.match(cmd) if res: vote = res.group("cmd") args = res.group("args") if res.group("args") else "" return minqlx.EVENT_DISPATCHERS["vote_called"].dispatch(player, vote, args) res = _re_vote.match(cmd) if res and minqlx.Plugin.is_vote_active(): arg = res.group("arg") if arg.lower() == "y" or arg == "1": return minqlx.EVENT_DISPATCHERS["vote"].dispatch(True) elif arg.lower() == "n" or arg == "1": return minqlx.EVENT_DISPATCHERS["vote"].dispatch(False) except: minqlx.log_exception() return True
def handle_rcon(cmd): """Console commands that are to be processed as regular pyminqlx commands as if the owner executes it. This allows the owner to interact with the Python part of minqlx without having to connect. """ try: minqlx.COMMANDS.handle_input(minqlx.RconDummyPlayer(), cmd, minqlx.CONSOLE_CHANNEL) except: minqlx.log_exception() return True
def cmd_unload(self, player, msg, channel): if len(msg) < 2: return minqlx.CMD_USAGE else: try: minqlx.unload_plugin(msg[1]) channel.reply("Plugin ^6{} ^7has been successfully unloaded." .format(msg[1])) except Exception as e: channel.reply("Plugin ^6{} ^7has failed to unload: {} - {}" .format(msg[1], e.__class__.__name__, e)) minqlx.log_exception(self)
def cmd_unloadall(self, player, msg, channel): for plugin in self.plugins: if plugin != self.__class__.__name__: try: minqlx.unload_plugin(plugin) except Exception as e: channel.reply("Plugin ^6{} ^7has failed to unload: {} - {}" .format(plugin, e.__class__.__name__, e)) minqlx.log_exception(self) channel.reply("Successfully unloaded all plugins except {}." .format(self.__class__.__name__))
def handle_player_spawn(client_id): """Called when a player spawns. Note that a spectator going in free spectate mode makes the client spawn, so you'll want to check for that if you only want "actual" spawns. """ try: player = minqlx.Player(client_id) return minqlx.EVENT_DISPATCHERS["player_spawn"].dispatch(player) except: minqlx.log_exception() return True
def cmd_reload(self, player, msg, channel): if len(msg) < 2: return minqlx.CMD_USAGE else: try: minqlx.reload_plugin(msg[1]) channel.reply("^7Plugin ^6{} ^7has been successfully reloaded." .format(msg[1])) except Exception as e: channel.reply("^7Plugin ^6{} ^7has failed to reload: {} - {}" .format(msg[1], e.__class__.__name__, e)) minqlx.log_exception(self)
def execute_qlx_command(self, user: discord.User, message: Message, qlx_command: str): discord_interaction = DiscordInteractionChannel(user, message, loop=self.bot.loop) try: minqlx.COMMANDS.handle_input(discord_interaction, qlx_command, discord_interaction) except Exception as e: # pylint: disable=broad-except send_message = message.edit(content=f"{e.__class__.__name__}: {e}") asyncio.run_coroutine_threadsafe(send_message, loop=self.bot.loop) minqlx.log_exception()
def handle_player_disconnect(self, player, reason): self.remove_from_spec(player) self.remove_from_queue(player) try: del self._wins[player.steam_id] except KeyError: pass except Exception as e: if self.logging_enabled: minqlx.log_exception(self) minqlx.console_print( "Battle Royale Player Disconnect Error: {}".format(e)) self.check_for_opening(0.5)
def handle_rcon(cmd: str): # pylint: disable=inconsistent-return-statements """Console commands that are to be processed as regular pyminqlx commands as if the owner executes it. This allows the owner to interact with the Python part of minqlx without having to connect. """ # noinspection PyBroadException try: minqlx.COMMANDS.handle_input(minqlx.RconDummyPlayer(), cmd, minqlx.CONSOLE_CHANNEL) except: # pylint: disable=bare-except minqlx.log_exception() return True
def handle_player_disconnect(client_id, reason): """This will be called whenever a player disconnects. :param client_id: The client identifier. :type client_id: int """ try: player = minqlx.Player(client_id) return minqlx.EVENT_DISPATCHERS["player_disconnect"].dispatch(player, reason) except: minqlx.log_exception() return True
def handle_kamikaze_use(client_id): """This will be called whenever player uses kamikaze item. :param client_id: The client identifier. :type client_id: int """ try: player = minqlx.Player(client_id) return minqlx.EVENT_DISPATCHERS["kamikaze_use"].dispatch(player) except: minqlx.log_exception() return True
def handle_player_disconnect(client_id): """This will be called whenever a player disconnects. :param client_id: The client identifier. :type client_id: int """ try: player = minqlx.Player(client_id) return minqlx.EVENT_DISPATCHERS["player_disconnect"].dispatch(player) except: minqlx.log_exception() return True
def handle_console_print(self, text): """Called whenever the server prints something to the console.""" try: if not text: return if _map_redirection: global _map_buffer if '.bsp' in text: _map_buffer += text except: minqlx.log_exception() return True
def handle_kamikaze_use(client_id: int): """This will be called whenever player uses kamikaze item. :param: client_id: The client identifier. :type: client_id: int """ # noinspection PyBroadException try: player = minqlx.Player(client_id) return minqlx.EVENT_DISPATCHERS["kamikaze_use"].dispatch(player) except: # pylint: disable=bare-except minqlx.log_exception() return True
def restoreIdentFile(self): """Restore the identfile.""" if not os.path.isfile(IDENTFILE): return try: with open(IDENTFILE, 'w') as ifile: for l in self.ifile_buf: ifile.write(l) except Exception: minqlx.log_exception() # We're done, release the lock so other # minqlx-plugins can do the same self.flock.release()
def run(self): loop = asyncio.new_event_loop() logger = minqlx.get_logger("irc") asyncio.set_event_loop(loop) while not self.stop_event.is_set(): try: loop.run_until_complete(self.connect()) except Exception: minqlx.log_exception() # Disconnected. Try reconnecting in 30 seconds. logger.info("Disconnected from IRC. Reconnecting in 30 seconds...") time.sleep(30) loop.close()
def handle_player_loaded(client_id): """This will be called whenever a player has connected and finished loading, meaning it'll go off a bit later than the usual "X connected" messages. This will not trigger on bots. :param client_id: The client identifier. :type client_id: int """ try: player = minqlx.Player(client_id) return minqlx.EVENT_DISPATCHERS["player_loaded"].dispatch(player) except: minqlx.log_exception() return True
def run(self): loop = asyncio.new_event_loop() logger = minqlx.get_logger("irc") asyncio.set_event_loop(loop) while not self.stop_event.is_set(): try: loop.run_until_complete(self.connect()) except Exception: minqlx.log_exception() # Disconnected. Try reconnecting in 30 seconds. logger.info("Disconnected from IRC. Reconnecting in 30 seconds...") minqlx.CHAT_CHANNEL.reply("Connection attempt to ^3CommLink^7 server failed, retrying in 30 seconds.") time.sleep(30) loop.close()
def handle_new_game(): # This is called early in the launch process, so it's a good place to warn # the owner if ZMQ stats are disabled. if not bool(int(minqlx.get_cvar("zmq_stats_enable"))) and not _zmq_warning_issued: global _zmq_warning_issued logger = minqlx.get_logger() logger.warning( "Some events will not work because ZMQ stats is not enabled. " 'Launch the server with "zmq_stats_enable 1"' ) _zmq_warning_issued = True try: minqlx.EVENT_DISPATCHERS["map"].dispatch(minqlx.get_cvar("mapname"), minqlx.get_cvar("g_factory")) except: minqlx.log_exception() return True
def handle_kamikaze_explode(client_id, is_used_on_demand): """This will be called whenever kamikaze explodes. :param client_id: The client identifier. :type client_id: int :param is_used_on_demand: Non-zero if kamikaze is used on demand. :type is_used_on_demand: int """ try: player = minqlx.Player(client_id) return minqlx.EVENT_DISPATCHERS["kamikaze_explode"].dispatch(player, True if is_used_on_demand else False) except: minqlx.log_exception() return True
def writeIdentFile(self): """Write self.ident to oidentd's user cfg file but keep any entries for restoring them later.""" if not os.path.isfile(IDENTFILE): return # In the process of connecting, acquire the lock self.flock.acquire() try: with open(IDENTFILE, 'r') as ifile: self.ifile_buf = ifile.readlines() with open(IDENTFILE, 'w') as ifile: ifile.write(IDENTFMT.format(self.idnt)) except Exception: minqlx.log_exception()
def handle_player_connect(client_id, is_bot): """This will be called whenever a player tries to connect. If the dispatcher returns False, it will not allow the player to connect and instead show them a message explaining why. The default message is "You are banned from this server.", but it can be set with :func:`minqlx.set_ban_message`. :param client_id: The client identifier. :type client_id: int :param is_bot: Whether or not the player is a bot. :type is_bot: bool """ try: player = minqlx.Player(client_id) return minqlx.EVENT_DISPATCHERS["player_connect"].dispatch(player) except: minqlx.log_exception() return True
def handle_console_print(text): """Called whenever the server prints something to the console and when rcon is used.""" try: text = text.rstrip() if not text: return # Log console output. Removes the need to have stdout logs in addition to minqlx.log. minqlx.get_logger().debug(text) res = minqlx.EVENT_DISPATCHERS["console_print"].dispatch(text) if res == False: return False elif isinstance(res, str): text = res if _print_redirection: global _print_buffer _print_buffer += text return res except: minqlx.log_exception() return True
def handle_client_command(client_id, cmd): """Client commands are commands such as "say", "say_team", "scores", "disconnect" and so on. This function parses those and passes it on to the event dispatcher. :param client_id: The client identifier. :type client_id: int :param cmd: The command being ran by the client. :type cmd: str """ try: # Dispatch the "client_command" event before further processing. player = minqlx.Player(client_id) retval = minqlx.EVENT_DISPATCHERS["client_command"].dispatch(player, cmd) if retval == False: return False elif isinstance(retval, str): # Allow plugins to modify the command before passing it on. cmd = retval res = _re_say.match(cmd) if res: msg = res.group("msg").replace("\"", "") channel = minqlx.CHAT_CHANNEL if minqlx.EVENT_DISPATCHERS["chat"].dispatch(player, msg, channel) == False: return False return cmd res = _re_say_team.match(cmd) if res: msg = res.group("msg").replace("\"", "") if player.team == "free": # I haven't tried this, but I don't think it's even possible. channel = minqlx.FREE_CHAT_CHANNEL elif player.team == "red": channel = minqlx.RED_TEAM_CHAT_CHANNEL elif player.team == "blue": channel = minqlx.BLUE_TEAM_CHAT_CHANNEL else: channel = minqlx.SPECTATOR_CHAT_CHANNEL if minqlx.EVENT_DISPATCHERS["chat"].dispatch(player, msg, channel) == False: return False return cmd res = _re_callvote.match(cmd) if res and not minqlx.Plugin.is_vote_active(): vote = res.group("cmd") args = res.group("args") if res.group("args") else "" if minqlx.EVENT_DISPATCHERS["vote_called"].dispatch(player, vote, args) == False: return False return cmd res = _re_vote.match(cmd) if res and minqlx.Plugin.is_vote_active(): arg = res.group("arg").lower() if arg == "y" or arg == "1": if minqlx.EVENT_DISPATCHERS["vote"].dispatch(player, True) == False: return False elif arg == "n" or arg == "2": if minqlx.EVENT_DISPATCHERS["vote"].dispatch(player, False) == False: return False return cmd res = _re_team.match(cmd) if res: arg = res.group("arg").lower() target_team = "" if arg == player.team[0]: # Don't trigger if player is joining the same team. return cmd elif arg == "f": target_team = "free" elif arg == "r": target_team = "red" elif arg == "b": target_team = "blue" elif arg == "s": target_team = "spectator" elif arg == "a": target_team = "any" if target_team: if minqlx.EVENT_DISPATCHERS["team_switch_attempt"].dispatch(player, player.team, target_team) == False: return False return cmd res = _re_userinfo.match(cmd) if res: new_info = minqlx.parse_variables(res.group("vars"), ordered=True) old_info = player.cvars changed = {} for key in new_info: if key not in old_info or (key in old_info and new_info[key] != old_info[key]): changed[key] = new_info[key] if changed: ret = minqlx.EVENT_DISPATCHERS["userinfo"].dispatch(player, changed) if ret == False: return False elif isinstance(ret, dict): for key in ret: new_info[key] = ret[key] cmd = "userinfo \"{}\"".format("".join(["\\{}\\{}".format(key, new_info[key]) for key in new_info])) return cmd except: minqlx.log_exception() return True
def f(): try: minqlx.COMMANDS.handle_input(IrcDummyPlayer(self.irc, user[0]), " ".join(msg[1:]), IrcChannel(self.irc, user[0])) except Exception as e: irc.msg(channel, "{}: {}".format(e.__class__.__name__, e)) minqlx.log_exception()