Example #1
0
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
Example #2
0
    def __init__(self):
        self.done: bool
        if not bool(int(minqlx.get_cvar("zmq_stats_enable"))):
            self.done = True
            return

        stats: Optional[str] = minqlx.get_cvar("zmq_stats_ip")
        port: Optional[str] = minqlx.get_cvar("zmq_stats_port")
        if not port:
            port = minqlx.get_cvar("net_port")
        host = "127.0.0.1" if not stats else stats
        self.address: str = f"tcp://{host}:{port}"
        self.password: Optional[str] = minqlx.get_cvar("zmq_stats_password")

        # Initialize socket, connect, and subscribe.
        self.context: zmq.Context = zmq.Context()
        self.socket: zmq.Socket = self.context.socket(zmq.SUB)
        if self.password:
            self.socket.plain_username = b"stats"
            self.socket.plain_password = self.password.encode()
        self.socket.zap_domain = b"stats"
        self.socket.connect(self.address)
        self.socket.setsockopt_string(zmq.SUBSCRIBE, "")

        self.done = False
        self._in_progress: bool = False
Example #3
0
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
Example #4
0
    def __init__(self):
        if not bool(int(minqlx.get_cvar("zmq_stats_enable"))):
            self.done = True
            return

        stats = minqlx.get_cvar("zmq_stats_ip")
        port = minqlx.get_cvar("zmq_stats_port")
        if not port:
            port = minqlx.get_cvar("net_port")
        self.address = "tcp://{}:{}".format(
            "127.0.0.1" if not stats else stats, port)
        self.password = minqlx.get_cvar("zmq_stats_password")

        # Initialize socket, connect, and subscribe.
        self.context = zmq.Context()
        self.socket = self.context.socket(zmq.SUB)
        if self.password:
            self.socket.plain_username = b"stats"
            self.socket.plain_password = self.password.encode()
        self.socket.zap_domain = b"stats"
        self.socket.connect(self.address)
        self.socket.setsockopt_string(zmq.SUBSCRIBE, "")

        self.done = False
        self._in_progress = False
Example #5
0
    def is_eligible_player(self, player, is_client_cmd):
        """Check if a player has the rights to execute the command."""
        # Check if config overrides permission.
        perm = self.permission
        client_cmd_perm = self.client_cmd_perm

        if is_client_cmd:
            cvar_client_cmd = minqlx.get_cvar("qlx_ccmd_perm_" + self.name[0])
            if cvar_client_cmd:
                client_cmd_perm = int(cvar_client_cmd)
        else:
            cvar = minqlx.get_cvar("qlx_perm_" + self.name[0])
            if cvar:
                perm = int(cvar)

        if (player.steam_id == minqlx.owner() or
            (not is_client_cmd and perm == 0) or
            (is_client_cmd and client_cmd_perm == 0)):
            return True
        
        player_perm = self.plugin.db.get_permission(player)
        if is_client_cmd:
            return player_perm >= client_cmd_perm
        else:
            return player_perm >= perm
Example #6
0
    def is_eligible_player(self, player, is_client_cmd):
        """Check if a player has the rights to execute the command."""
        # Check if config overrides permission.
        perm = self.permission
        client_cmd_perm = self.client_cmd_perm

        if is_client_cmd:
            cvar_client_cmd = minqlx.get_cvar("qlx_ccmd_perm_" + self.name[0])
            if cvar_client_cmd:
                client_cmd_perm = int(cvar_client_cmd)
        else:
            cvar = minqlx.get_cvar("qlx_perm_" + self.name[0])
            if cvar:
                perm = int(cvar)

        if (player.steam_id == minqlx.owner()
                or (not is_client_cmd and perm == 0)
                or (is_client_cmd and client_cmd_perm == 0)):
            return True

        player_perm = self.plugin.db.get_permission(player)
        if is_client_cmd:
            return player_perm >= client_cmd_perm
        else:
            return player_perm >= perm
Example #7
0
    def connect(self, host=None, database=0, unix_socket=False, password=None):
        """Returns a connection to a Redis database. If *host* is None, it will
        fall back to the settings in the config and ignore the rest of the arguments.
        It will also share the connection across any plugins using the default
        configuration. Passing *host* will make it connect to a specific database
        that is not shared at all. Subsequent calls to this will return the connection
        initialized the first call unless it has been closed.

        :param host: The host name. If no port is specified, it will use 6379. Ex.: ``localhost:1234``.
        :type host: str
        :param database: The database number that should be used.
        :type database: int
        :param unix_socket: Whether or not *host* should be interpreted as a unix socket path.
        :type unix_socket: bool
        :raises: RuntimeError

        """
        if not host and not self._conn: # Resort to default settings in config?
            if not Redis._conn:
                cvar_host = minqlx.get_cvar("qlx_redisAddress")
                cvar_db = int(minqlx.get_cvar("qlx_redisDatabase"))
                cvar_unixsocket = bool(int(minqlx.get_cvar("qlx_redisUnixSocket")))
                Redis._pass = minqlx.get_cvar("qlx_redisPassword")
                if cvar_unixsocket:
                    Redis._conn = redis.StrictRedis(unix_socket_path=cvar_host,
                        db=cvar_db, password=Redis._pass, decode_responses=True)
                else:
                    split_host = cvar_host.split(":")
                    if len(split_host) > 1:
                        port = int(split_host[1])
                    else:
                        port = 6379 # Default port.
                    Redis._pool = redis.ConnectionPool(host=split_host[0],
                        port=port, db=cvar_db, password=Redis._pass, decode_responses=True)
                    Redis._conn = redis.StrictRedis(connection_pool=Redis._pool, decode_responses=True)
                    # TODO: Why does self._conn get set when doing Redis._conn?
                    self._conn = None
            return Redis._conn
        elif not self._conn:
            split_host = host.split(":")
            if len(split_host) > 1:
                port = int(split_host[1])
            else:
                port = 6379 # Default port.

            if unix_socket:
                self._conn = redis.StrictRedis(unix_socket_path=host, db=database, password=password, decode_responses=True)
            else:
                self._pool = redis.ConnectionPool(host=split_host[0], port=port, db=database, password=password, decode_responses=True)
                self._conn = redis.StrictRedis(connection_pool=self._pool, decode_responses=True)
        return self._conn
Example #8
0
 def start_stats_listener():
     if bool(int(minqlx.get_cvar("zmq_stats_enable"))):
         global _stats
         _stats = minqlx.StatsListener()
         logger.info("Stats listener started on {}.".format(_stats.address))
         # Start polling. Not blocking due to decorator magic. Aw yeah.
         _stats.keep_receiving()
Example #9
0
 def get_maxplayers(self):
     maxplayers = int(self.game.teamsize)
     if self.game.type_short in TEAM_BASED_GAMETYPES:
         maxplayers = maxplayers * 2
     if maxplayers == 0:
         maxplayers = minqlx.get_cvar("sv_maxClients", int)
     return maxplayers
Example #10
0
    def add_hook(self, plugin, handler, priority=minqlx.PRI_NORMAL):
        """Hook the event, making the handler get called with relevant arguments
        whenever the event is takes place.

        :param plugin: The plugin that's hooking the event.
        :type plugin: minqlx.Plugin
        :param handler: The handler to be called when the event takes place.
        :type handler: callable
        :param priority: The priority of the hook. Determines the order the handlers are called in.
        :type priority: minqlx.PRI_LOWEST, minqlx.PRI_LOW, minqlx.PRI_NORMAL, minqlx.PRI_HIGH or minqlx.PRI_HIGHEST
        :raises: ValueError

        """
        if not (minqlx.PRI_HIGHEST <= priority <= minqlx.PRI_LOWEST):
            raise ValueError("'{}' is an invalid priority level.".format(priority))

        if self.need_zmq_stats_enabled and not bool(int(minqlx.get_cvar("zmq_stats_enable"))):
            raise AssertionError("{} hook requires zmq_stats_enabled cvar to have nonzero value".format(self.name))

        if plugin not in self.plugins:
            # Initialize tuple.
            self.plugins[plugin] = ([], [], [], [], []) # 5 priority levels.
        else:
            # Check if we've already registered this handler.
            for i in range(len(self.plugins[plugin])):
                for hook in self.plugins[plugin][i]:
                    if handler == hook:
                        raise ValueError("The event has already been hooked with the same handler and priority.")

        self.plugins[plugin][priority].append(handler)
Example #11
0
    def __init__(self):
        self.add_hook("player_connect", self.handle_player_connect, priority=minqlx.PRI_LOWEST)
        self.add_hook("player_disconnect", self.handle_player_disconnect, priority=minqlx.PRI_LOWEST)
        self.add_hook("chat", self.handle_chat, priority=minqlx.PRI_LOWEST)
        self.add_hook("command", self.handle_command, priority=minqlx.PRI_LOWEST)

        self.set_cvar_once("qlx_chatlogs", "3")
        self.set_cvar_once("qlx_chatlogsSize", str(3 * 10 ** 6))  # 3 MB

        self.chatlog = logging.Logger(__name__)
        file_dir = os.path.join(minqlx.get_cvar("fs_homepath"), "chatlogs")
        if not os.path.isdir(file_dir):
            os.makedirs(file_dir)

        file_path = os.path.join(file_dir, "chat.log")
        maxlogs = minqlx.Plugin.get_cvar("qlx_chatlogs", int)
        maxlogsize = minqlx.Plugin.get_cvar("qlx_chatlogsSize", int)
        file_fmt = logging.Formatter("[%(asctime)s] %(message)s", "%Y-%m-%d %H:%M:%S")
        file_handler = RotatingFileHandler(file_path, encoding="utf-8", maxBytes=maxlogsize, backupCount=maxlogs)
        file_handler.setFormatter(file_fmt)
        self.chatlog.addHandler(file_handler)
        self.chatlog.info(
            "============================= Logger started @ {} =============================".format(
                datetime.datetime.now()
            )
        )
Example #12
0
    def get_cvar(cls, name, return_type=str):
        """Gets the value of a cvar as a string.

        :param name: The name of the cvar.
        :type name: str
        :param return_type: The type the cvar should be returned in.
            Supported types: str, int, float, bool, list, tuple

        """
        res = minqlx.get_cvar(name)
        if return_type == str:
            return res
        elif return_type == int:
            return int(res)
        elif return_type == float:
            return float(res)
        elif return_type == bool:
            return bool(int(res))
        elif return_type == list:
            return [s.strip() for s in res.split(",")]
        elif return_type == set:
            return {s.strip() for s in res.split(",")}
        elif return_type == tuple:
            return tuple([s.strip() for s in res.split(",")])
        else:
            raise ValueError("Invalid return type: {}".format(return_type))
Example #13
0
def load_plugin(plugin):
    logger = get_logger(None)
    logger.info("Loading plugin '{}'...".format(plugin))
    plugins = minqlx.Plugin._loaded_plugins
    plugins_path = os.path.abspath(minqlx.get_cvar("qlx_pluginsPath"))
    plugins_dir = os.path.basename(plugins_path)

    if not os.path.isfile(os.path.join(plugins_path, plugin + ".py")):
        raise PluginLoadError("No such plugin exists.")
    elif plugin in plugins:
        return reload_plugin(plugin)
    try:
        module = importlib.import_module("{}.{}".format(plugins_dir, plugin))
        # We add the module regardless of whether it fails or not, otherwise we can't reload later.
        global _modules
        _modules[plugin] = module

        if not hasattr(module, plugin):
            raise (PluginLoadError(
                "The plugin needs to have a class with the exact name as the file, minus the .py."
            ))

        plugin_class = getattr(module, plugin)
        if issubclass(plugin_class, minqlx.Plugin):
            plugins[plugin] = plugin_class()
        else:
            raise (PluginLoadError(
                "Attempted to load a plugin that is not a subclass of 'minqlx.Plugin'."
            ))
    except:
        log_exception(plugin)
        raise
Example #14
0
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
Example #15
0
 def process_frame(self):
     self.frame_counter += 1
     if self.frame_counter == (
             int(self.get_cvar("qlx_pingSpecSecondsBetweenChecks")) *
             int(minqlx.get_cvar("sv_fps"))):
         self.frame_counter = 0
         self.check_ping()
Example #16
0
def _configure_logger():
    logger = logging.getLogger("minqlx")
    logger.setLevel(logging.DEBUG)

    # File
    file_path = os.path.join(minqlx.get_cvar("fs_homepath"), "minqlx.log")
    maxlogs = minqlx.Plugin.get_cvar("qlx_logs", int)
    maxlogsize = minqlx.Plugin.get_cvar("qlx_logsSize", int)
    file_fmt = logging.Formatter(
        "(%(asctime)s) [%(levelname)s @ %(name)s.%(funcName)s] %(message)s",
        "%H:%M:%S")
    file_handler = RotatingFileHandler(file_path,
                                       encoding="utf-8",
                                       maxBytes=maxlogsize,
                                       backupCount=maxlogs)
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(file_fmt)
    logger.addHandler(file_handler)
    logger.info(
        "============================= minqlx run @ {} ============================="
        .format(datetime.datetime.now()))

    # Console
    console_fmt = logging.Formatter(
        "[%(name)s.%(funcName)s] %(levelname)s: %(message)s", "%H:%M:%S")
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(console_fmt)
    logger.addHandler(console_handler)
Example #17
0
def load_preset_plugins() -> None:
    plugins_temp = []
    plugins_cvar = minqlx.Plugin.get_cvar("qlx_plugins", list)
    if plugins_cvar is None:
        return
    for p in plugins_cvar:
        if p == "DEFAULT":
            plugins_temp += list(DEFAULT_PLUGINS)
        else:
            plugins_temp.append(p)

    plugins = []
    for p in plugins_temp:
        if p not in plugins:
            plugins.append(p)

    plugins_path_cvar = minqlx.get_cvar("qlx_pluginsPath")
    if plugins_path_cvar is None:
        raise PluginLoadError("cvar qlx_pluginsPath misconfigured")

    plugins_path = os.path.abspath(plugins_path_cvar)
    plugins_dir = os.path.basename(plugins_path)

    if os.path.isdir(plugins_path):
        plugins = [p for p in plugins if f"{plugins_dir}.{p}"]
        for p in plugins:
            load_plugin(p)
    else:
        raise PluginLoadError(
            f"Cannot find the plugins directory '{os.path.abspath(plugins_path)}'."
        )
Example #18
0
def load_plugin(plugin):
    logger = get_logger(None)
    logger.info("Loading plugin '{}'...".format(plugin))
    plugins = minqlx.Plugin._loaded_plugins
    plugins_path = os.path.abspath(minqlx.get_cvar("qlx_pluginsPath"))
    plugins_dir = os.path.basename(plugins_path)

    if not os.path.isfile(os.path.join(plugins_path, plugin + ".py")):
        raise PluginLoadError("No such plugin exists.")
    elif plugin in plugins:
        return reload_plugin(plugin)
    try:
        module = importlib.import_module("{}.{}".format(plugins_dir, plugin))
        # We add the module regardless of whether it fails or not, otherwise we can't reload later.
        global _modules
        _modules[plugin] = module

        if not hasattr(module, plugin):
            raise(PluginLoadError("The plugin needs to have a class with the exact name as the file, minus the .py."))

        plugin_class = getattr(module, plugin)
        if issubclass(plugin_class, minqlx.Plugin):
            plugins[plugin] = plugin_class()
        else:
            raise(PluginLoadError("Attempted to load a plugin that is not a subclass of 'minqlx.Plugin'."))
    except:
        log_exception(plugin)
        raise
Example #19
0
    def get_cvar(cls, name: str, return_type: Type[Union[str, bool, int, float, list, set, tuple]] = str) \
            -> Optional[Union[str, bool, int, float, List[str], Set[str], Tuple[str, ...]]]:
        """Gets the value of a cvar as a string.

        :param: name: The name of the cvar.
        :type: name: str
        :param: return_type: The type the cvar should be returned in.
            Supported types: str, int, float, bool, list, tuple

        """
        res = minqlx.get_cvar(name)
        if return_type == str:
            return res
        if return_type == int:
            return int(res) if res else None
        if return_type == float:
            return float(res) if res else None
        if return_type == bool:
            return bool(int(res)) if res else False
        if return_type == list:
            return [s.strip() for s in res.split(",")] if res else []
        if return_type == set:
            return {s.strip() for s in res.split(",")} if res else set()
        if return_type == tuple:
            return ([s.strip() for s in res.split(",")]) if res else ()

        raise ValueError(f"Invalid return type: {return_type}")
 def get_maxplayers(self):
     maxplayers = int(self.game.teamsize)
     if self.game.type_short in TEAM_BASED_GAMETYPES:
         maxplayers = maxplayers * 2
     if maxplayers == 0:
         maxplayers = minqlx.get_cvar("sv_maxClients")
     return maxplayers
Example #21
0
    def get_cvar(cls, name, return_type=str):
        """Gets the value of a cvar as a string.

        :param name: The name of the cvar.
        :type name: str
        :param return_type: The type the cvar should be returned in.
            Supported types: str, int, float, bool, list, tuple

        """
        res = minqlx.get_cvar(name)
        if return_type == str:
            return res
        elif return_type == int:
            return int(res)
        elif return_type == float:
            return float(res)
        elif return_type == bool:
            return bool(int(res))
        elif return_type == list:
            return [s.strip() for s in res.split(",")]
        elif return_type == set:
            return {s.strip() for s in res.split(",")}
        elif return_type == tuple:
            return tuple([s.strip() for s in res.split(",")])
        else:
            raise ValueError("Invalid return type: {}".format(return_type))
Example #22
0
 def is_eligible_name(self, name):
     if self.prefix:
         prefix = minqlx.get_cvar("qlx_commandPrefix")
         if not name.startswith(prefix):
             return False
         name = name[len(prefix):]
     
     return name.lower() in self.name
Example #23
0
    def is_eligible_name(self, name):
        if self.prefix:
            prefix = minqlx.get_cvar("qlx_commandPrefix")
            if not name.startswith(prefix):
                return False
            name = name[len(prefix):]

        return name.lower() in self.name
Example #24
0
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
Example #25
0
def owner():
    """Returns the SteamID64 of the owner. This is set in the config."""
    try:
        sid = int(minqlx.get_cvar("qlx_owner"))
        if sid == -1:
            raise RuntimeError
        return sid
    except:
        logger = minqlx.get_logger()
        logger.error("Failed to parse the Owner Steam ID. Make sure it's in SteamID64 format.")
Example #26
0
def set_cvar_limit_once(name: str,
                        value: Union[int, float],
                        minimum: Union[int, float],
                        maximum: Union[int, float],
                        flags: int = 0) -> bool:
    if minqlx.get_cvar(name) is None:
        minqlx.set_cvar_limit(name, value, minimum, maximum, flags)
        return True

    return False
Example #27
0
def late_init() -> None:
    """Initialization that needs to be called after QLDS has finished
    its own initialization.

    """
    minqlx.initialize_cvars()

    # Set the default database plugins should use.
    # TODO: Make Plugin.database setting generic.
    database_cvar = minqlx.get_cvar("qlx_database")
    if database_cvar is not None and database_cvar.lower() == "redis":
        minqlx.Plugin.database = minqlx.database.Redis

    # Get the plugins path and set minqlx.__plugins_version__.
    plugins_path_cvar = minqlx.get_cvar("qlx_pluginsPath")
    if plugins_path_cvar is not None:
        plugins_path = os.path.abspath(plugins_path_cvar)
        set_plugins_version(plugins_path)

        # Add the plugins path to PATH so that we can load plugins later.
        sys.path.append(os.path.dirname(plugins_path))

    # Initialize the logger now that we have fs_basepath.
    _configure_logger()
    logger = get_logger()
    # Set our own exception handler so that we can log them if unhandled.
    sys.excepthook = handle_exception

    if sys.version_info >= (3, 8):
        threading.excepthook = threading_excepthook

    logger.info("Loading preset plugins...")
    load_preset_plugins()

    stats_enable_cvar = minqlx.get_cvar("zmq_stats_enable")
    if stats_enable_cvar is not None and bool(int(stats_enable_cvar)):
        global _stats  # pylint: disable=global-statement
        _stats = minqlx.StatsListener()
        logger.info("Stats listener started on %s.", _stats.address)
        # Start polling. Not blocking due to decorator magic. Aw yeah.
        _stats.keep_receiving()

    logger.info("We're good to go!")
Example #28
0
def owner():
    """Returns the SteamID64 of the owner. This is set in the config."""
    try:
        sid = int(minqlx.get_cvar("qlx_owner"))
        if sid == -1:
            raise RuntimeError
        return sid
    except:
        logger = minqlx.get_logger()
        logger.error("Failed to parse the Owner Steam ID. Make sure it's in SteamID64 format.")
Example #29
0
    def __init__(self):
        if not bool(int(minqlx.get_cvar("zmq_stats_enable"))):
            self.done = True
            return
        
        stats = minqlx.get_cvar("zmq_stats_ip")
        port = minqlx.get_cvar("zmq_stats_port")
        if not port:
            port = minqlx.get_cvar("net_port")
        self.address = "tcp://{}:{}".format("127.0.0.1" if not stats else stats, port)
        self.password = minqlx.get_cvar("zmq_stats_password")

        # Initialize socket, connect, and subscribe.
        self.context = zmq.Context()
        self.socket = self.context.socket(zmq.SUB)
        self.socket.plain_username = b"stats"
        self.socket.plain_password = self.password.encode()
        self.socket.zap_domain = b"stats"
        self.socket.connect(self.address)
        self.socket.setsockopt_string(zmq.SUBSCRIBE, "")

        self.done = False
        self._in_progress = False
Example #30
0
def late_init():
    """Initialization that needs to be called after QLDS has finished
    its own initialization.

    """
    minqlx.initialize_cvars()

    # Set the default database plugins should use.
    # TODO: Make Plugin.database setting generic.
    if minqlx.get_cvar("qlx_database").lower() == "redis":
        minqlx.Plugin.database = minqlx.database.Redis

    # Get the plugins path and set minqlx.__plugins_version__.
    plugins_path = os.path.abspath(minqlx.get_cvar("qlx_pluginsPath"))
    set_plugins_version(plugins_path)

    # Initialize the logger now that we have fs_basepath.
    _configure_logger()
    logger = get_logger()
    # Set our own exception handler so that we can log them if unhandled.
    sys.excepthook = handle_exception

    # Add the plugins path to PATH so that we can load plugins later.
    sys.path.append(os.path.dirname(plugins_path))

    logger.info("Loading preset plugins...")
    load_preset_plugins()

    if bool(int(minqlx.get_cvar("zmq_stats_enable"))):
        global _stats
        _stats = minqlx.StatsListener()
        logger.info("Stats listener started on {}.".format(_stats.address))
        # Start polling. Not blocking due to decorator magic. Aw yeah.
        _stats.keep_receiving()

    logger.info("We're good to go!")
Example #31
0
 def print_instructions(self, player):
     player.tell("^4Wipeout is a modified Clan Arena gametype with respawns played in Quake Live.\n"
                 "A team wins by having all the players on the opposing team dead at the same time.\n"
                 "When a player dies they spectate for a certain time period. "
                 "The time period starts at 5 seconds but is increased by {1} seconds"
                 " for each death on the player's team.\nThe first team to win {2} rounds wins the match.\n"
                 "When round starts you are given 2 power up holdables. Medkit and either\n"
                 "Invulnerability-Shield/Teleport/Flight/Kamikazee\nwith a {3} percent chance of getting kamakazi.\n"
                 "^1To use them you need to have 2 keys bound:\n"
                 "^61) ^1bind <key> \"+button2\" ^3This is the use bind\n"
                 "^62) ^1bind <key> \"say {0}power\" ^3Item swap (medkit and powerup) bind\n"
                 "Quotes used in the BIND commands need to be ^6double-quotes^3.\n"
                 "If the server is blocking saying {0}power in chat, it will not spam the server."
                 .format(minqlx.get_cvar("qlx_commandPrefix"), self.add_seconds,
                         self.get_cvar("qlx_wipeoutRounds"), self.get_cvar("qlx_wipeoutKamakazi")))
Example #32
0
def load_preset_plugins():
    plugins = minqlx.Plugin.get_cvar("qlx_plugins", set)
    if "DEFAULT" in plugins:
        plugins.remove("DEFAULT")
        plugins.update(DEFAULT_PLUGINS)

    plugins_path = os.path.abspath(minqlx.get_cvar("qlx_pluginsPath"))
    plugins_dir = os.path.basename(plugins_path)

    if os.path.isdir(plugins_path):
        plugins = [p for p in plugins if "{}.{}".format(plugins_dir, p)]
        for p in plugins:
            load_plugin(p)
    else:
        raise(PluginLoadError("Cannot find the plugins directory '{}'."
            .format(os.path.abspath(plugins_path))))
Example #33
0
def owner() -> Optional[int]:  # pylint: disable=inconsistent-return-statements
    """Returns the SteamID64 of the owner. This is set in the config."""
    # noinspection PyBroadException
    try:
        owner_cvar = minqlx.get_cvar("qlx_owner")
        if owner_cvar is None:
            raise RuntimeError
        sid = int(owner_cvar)
        if sid == -1:
            raise RuntimeError
        return sid
    except:  # pylint: disable=bare-except
        logger = minqlx.get_logger()
        logger.error(
            "Failed to parse the Owner Steam ID. Make sure it's in SteamID64 format."
        )
    return None
Example #34
0
def load_preset_plugins():
    plugins = minqlx.Plugin.get_cvar("qlx_plugins", set)
    if "DEFAULT" in plugins:
        plugins.remove("DEFAULT")
        plugins.update(DEFAULT_PLUGINS)

    plugins_path = os.path.abspath(minqlx.get_cvar("qlx_pluginsPath"))
    plugins_dir = os.path.basename(plugins_path)

    if os.path.isdir(plugins_path):
        plugins = [p for p in plugins if "{}.{}".format(plugins_dir, p)]
        for p in plugins:
            load_plugin(p)
    else:
        raise (PluginLoadError(
            "Cannot find the plugins directory '{}'.".format(
                os.path.abspath(plugins_path))))
Example #35
0
    def __init__(self):
        self.add_hook("new_game", self.handle_new_game)
        self.add_hook("game_end", self.handle_game_end)
        self.add_hook("player_loaded", self.handle_player_loaded)
        self.add_hook("player_disconnect", self.handle_player_disconnect)
        self.add_hook("team_switch", self.handle_team_switch)
        self.add_hook("team_switch_attempt", self.handle_team_switch_attempt)
        self.add_hook("set_configstring", self.handle_configstring, priority=minqlx.PRI_HIGH)
        self.add_hook("client_command", self.handle_client_command)
        self.add_hook("vote_ended", self.handle_vote_ended)
        self.add_hook("console_print", self.handle_console_print)
        self.add_command(("q", "queue"), self.cmd_lq)
        self.add_command("afk", self.cmd_afk)
        self.add_command("here", self.cmd_playing)
        self.add_command("qversion", self.cmd_qversion)
        self.add_command(("teamsize", "ts"), self.cmd_teamsize, priority=minqlx.PRI_HIGH)

        # Commands for debugging
        self.add_command("qpush", self.cmd_qpush, 5)
        self.add_command("qadd", self.cmd_qadd, 5, usage="<id>")
        self.add_command("qupd", self.cmd_qupd, 5)

        self.version = "1.0"
        self._queue = []
        self._vip_queue = []
        self._afk = []
        self._tags = {}
        self.initialize()
        self.is_red_locked = False
        self.is_blue_locked = False
        self.is_push_pending = False
        self.is_endscreen = False  ######## TODO: replace for something better, because
        ######## loading during the endgame screen might cause bugs
        self.set_cvar_once("qlx_queueSetAfkPermission", "2")
        self.set_cvar_once("qlx_queueAFKTag", "^3AFK")
        self.game_port = minqlx.get_cvar("net_port")
        self.jointimes = {}
        self._extended_vip = self.check_if_extended_vip()

        self.test_logger = minqlx.get_logger()
        self.qlogger = self._configure_logger()
Example #36
0
def _configure_logger():
    logger = logging.getLogger("minqlx")
    logger.setLevel(logging.DEBUG)

    # File
    file_path = os.path.join(minqlx.get_cvar("fs_homepath"), "minqlx.log")
    maxlogs = minqlx.Plugin.get_cvar("qlx_logs", int)
    maxlogsize = minqlx.Plugin.get_cvar("qlx_logsSize", int)
    file_fmt = logging.Formatter("(%(asctime)s) [%(levelname)s @ %(name)s.%(funcName)s] %(message)s", "%H:%M:%S")
    file_handler = RotatingFileHandler(file_path, encoding="utf-8", maxBytes=maxlogsize, backupCount=maxlogs)
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(file_fmt)
    logger.addHandler(file_handler)
    logger.info("============================= minqlx run @ {} ============================="
        .format(datetime.datetime.now()))

    # Console
    console_fmt = logging.Formatter("[%(name)s.%(funcName)s] %(levelname)s: %(message)s", "%H:%M:%S")
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(console_fmt)
    logger.addHandler(console_handler)
 def setup_extended_logger() -> None:
     discord_logger: logging.Logger = logging.getLogger("discord")
     discord_logger.setLevel(logging.DEBUG)
     # File
     file_path = os.path.join(minqlx.get_cvar("fs_homepath"),
                              "minqlx_discord.log")
     maxlogs: int = minqlx.Plugin.get_cvar("qlx_logs", int)
     maxlogsize: int = minqlx.Plugin.get_cvar("qlx_logsSize", int)
     file_fmt: logging.Formatter = \
         logging.Formatter("(%(asctime)s) [%(levelname)s @ %(name)s.%(funcName)s] %(message)s", "%H:%M:%S")
     file_handler: logging.FileHandler = \
         RotatingFileHandler(file_path, encoding="utf-8", maxBytes=maxlogsize, backupCount=maxlogs)
     file_handler.setLevel(logging.DEBUG)
     file_handler.setFormatter(file_fmt)
     discord_logger.addHandler(file_handler)
     # Console
     console_fmt: logging.Formatter = \
         logging.Formatter("[%(name)s.%(funcName)s] %(levelname)s: %(message)s", "%H:%M:%S")
     console_handler: logging.Handler = logging.StreamHandler()
     console_handler.setLevel(logging.INFO)
     console_handler.setFormatter(console_fmt)
     discord_logger.addHandler(console_handler)
Example #38
0
    def __init__(self):
        self.add_hook("new_game", self.handle_new_game)
        # self.add_hook("game_end", self.handle_game_end)
        self.add_hook("player_loaded", self.handle_player_loaded)
        self.add_hook("player_disconnect", self.handle_player_disconnect)

        # Commands for debugging
        # self.add_command("qpush", self.cmd_qpush, 5)
        # self.add_command("qadd", self.cmd_qadd, 5, usage="<id>")
        # self.add_command("qupd", self.cmd_qupd, 5)

        self._queue = []
        self._vip_queue = []
        # self.initialize()
        self.is_endscreen = False  ######## TODO: replace for something better, because
        ######## loading during the endgame screen might cause bugs
        # self.set_cvar_once("qlx_queueSetAfkPermission", "2")
        # self.set_cvar_once("qlx_queueAFKTag", "^3AFK")
        self.game_port = minqlx.get_cvar("net_port")
        self.jointimes = {}
        self._extended_vip = self.checkIfExtendedVipEnabled()

        self.qlogger = self._configure_logger()
Example #39
0
    def __init__(self):
        self.add_hook("player_connect", self.handle_player_connect, priority=minqlx.PRI_LOWEST)
        self.add_hook("player_disconnect", self.handle_player_disconnect, priority=minqlx.PRI_LOWEST)
        self.add_hook("chat", self.handle_chat, priority=minqlx.PRI_LOWEST)
        self.add_hook("command", self.handle_command, priority=minqlx.PRI_LOWEST)

        self.set_cvar_once("qlx_chatlogs", "3")
        self.set_cvar_once("qlx_chatlogsSize", str(3*10**6)) # 3 MB

        self.chatlog = logging.Logger(__name__)
        file_dir = os.path.join(minqlx.get_cvar("fs_homepath"), "chatlogs")
        if not os.path.isdir(file_dir):
            os.makedirs(file_dir)

        file_path = os.path.join(file_dir, "chat.log")
        maxlogs = minqlx.Plugin.get_cvar("qlx_chatlogs", int)
        maxlogsize = minqlx.Plugin.get_cvar("qlx_chatlogsSize", int)
        file_fmt = logging.Formatter("[%(asctime)s] %(message)s", "%Y-%m-%d %H:%M:%S")
        file_handler = RotatingFileHandler(file_path, encoding="utf-8", maxBytes=maxlogsize, backupCount=maxlogs)
        file_handler.setFormatter(file_fmt)
        self.chatlog.addHandler(file_handler)
        self.chatlog.info("============================= Logger started @ {} ============================="
            .format(datetime.datetime.now()))
Example #40
0
def load_preset_plugins():
    plugins_temp = []
    for p in minqlx.Plugin.get_cvar("qlx_plugins", list):
        if p == "DEFAULT":
           plugins_temp += list(DEFAULT_PLUGINS)
        else:
           plugins_temp.append(p)

    plugins = []
    for p in plugins_temp:
        if p not in plugins:
           plugins.append(p)

    plugins_path = os.path.abspath(minqlx.get_cvar("qlx_pluginsPath"))
    plugins_dir = os.path.basename(plugins_path)

    if os.path.isdir(plugins_path):
        plugins = [p for p in plugins if "{}.{}".format(plugins_dir, p)]
        for p in plugins:
            load_plugin(p)
    else:
        raise(PluginLoadError("Cannot find the plugins directory '{}'."
            .format(os.path.abspath(plugins_path))))
Example #41
0
def load_plugin(plugin: str) -> None:
    logger = get_logger(None)
    logger.info("Loading plugin '%s'...", plugin)
    # noinspection PyProtectedMember
    plugins = minqlx.Plugin._loaded_plugins  # pylint: disable=protected-access
    plugins_path_cvar = minqlx.get_cvar("qlx_pluginsPath")
    if plugins_path_cvar is None:
        raise PluginLoadError("cvar qlx_pluginsPath misconfigured")

    plugins_path = os.path.abspath(plugins_path_cvar)
    plugins_dir = os.path.basename(plugins_path)

    if not os.path.isfile(os.path.join(plugins_path, plugin + ".py")):
        raise PluginLoadError("No such plugin exists.")
    if plugin in plugins:
        reload_plugin(plugin)
        return
    try:
        module = importlib.import_module(f"{plugins_dir}.{plugin}")
        # We add the module regardless of whether it fails or not, otherwise we can't reload later.
        _modules[plugin] = module

        if not hasattr(module, plugin):
            raise PluginLoadError(
                "The plugin needs to have a class with the exact name as the file, minus the .py."
            )

        plugin_class = getattr(module, plugin)
        if issubclass(plugin_class, minqlx.Plugin):
            plugins[plugin] = plugin_class()
        else:
            raise PluginLoadError(
                "Attempted to load a plugin that is not a subclass of 'minqlx.Plugin'."
            )
    except:
        log_exception(plugin)
        raise
Example #42
0
 def cmd_excessive_weaps(self, player, msg, channel):
     if len(msg) < 2:
         return minqlx.RET_USAGE
     
     if msg[1] == "on":
         minqlx.set_cvar("weapon_reload_sg", "200")
         minqlx.set_cvar("weapon_reload_rl", "200")
         minqlx.set_cvar("weapon_reload_rg", "50")
         minqlx.set_cvar("weapon_reload_prox", "200")
         minqlx.set_cvar("weapon_reload_pg", "40")
         minqlx.set_cvar("weapon_reload_ng", "800")
         minqlx.set_cvar("weapon_reload_mg", "40")
         minqlx.set_cvar("weapon_reload_hmg", "40")
         minqlx.set_cvar("weapon_reload_gl", "200")
         minqlx.set_cvar("weapon_reload_gauntlet", "100")
         minqlx.set_cvar("weapon_reload_cg", "30")
         minqlx.set_cvar("weapon_reload_bfg", "75")
         minqlx.set_cvar("qlx_excessive", "1")
         self.msg("Excessive weapons are enabled.")
     if msg[1] == "off":
         minqlx.console_command("reset weapon_reload_sg")
         minqlx.console_command("reset weapon_reload_rl")
         if (minqlx.get_cvar("pmove_airControl")) == "1":
             minqlx.set_cvar("weapon_reload_rg", "1200")
         else:
             minqlx.console_command("reset weapon_reload_rg")
         minqlx.console_command("reset weapon_reload_prox")
         minqlx.console_command("reset weapon_reload_pg")
         minqlx.console_command("reset weapon_reload_ng")
         minqlx.console_command("reset weapon_reload_mg")
         minqlx.console_command("reset weapon_reload_hmg")
         minqlx.console_command("reset weapon_reload_gl")
         minqlx.console_command("reset weapon_reload_gauntlet")
         minqlx.console_command("reset weapon_reload_cg")
         minqlx.console_command("reset weapon_reload_bfg")
         minqlx.set_cvar("qlx_excessive", "0")
         self.msg("Excessive weapons are disabled.")
Example #43
0
    def cmd_excessive_weaps(self, player, msg, channel):
        if len(msg) < 2:
            return minqlx.RET_USAGE

        if msg[1] == "on":
            minqlx.set_cvar("weapon_reload_sg", "200")
            minqlx.set_cvar("weapon_reload_rl", "200")
            minqlx.set_cvar("weapon_reload_rg", "50")
            minqlx.set_cvar("weapon_reload_prox", "200")
            minqlx.set_cvar("weapon_reload_pg", "40")
            minqlx.set_cvar("weapon_reload_ng", "800")
            minqlx.set_cvar("weapon_reload_mg", "40")
            minqlx.set_cvar("weapon_reload_hmg", "40")
            minqlx.set_cvar("weapon_reload_gl", "200")
            minqlx.set_cvar("weapon_reload_gauntlet", "100")
            minqlx.set_cvar("weapon_reload_cg", "30")
            minqlx.set_cvar("weapon_reload_bfg", "75")
            minqlx.set_cvar("qlx_excessive", "1")
            self.msg("Excessive weapons are enabled.")
        if msg[1] == "off":
            minqlx.console_command("reset weapon_reload_sg")
            minqlx.console_command("reset weapon_reload_rl")
            if (minqlx.get_cvar("pmove_airControl")) == "1":
                minqlx.set_cvar("weapon_reload_rg", "1200")
            else:
                minqlx.console_command("reset weapon_reload_rg")
            minqlx.console_command("reset weapon_reload_prox")
            minqlx.console_command("reset weapon_reload_pg")
            minqlx.console_command("reset weapon_reload_ng")
            minqlx.console_command("reset weapon_reload_mg")
            minqlx.console_command("reset weapon_reload_hmg")
            minqlx.console_command("reset weapon_reload_gl")
            minqlx.console_command("reset weapon_reload_gauntlet")
            minqlx.console_command("reset weapon_reload_cg")
            minqlx.console_command("reset weapon_reload_bfg")
            minqlx.set_cvar("qlx_excessive", "0")
            self.msg("Excessive weapons are disabled.")
Example #44
0
    def handle_vote_called(self, caller, vote, args):
        if not (self.get_cvar("g_allowSpecVote", bool)) and caller.team == "spectator":
            if caller.privileges == None:
                caller.tell("You are not allowed to call a vote as spectator.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "infiniteammo":
            # enables the '/cv infiniteammo [on/off]' command
            if args.lower() == "off":
                self.callvote("set g_infiniteAmmo 0", "infinite ammo: off")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            elif args.lower() == "on":
                self.callvote("set g_infiniteAmmo 1", "infinite ammo: on")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv infiniteammo [on/off]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "freecam":
            # enables the '/cv freecam [on/off]' command
            if args.lower() == "off":
                self.callvote("set g_teamSpecFreeCam 0", "team spectator free-cam: off")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            elif args.lower() == "on":
                self.callvote("set g_teamSpecFreeCam 1", "team spectator free-cam: on")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv freecam [on/off]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "floordamage":
            # enables the '/cv floordamage [on/off]' command
            if args.lower() == "off":
                self.callvote("set g_forceDmgThroughSurface 0", "damage through floor: off")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            elif args.lower() == "on":
                self.callvote("set g_forceDmgThroughSurface 1", "damage through floor: on")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv floordamage [on/off]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "alltalk":
            # enables the '/cv alltalk [on/off]' command
            if args.lower() == "off":
                self.callvote("set g_allTalk 0", "voice comm between teams: off")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            elif args.lower() == "on":
                self.callvote("set g_allTalk 1", "voice comm between teams: on")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv alltalk [on/off]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "allready":
            # enables the '/cv allready' command
            if self.game.state == "warmup":
                self.callvote("allready", "begin game immediately")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("You can't vote to begin the game when the game is already on.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "ruleset":
            # enables the '/cv ruleset [pql/vql]' command
            if (minqlx.get_cvar("qlx_rulesetLocked")) == "1":
                caller.tell("Voting to change the ruleset is disabled on ruleset-locked servers.")
                return minqlx.RET_STOP_ALL

            if args.lower() == "pql":
                self.callvote("qlx !ruleset pql", "ruleset: pql")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            elif args.lower() == "vql":
                self.callvote("qlx !ruleset vql", "ruleset: vql")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv ruleset [pql/vql]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL
            
        if vote.lower() == "abort":
            # enables the '/cv abort' command
            if self.game.state != "warmup":
                self.callvote("abort", "abort the game", 30)
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("You can't vote to abort the game when the game isn't in progress.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "chatsounds":
            # enables the '/cv chatsounds [on/off]' command
            if args.lower() == "off":
                self.callvote("qlx !unload fun", "chat-activated sounds: off")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            elif args.lower() == "on":
                self.callvote("qlx !load fun", "chat-activated sounds: on")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv chatsounds [on/off]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() in ("silence", "mute"):
            # enables the '/cv silence <id>' command
            try:
                player_name = self.player(int(args)).clean_name
                player_id = self.player(int(args)).id
            except:
                caller.tell("^1Invalid ID.^7 Use a client ID from the ^2/players^7 command.")
                return minqlx.RET_STOP_ALL

            if self.get_cvar("qlx_serverExemptFromModeration") == "1":
                caller.tell("This server has the serverExemptFromModeration flag set, and therefore, silencing is disabled.")
                return minqlx.RET_STOP_ALL
            
            self.callvote("qlx !silence {} 10 minutes You were call-voted silent for 10 minutes.; mute {}".format(player_id, player_id), "silence {} for 10 minutes".format(player_name))
            self.msg("{}^7 called a vote.".format(caller.name))
            return minqlx.RET_STOP_ALL

        if vote.lower() == "tempban":
            # enables the '/cv tempban <id>' command
            if self.get_cvar("qlx_disablePlayerRemoval", bool):
                # if player removal cvar is set, do not permit '/cv tempban'
                if caller.privileges == None:
                    caller.tell("Voting to tempban is disabled in this server.")
                    caller.tell("^2/cv spec <id>^7 and ^2/cv silence <id>^7 exist as substitutes to kicking/tempbanning.")
                    return minqlx.RET_STOP_ALL
            try:
                player_name = self.player(int(args)).clean_name
                player_id = self.player(int(args)).id
            except:
                caller.tell("^1Invalid ID.^7 Use a client ID from the ^2/players^7 command.")
                return minqlx.RET_STOP_ALL

            if self.player(int(args)).privileges != None:
                caller.tell("The player specified is an admin, a mod or banned, and cannot be tempbanned.")
                return minqlx.RET_STOP_ALL
            
            self.callvote("tempban {}".format(player_id), "^1ban {} until the map changes^3".format(player_name))
            self.msg("{}^7 called a vote.".format(caller.name))
            return minqlx.RET_STOP_ALL

        if vote.lower() == "spec":
            # enables the '/cv spec <id>' command
            try:
                player_name = self.player(int(args)).clean_name
                player_id = self.player(int(args)).id
            except:
                caller.tell("^1Invalid ID.^7 Use a client ID from the ^2/players^7 command.")
                return minqlx.RET_STOP_ALL

            if self.player(int(args)).team == "spectator":
                caller.tell("That player is already in the spectators.")
                return minqlx.RET_STOP_ALL
            
            self.callvote("put {} spec".format(player_id), "move {} to the spectators".format(player_name))
            self.msg("{}^7 called a vote.".format(caller.name))
            return minqlx.RET_STOP_ALL

        if vote.lower() == "excessive":
            # enables the '/cv excessive [on/off]' command
            if args.lower() == "off":
                self.callvote("qlx !excessiveweaps off", "excessive weapons: off")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            elif args.lower() == "on":
                self.callvote("qlx !excessiveweaps on", "excessive weapons: on")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv excessive [on/off]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() in ("kick", "clientkick"):
            # if player removal cvar is set, do not permit '/cv kick' or '/cv clientkick'
            if self.get_cvar("qlx_disablePlayerRemoval", bool):
                if caller.privileges == None:
                    caller.tell("Voting to kick/clientkick is disabled in this server.")
                    caller.tell("^2/cv spec <id>^7 and ^2/cv silence <id>^7 exist as substitutes to kicking.")
                    return minqlx.RET_STOP_ALL

        if vote.lower() == "lock":
            # enables the '/cv lock <team>' command
            if len(args) <= 1:
                self.callvote("lock", "lock all teams")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                if args.lower() == "blue":
                    self.callvote("lock blue", "lock the ^4blue^3 team")
                    self.msg("{}^7 called a vote.".format(caller.name))
                    return minqlx.RET_STOP_ALL
                elif args.lower() == "red":
                    self.callvote("lock red", "lock the ^1red^3 team")
                    self.msg("{}^7 called a vote.".format(caller.name))
                    return minqlx.RET_STOP_ALL
                else:
                    caller.tell("^2/cv lock^7 or ^2/cv lock <blue/red>^7 is the usage for this callvote command.")
                    return minqlx.RET_STOP_ALL

        if vote.lower() == "unlock":
            # enables the '/cv unlock <team>' command
            if len(args) <= 1:
                self.callvote("unlock", "unlock all teams")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                if args.lower() == "blue":
                    self.callvote("unlock blue", "unlock the ^4blue^3 team")
                    self.msg("{}^7 called a vote.".format(caller.name))
                    return minqlx.RET_STOP_ALL
                elif args.lower() == "red":
                    self.callvote("unlock red", "unlock the ^1red^3 team")
                    self.msg("{}^7 called a vote.".format(caller.name))
                    return minqlx.RET_STOP_ALL
                else:
                    caller.tell("^2/cv unlock^7 or ^2/cv unlock <blue/red>^7 is the usage for this callvote command.")
                    return minqlx.RET_STOP_ALL

        if vote.lower() == "balancing":
            # enables the '/cv balancing on/off' command
            if args.lower() == "off":
                self.callvote("qlx !unload balance", "glicko-based team balancing: off")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            elif args.lower() == "on":
                self.callvote("qlx !load balance", "glicko-based team balancing: on")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv balancing [on/off]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "roundtimelimit":
            # enables the '/cv roundtimelimit [90/120/180]' command
            if args.lower() == "180":
                self.callvote("set roundtimelimit 180", "round time limit: 180")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            if args.lower() == "120":
                self.callvote("set roundtimelimit 120", "round time limit: 120")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            if args.lower() == "90":
                self.callvote("set roundtimelimit 90", "round time limit: 90")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv roundtimelimit [90/120/180]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "balance":
            # enables the '/cv balance' command
            self.callvote("qlx !balance", "balance the teams")
            self.msg("{}^7 called a vote.".format(caller.name))
            return minqlx.RET_STOP_ALL

        if vote.lower() == "lgdamage":
            # enables the '/cv lgdamage [6/7]' command
            if args.lower() == "6":
                self.callvote("set g_damage_lg 6; set g_knockback_lg 1.75", "^7Lightning gun^3 damage: 6")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            if args.lower() == "7":
                self.callvote("set g_damage_lg 7; set g_knockback_lg 1.50", "^7Lightning gun^3 damage: 7 (with appropriate knockback)")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv lgdamage [6/7]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "lgdamage":
            # enables the '/cv lgdamage [6/7]' command
            if args.lower() == "6":
                self.callvote("set g_damage_lg 6; set g_knockback_lg 1.75", "^7Lightning gun^3 damage: 6")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            if args.lower() == "7":
                self.callvote("set g_damage_lg 7; set g_knockback_lg 1.50", "^7Lightning gun^3 damage: 7 (with appropriate knockback)")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv lgdamage [6/7]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "rgdamage":
            # enables the '/cv rgdamage [80/100]' command
            if args.lower() == "80":
                self.callvote("set g_damage_rg 80", "^2Railgun^3 damage: 80")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            if args.lower() == "100":
                self.callvote("set g_damage_rg 100", "^2Railgun^3 damage: 100")
                self.msg("{}^7 called a vote.".format(caller.name))
                return minqlx.RET_STOP_ALL
            else:
                caller.tell("^2/cv rgdamage [80/100]^7 is the usage for this callvote command.")
                return minqlx.RET_STOP_ALL

        if vote.lower() == "cvar":
            if not self.get_cvar("qlx_disableCvarVoting", bool):
                if not len(args) <= 1:
                    # enables the '/cv cvar <variable> <value>' command
                    if self.db.has_permission(caller.steam_id, self.get_cvar("qlx_cvarVotePermissionRequired", int)):
                        self.callvote("set {}".format(args), "Server CVAR change: {}^3".format(args))
                        self.msg("{}^7 called a server vote.".format(caller.name))
                        return minqlx.RET_STOP_ALL
                    else:
                        caller.tell("^1Insufficient privileges to change a server cvar.^7 Permission Level required: ^43^7.")
                        return minqlx.RET_STOP_ALL
                else:
                    caller.tell("^2/cv cvar <variable> <value>^7 is the usage for this callvote command.")
                    return minqlx.RET_STOP_ALL
            else:
                caller.tell("Voting to change server CVARs is disabled on this server.")
                return minqlx.RET_STOP_ALL
Example #45
0
 def process_frame(self):
     self.frame_counter += 1
     if self.frame_counter == (int(self.get_cvar("qlx_pingSpecSecondsBetweenChecks")) * int(minqlx.get_cvar("sv_fps"))):
         self.frame_counter = 0
         self.check_ping()
Example #46
0
 def tags(self):
     return minqlx.get_cvar("sv_tags").split(",")
Example #47
0
 def tags(self):
     return minqlx.get_cvar("sv_tags").split(",")
Example #48
0
def set_cvar_once(name, value, flags=0):
    if minqlx.get_cvar(name) is None:
        minqlx.set_cvar(name, value, flags)
        return True

    return False
Example #49
0
def set_cvar_limit_once(name, value, minimum, maximum, flags=0):
    if minqlx.get_cvar(name) is None:
        minqlx.set_cvar_limit(name, value, minimum, maximum, flags)
        return True

    return False