From a2738c62d5208037c2e7f5eb8d3730cdb097d91f Mon Sep 17 00:00:00 2001 From: cclecle Date: Sat, 23 Sep 2023 12:08:53 +0100 Subject: [PATCH] feat: create official module (CI-CD) chore: get src from ut99 & ut2k4 & cod4 work chore: port everything to pysimpleini --- .project | 2 +- .settings/org.eclipse.core.resources.prefs | 1 + pyproject.toml | 3 +- src/pygamecfg/__init__.py | 22 +- src/pygamecfg/__main__.py | 61 ++ src/pygamecfg/cod4.py | 101 +++ src/pygamecfg/core.py | 97 +++ src/pygamecfg/test_module.py | 43 -- src/pygamecfg/ut2k4.py | 737 +++++++++++++++++++++ src/pygamecfg/ut99.py | 569 ++++++++++++++++ test/{test_test_module.py => test_ut99.py} | 0 11 files changed, 1587 insertions(+), 49 deletions(-) create mode 100644 src/pygamecfg/__main__.py create mode 100644 src/pygamecfg/cod4.py create mode 100644 src/pygamecfg/core.py delete mode 100644 src/pygamecfg/test_module.py create mode 100644 src/pygamecfg/ut2k4.py create mode 100644 src/pygamecfg/ut99.py rename test/{test_test_module.py => test_ut99.py} (100%) diff --git a/.project b/.project index dc36649..6318cd7 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - {{project_name}} + pygamecfg diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index 99f26c0..f4a6dfe 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -1,2 +1,3 @@ eclipse.preferences.version=1 +encoding//src/pygamecfg/__main__.py=utf-8 encoding/=UTF-8 diff --git a/pyproject.toml b/pyproject.toml index 9922647..87d9661 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,8 @@ classifiers = [ ] dependencies = [ 'importlib-metadata; python_version<"3.9"', - 'packaging' + 'packaging', + 'pysimpleini>=0.3.1' ] dynamic = ["version"] diff --git a/src/pygamecfg/__init__.py b/src/pygamecfg/__init__.py index 8b360f0..e1661c2 100644 --- a/src/pygamecfg/__init__.py +++ b/src/pygamecfg/__init__.py @@ -10,13 +10,27 @@ Main module __init__ file. """ -from importlib.metadata import version, PackageNotFoundError +from importlib.metadata import distribution, version, PackageNotFoundError +import warnings + +from .core import GameOptions_Factory try: # pragma: no cover __version__ = version("pygamecfg") -except PackageNotFoundError: # pragma: no cover - import warnings +except PackageNotFoundError: # pragma: no cover warnings.warn("can not read __version__, assuming local test context, setting it to ?.?.?") __version__ = "?.?.?" -from .test_module import test_function +try: # pragma: no cover + dist = distribution("pygamecfg") + __Summuary__ = dist.metadata["Summary"] +except PackageNotFoundError: # pragma: no cover + warnings.warn('can not read dist.metadata["Summary"], assuming local test context, setting it to ') + __Summuary__ = "pygamecfg description" + +try: # pragma: no cover + dist = distribution("pygamecfg") + __Name__ = dist.metadata["Name"] +except PackageNotFoundError: # pragma: no cover + warnings.warn('can not read dist.metadata["Name"], assuming local test context, setting it to ') + __Name__ = "pygamecfg" diff --git a/src/pygamecfg/__main__.py b/src/pygamecfg/__main__.py new file mode 100644 index 0000000..60680b5 --- /dev/null +++ b/src/pygamecfg/__main__.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# PySimpleINI (c) by chacha +# +# PySimpleINI is licensed under a +# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Unported License. +# +# You should have received a copy of the license along with this +# work. If not, see . + +"""CLI interface module""" +from __future__ import annotations + +from argparse import ArgumentParser + +from . import __Summuary__, __Name__ +from . import GameOptions_Factory + +parser = ArgumentParser(description=__Summuary__) +parser.add_argument("-v", "--verbosity", action="count", default=0, help="increase output verbosity") + +parser.add_argument("-b", "--basegamedir", help="set the game base dir", default="./") +parser.add_argument("-c", "--configfile", help="set the default config file", default="") +parser.add_argument("-g", "--game", help="the target game", choices=["ut99", "cod4"]) +subparsers = parser.add_subparsers(dest="command", help="command type", required=True) + +SetOption_subparser = subparsers.add_parser("SetOption", help="Set/Add a game config file option value (may need reboot)") +SetOption_subparser.add_argument("option") +SetOption_subparser.add_argument("value", nargs="?") + + +RemOption_subparser = subparsers.add_parser("RemOption", help="Remove a game config file option, w or w/o value (may need reboot)") +RemOption_subparser.add_argument("option") +RemOption_subparser.add_argument("value", nargs="?") + +GetOption_subparser = subparsers.add_parser("GetOption", help="Get a game config file option value") +GetOption_subparser.add_argument("option") + +args = parser.parse_args() + +if args.verbosity: + print("Using base game dir: {0}".format(args.basegamedir)) + print("Using config file: {0}".format(args.configfile)) + +if args.configfile == "": + if args.game == "ut99": + args.configfile = "./System/UnrealTournament.ini" + elif args.game == "cod4": + args.configfile = "./main/server.cfg" +_GameOptions = GameOptions_Factory(args.game, args.basegamedir, args.configfile) + + +if args.command == "SetOption": + _GameOptions.set(args.option, args.value) +elif args.command == "RemOption": + _GameOptions.rem(args.option, args.value) +elif args.command == "GetOption": + res = _GameOptions.get(args.option) + print(res) +else: + raise RuntimeError("Invalid argument") diff --git a/src/pygamecfg/cod4.py b/src/pygamecfg/cod4.py new file mode 100644 index 0000000..447b88c --- /dev/null +++ b/src/pygamecfg/cod4.py @@ -0,0 +1,101 @@ +from __future__ import annotations +from typing import Union + +import re +from os.path import join + +from .core import GameOptions_Factory_Register, GameOption, OptionType + + +class GameOption_COD4(GameOption): + szGameType = "cod4" + TValueType = OptionType.OT_INVALID + + szOptionName: str = "" + szKeyName: str = "" + bDblQuoted: bool = False + szPrefix = "" + + def __init__(self, GameRootDir: str, ConfigFileRelPath: str) -> None: + super().__init__(GameRootDir, ConfigFileRelPath) + self.mainConfigFilePath = join(GameRootDir, ConfigFileRelPath) + self.cfgfile = open(self.mainConfigFilePath, "r") + + def set(self, value: str) -> None: + if not self.szOptionName: + raise RuntimeError("szOptionName is not set") + self.format(value) + if self.bDblQuoted: + value = '"' + value + '"' + if self.szPrefix: + value = self.szPrefix + " " + self.szKeyName + " " + value + + for line in self.cfgfile.readlines(): + if re.search(r"\s+" + self.szPrefix + r"\s+"): + print(f"found: {line}") + + def rem(self, value: Union[str, None] = None) -> None: + if not self.szOptionName: + raise RuntimeError("szOptionName is not set") + + regex = r"^\s*" + self.szPrefix + r"\s+" + self.szOptionName + r"\s*(?P.*)" + + bfound = False + + newFile = "" + for line in self.cfgfile.readlines(): + if re.search(regex, line): + if bfound: + print("[warning] Option defined multiple time") + bfound = True + else: + newFile += line + if not bfound: + raise RuntimeError("Option not found in file") + + self.cfgfile.close() + with open(self.mainConfigFilePath, "w") as ofile: + ofile.write(newFile) + self.cfgfile = open(self.mainConfigFilePath, "r") + + def get(self) -> Union[None, str]: + if not self.szOptionName: + raise RuntimeError("szOptionName not set") + + if self.bDblQuoted: + regex = r"^\s*" + self.szPrefix + r"\s+" + self.szOptionName + r"\s*\"(?P.*)\"" + else: + regex = r"^\s*" + self.szPrefix + r"\s+" + self.szOptionName + r"\s*(?P.*)" + bfound = False + res = None + for line in self.cfgfile.readlines(): + if result := re.search(regex, line): + if bfound: + raise RuntimeError("Option defined multiple time") + res = result.groupdict()["value"] + bfound = True + if not bfound: + raise RuntimeError("Option not found in file") + return res + + +@GameOptions_Factory_Register +class GameOption_COD4_sv_maxclients(GameOption_COD4): + TValueType = OptionType.OT_INTEGER + szOptionName: str = "sv_maxclients" + szKeyName: str = "sv_maxclients" + bDblQuoted: bool = True + szPrefix = "set" + szDefaultValue = "12" + szHelp = "Maximum client number" + + +@GameOptions_Factory_Register +class GameOption_COD4_sv_mapRotation(GameOption_COD4): + TValueType = OptionType.OT_STRING + szOptionName: str = "sv_mapRotation" + szKeyName: str = "sv_mapRotation" + bDblQuoted: bool = True + szPrefix = "set" + szDefaultValue = "gametype dm map mp_block" + szHelp = "Map rotation list" diff --git a/src/pygamecfg/core.py b/src/pygamecfg/core.py new file mode 100644 index 0000000..9c6d921 --- /dev/null +++ b/src/pygamecfg/core.py @@ -0,0 +1,97 @@ +from __future__ import annotations +from typing import Union + +from abc import ABCMeta, abstractmethod +from enum import Enum + + +class OptionType(Enum): + OT_INVALID = 0 + OT_STRING = 1 + OT_INTEGER = 2 + OT_BOOLEAN = 3 + OT_FLOAT = 4 + + +class GameOption(metaclass=ABCMeta): + szGameType: str = "" + szOptionName: str = "" + TValueType: OptionType = OptionType.OT_INVALID + szDefaultValue: str = "" + szHelp: str = "" + szFormatedValue: str = "" + + @abstractmethod + def __init__(self, GameRootDir: str, ConfigFileRelPath: Union[None, str] = None): + self.GameRootDir = GameRootDir + self.ConfigFileRelPath = ConfigFileRelPath + + def format(self, value: Union[int, str, float]) -> None: + if self.TValueType == OptionType.OT_STRING: + self.szFormatedValue = str(value) + elif self.TValueType == OptionType.OT_INTEGER: + self.szFormatedValue = str(int(value)) + elif self.TValueType == OptionType.OT_BOOLEAN: + try: + intval = int(value) + self.szFormatedValue = str(bool(intval)) + except: + self.szFormatedValue = str(bool(1)) if value.lower() == "true" else str(bool(0)) + elif self.TValueType == OptionType.OT_FLOAT: + self.szFormatedValue = str(float(value)) + else: + raise RuntimeError("Invalid Option TValueType") + + print("setting option <{0}> to: {1}".format(self.szOptionName, self.szFormatedValue)) + + @abstractmethod + def set(self, value: str) -> None: + raise NotImplementedError("method not implemented") + + @abstractmethod + def rem(self, value: Union[None, str]) -> None: + raise NotImplementedError("method not implemented") + + @abstractmethod + def get(self) -> Union[None, str]: + raise NotImplementedError("method not implemented") + + +class GameOptions_Factory: + ar_Options_cls = [] + + def __init__(self, szGameType: str, GameRootDir: str, ConfigFileRelPath: Union[None, str] = None) -> None: + self.szGameType = szGameType + self.ar_Options = [] + for Options_cls in self.ar_Options_cls: + if Options_cls.szGameType == szGameType: + self.ar_Options.append(Options_cls(GameRootDir, ConfigFileRelPath)) + + @classmethod + def GameOptionRegister(cls, Option: GameOption) -> None: + cls.ar_Options_cls.append(Option) + + def set(self, OptionName: str, value: str) -> None: + for _option in self.ar_Options: + if _option.szOptionName == OptionName: + _option.set(value) + return + raise RuntimeError("Option not found") + + def rem(self, OptionName: str, value: Union[None, str]) -> None: + for _option in self.ar_Options: + if _option.szOptionName == OptionName: + _option.rem(value) + return + raise RuntimeError("Option not found") + + def get(self, OptionName: str) -> Union[None, str]: + for _option in self.ar_Options: + if _option.szOptionName == OptionName: + return _option.get() + raise RuntimeError("Option not found") + + +def GameOptions_Factory_Register(cls): + GameOptions_Factory.GameOptionRegister(cls) + return cls diff --git a/src/pygamecfg/test_module.py b/src/pygamecfg/test_module.py deleted file mode 100644 index 13bb94b..0000000 --- a/src/pygamecfg/test_module.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# pygamecfg (c) by chacha -# -# pygamecfg is licensed under a -# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Unported License. -# -# You should have received a copy of the license along with this -# work. If not, see . - -"""Phasellus tellus lectus, volutpat eu dapibus ut, suscipit vel augue. - -Tips: - Aliquam non leo vel libero sagittis viverra. Quisque lobortis nunc sit amet augue euismod laoreet. -Note: - Maecenas volutpat porttitor pretium. Aliquam suscipit quis nisi non imperdiet. -Note: - Vivamus et efficitur lorem, eget imperdiet tortor. Integer vel interdum sem. -""" - -from __future__ import annotations -from typing import TYPE_CHECKING - -if TYPE_CHECKING: # Only imports the below statements during type checking - pass - -def test_function(testvar: int) -> int: - """ A test function that return testvar+1 and print "Hello world !" - - Proin eget sapien eget ipsum efficitur mollis nec ac nibh. - - Note: - Morbi id lectus maximus, condimentum nunc eget, porta felis. In tristique velit tortor. - - Args: - testvar: any integer - - Returns: - testvar+1 - """ - print("Hello world !") - return testvar+1 diff --git a/src/pygamecfg/ut2k4.py b/src/pygamecfg/ut2k4.py new file mode 100644 index 0000000..296a09f --- /dev/null +++ b/src/pygamecfg/ut2k4.py @@ -0,0 +1,737 @@ +from __future__ import annotations +from typing import Union + +from pysimpleini import PySimpleINI, KeyNotFoundError, SectionNotFoundError, Section, Key +from os.path import join + +from .core import GameOptions_Factory_Register, GameOption, OptionType + + +class ChaChaSimpleINI_UT2k4(PySimpleINI): + def GroupKeysInSection(self, szSectionName: str, szKeyName: str) -> None: + try: + _section = self.getSection(szSectionName) + if type(_section) is Section: + ar_ServerPackages = _section.getKey(szKeyName) + if isinstance(ar_ServerPackages, Key): + ar_ServerPackages = [ar_ServerPackages] + else: # array + pass + for ServerPackages in ar_ServerPackages: + _section.delKey(ServerPackages.name, None, ServerPackages.value) + for ServerPackages in ar_ServerPackages: + _section.setAddKeyValue(ServerPackages.name, ServerPackages.value, True) + except SectionNotFoundError: + pass + + def writeFile(self, bBeautify: bool = False) -> None: + self.GroupKeysInSection("Engine.GameEngine", "ServerPackages") + self.GroupKeysInSection("Engine.GameEngine", "ServerActors") + self.GroupKeysInSection("Core.System", "Suppress") + self.GroupKeysInSection("Core.System", "Paths") + self.GroupKeysInSection("Editor.EditorEngine", "EditPackages") + self.GroupKeysInSection("Editor.EditorEngine", "CutdownPackages") + super().writeFile(bBeautify) + + +class GameOption_UT2k4(GameOption): + szGameType = "ut2k4" + TValueType = OptionType.OT_INVALID + + szOptionName = "" + szSectionName = "" + szKeyName = "" + bForceAdd: bool = False + bRemovable: bool = False + + def __init__(self, GameRootDir: str, ConfigFileRelPath: Union[None, str]) -> None: + super().__init__(GameRootDir, ConfigFileRelPath) + self.mainConfigFilePath = join(GameRootDir, ConfigFileRelPath) + + def set(self, value: str) -> None: + if not self.szOptionName: + raise RuntimeError("szOptionName is not set") + self.format(value) + self.inifile = ChaChaSimpleINI_UT2k4(self.mainConfigFilePath) + self.inifile.setAddKeyValue(self.szSectionName, self.szKeyName, self.szFormatedValue, self.bForceAdd) + self.inifile.writeFile() + + def rem(self, value: Union[None, str]) -> None: + if not self.szOptionName: + raise RuntimeError("szOptionName is not set") + if not self.bRemovable: + raise RuntimeError("this options is not removable") + self.inifile.delKeyEx(self.szSectionName, self.szKeyName, None, value) + self.inifile.writeFile() + + def get(self) -> Union[None, str]: + if not self.szOptionName: + raise RuntimeError("szOptionName not set") + print("get option <{0}>".format(self.szOptionName)) + res = self.inifile.getKeyValue(self.szSectionName, self.szKeyName) + return res + + +class GameOption_UT2k4_GenAdd(GameOption_UT2k4): + bForceAdd = True + + def rem(self, value: Union[None, str]) -> None: + try: + super().rem(value) + except KeyNotFoundError: + pass + + def set(self, value: str) -> None: + # force to call the rem method of THIS level + GameOption_UT2k4_GenAdd.rem(self, value) + super().set(value) + + +class GameOption_UT2k4_GenAdd__Engine(GameOption_UT2k4_GenAdd): + szSectionName = "Engine.GameEngine" + TValueType = OptionType.OT_STRING + bRemovable = True + + +@GameOptions_Factory_Register +class GameOption_UT2k4_ServerPackages(GameOption_UT2k4_GenAdd__Engine): + szOptionName = "ServerPackages" + szKeyName = "ServerPackages" + szHelp = "Add a ServerPackages record" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_ServerActors(GameOption_UT2k4_GenAdd__Engine): + szOptionName = "ServerActors" + szKeyName = "ServerActors" + szHelp = "Add a ServerActors record" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MasterServerList(GameOption_UT2k4_GenAdd): + szSectionName = "IpDrv.MasterServerLink" + TValueType = OptionType.OT_STRING + bRemovable = True + szOptionName = "MasterServerList" + szKeyName = "MasterServerList" + szHelp = "Add a MasterServerList record" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_Port(GameOption_UT2k4): + szOptionName = "Port" + szSectionName = "URL" + szKeyName = "Port" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "7777" + szHelp = "Server Listening port" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_Map(GameOption_UT2k4): + szOptionName = "Map" + szSectionName = "URL" + szKeyName = "Map" + TValueType = OptionType.OT_STRING + szDefaultValue = "dm-deck16][" + szHelp = "Server Map" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_GameType(GameOption_UT2k4): + szOptionName = "GameType" + szSectionName = "Engine.Engine" + szKeyName = "DefaultServerGame" + TValueType = OptionType.OT_STRING + szDefaultValue = "XGame.XDeathmatch" + szHelp = "Server Gametype" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_HostName(GameOption_UT2k4): + szOptionName = "HostName" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "ServerName" + TValueType = OptionType.OT_STRING + szDefaultValue = "ChaCha Test Server" + szHelp = "Server's HostName" + + def set(self, value: str): + super().set(value) + self.inifile.setAddKeyValue("Engine.GameReplicationInfo", "ShortName", value) + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MOTD(GameOption_UT2k4): + szOptionName = "MOTD" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "MessageOfTheDay" + TValueType = OptionType.OT_STRING + szDefaultValue = "Welcome to ChaCha's server" + szHelp = "Message of the day" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_AdminEmail(GameOption_UT2k4): + szOptionName = "AdminEmail" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "AdminEmail" + TValueType = OptionType.OT_STRING + szDefaultValue = "chachacorp@protonmail.com" + szHelp = "Admin mail" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_AdminName(GameOption_UT2k4): + szOptionName = "AdminName" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "AdminName" + TValueType = OptionType.OT_STRING + szDefaultValue = "chacha" + szHelp = "Admin name" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_HTTPDownloadServer(GameOption_UT2k4): + szOptionName = "HTTPDownloadServer" + szSectionName = "IpDrv.HTTPDownload" + szKeyName = "RedirectToURL" + TValueType = OptionType.OT_STRING + szDefaultValue = "http://chacha.ddns.net/games/ut2k4" + szHelp = "FastDL url" + + def set(self, value: str): + super().set(value) + self.inifile.setAddKeyValue("IpDrv.HTTPDownload", "UseCompression", "True") + self.inifile.delKey("IpDrv.HTTPDownload", "ProxyServerHost") + self.inifile.delKey("IpDrv.HTTPDownload", "ProxyServerPort") + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MaxClientRate(GameOption_UT2k4): + szOptionName = "MaxClientRate" + szSectionName = "IpDrv.TcpNetDriver" + szKeyName = "MaxClientRate" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "25000" + szHelp = "Max Client Rate" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MaxInternetClientRate(GameOption_UT2k4): + szOptionName = "MaxInternetClientRate" + szSectionName = "IpDrv.TcpNetDriver" + szKeyName = "MaxClientRate" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "25000" + szHelp = "Max Client Rate" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_NetServerMaxTickRate(GameOption_UT2k4): + szOptionName = "NetServerMaxTickRate" + szSectionName = "IpDrv.TcpNetDriver" + szKeyName = "NetServerMaxTickRate" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "60" + szHelp = "Server Max TickRate" + + def set(self, value: str): + super().set(value) + self.inifile.setAddKeyValue("Engine.DemoRecDrive", "NetServerMaxTickRate", value) + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT2k4_LanServerMaxTickRate(GameOption_UT2k4): + szOptionName = "LanServerMaxTickRate" + szSectionName = "IpDrv.TcpNetDriver" + szKeyName = "LanServerMaxTickRate" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "60" + szHelp = "Lan Server Max TickRate" + + def set(self, value: str): + super().set(value) + self.inifile.setAddKeyValue("Engine.DemoRecDrive", "LanServerMaxTickRate", value) + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT2k4_AdminPassword(GameOption_UT2k4): + szOptionName = "AdminPassword" + szSectionName = "Engine.AccessControl" + szKeyName = "AdminPassword" + TValueType = OptionType.OT_STRING + szDefaultValue = "cfographut" + szHelp = "Admin password" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_GamePassword(GameOption_UT2k4): + szOptionName = "GamePassword" + szSectionName = "Engine.AccessControl" + szKeyName = "GamePassword" + TValueType = OptionType.OT_STRING + szDefaultValue = "" + szHelp = "Game password" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MaxPlayers(GameOption_UT2k4): + szOptionName = "MaxPlayers" + szSectionName = "Engine.GameInfo" + szKeyName = "MaxPlayers" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "20" + szHelp = "Game Max Players" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MaxSpectators(GameOption_UT2k4): + szOptionName = "MaxSpectators" + szSectionName = "Engine.GameInfo" + szKeyName = "MaxSpectators" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "20" + szHelp = "Game Max Spectators" + + +############## +## TimeLimit +############# +class GameOption_UT2k4_TimeLimit(GameOption_UT2k4): + szOptionName = "TimeLimit" + szKeyName = "TimeLimit" + TValueType = OptionType.OT_INTEGER + szHelp = "Game Time Limit" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_CTF_TimeLimit(GameOption_UT2k4_TimeLimit): + szOptionName = "CTF_TimeLimit" + szSectionName = "xGame.xCTFGame" + szDefaultValue = "20" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_DM_TimeLimit(GameOption_UT2k4_TimeLimit): + szOptionName = "DM_TimeLimit" + szSectionName = "XGame.xDeathMatch" + szDefaultValue = "20" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_TDM_TimeLimit(GameOption_UT2k4_TimeLimit): + szOptionName = "TDM_TimeLimit" + szSectionName = "XGame.xTeamGame" + szDefaultValue = "20" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_ONS_TimeLimit(GameOption_UT2k4_TimeLimit): + szOptionName = "ONS_TimeLimit" + szSectionName = "Onslaught.ONSOnslaughtGame" + szDefaultValue = "20" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_DOM_TimeLimit(GameOption_UT2k4_TimeLimit): + szOptionName = "DOM_TimeLimit" + szSectionName = "XGame.xDoubleDom" + szDefaultValue = "3" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_BR_TimeLimit(GameOption_UT2k4_TimeLimit): + szOptionName = "BR_TimeLimit" + szSectionName = "XGame.xBombingRun" + szDefaultValue = "20" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_AS_TimeLimit(GameOption_UT2k4_TimeLimit): + szOptionName = "AS_TimeLimit" + szSectionName = "UT2k4Assault.ASGameInfo" + szDefaultValue = "20" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_LMS_TimeLimit(GameOption_UT2k4_TimeLimit): + szOptionName = "LMS_TimeLimit" + szSectionName = "BonusPack.xLastManStandingGame" + szDefaultValue = "0" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_INV_TimeLimit(GameOption_UT2k4_TimeLimit): + szOptionName = "INV_TimeLimit" + szSectionName = "SkaarjPack.Invasion" + szDefaultValue = "20" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MUT_TimeLimit(GameOption_UT2k4_TimeLimit): + szOptionName = "MUT_TimeLimit" + szSectionName = "BonusPack.xMutantGame" + szDefaultValue = "20" + + +############## +## GoalScore +############# +class GameOption_UT2k4_GoalScore(GameOption_UT2k4): + szOptionName = "GoalScore" + szKeyName = "GoalScore" + TValueType = OptionType.OT_INTEGER + szHelp = "Game Score Goal" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_CTF_GoalScore(GameOption_UT2k4_GoalScore): + szOptionName = "CTF_GoalScore" + szSectionName = "xGame.xCTFGame" + szDefaultValue = "5" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_DM_GoalScore(GameOption_UT2k4_GoalScore): + szOptionName = "DM_GoalScore" + szSectionName = "XGame.xDeathMatch" + szDefaultValue = "25" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_TDM_GoalScore(GameOption_UT2k4_GoalScore): + szOptionName = "TDM_GoalScore" + szSectionName = "XGame.xTeamGame" + szDefaultValue = "60" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_ONS_GoalScore(GameOption_UT2k4_GoalScore): + szOptionName = "ONS_GoalScore" + szSectionName = "Onslaught.ONSOnslaughtGame" + szDefaultValue = "3" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_DOM_GoalScore(GameOption_UT2k4_GoalScore): + szOptionName = "DOM_GoalScore" + szSectionName = "XGame.xDoubleDom" + szDefaultValue = "3" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_BR_GoalScore(GameOption_UT2k4_GoalScore): + szOptionName = "BR_GoalScore" + szSectionName = "XGame.xBombingRun" + szDefaultValue = "15" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_AS_GoalScore(GameOption_UT2k4_GoalScore): + szOptionName = "AS_GoalScore" + szSectionName = "UT2k4Assault.ASGameInfo" + szDefaultValue = "0" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_LMS_GoalScore(GameOption_UT2k4_GoalScore): + szOptionName = "LMS_GoalScore" + szSectionName = "BonusPack.xLastManStandingGame" + szDefaultValue = "25" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_INV_GoalScore(GameOption_UT2k4_GoalScore): + szOptionName = "INV_GoalScore" + szSectionName = "SkaarjPack.Invasion" + szDefaultValue = "60" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MUT_GoalScore(GameOption_UT2k4_GoalScore): + szOptionName = "MUT_GoalScore" + szSectionName = "BonusPack.xMutantGame" + szDefaultValue = "20" + + +############## +## ForceRespawn +############# +class GameOption_UT2k4_ForceRespawn(GameOption_UT2k4): + szOptionName = "ForceRespawn" + szKeyName = "bForceRespawn" + TValueType = OptionType.OT_BOOLEAN + szHelp = "Force player to Respawn" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_CTF_ForceRespawn(GameOption_UT2k4_ForceRespawn): + szOptionName = "CTF_ForceRespawn" + szSectionName = "xGame.xCTFGame" + szDefaultValue = "True" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_DM_ForceRespawn(GameOption_UT2k4_ForceRespawn): + szOptionName = "DM_ForceRespawn" + szSectionName = "XGame.xDeathMatch" + szDefaultValue = "True" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_TDM_ForceRespawn(GameOption_UT2k4_ForceRespawn): + szOptionName = "TDM_ForceRespawn" + szSectionName = "XGame.xTeamGame" + szDefaultValue = "True" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_ONS_ForceRespawn(GameOption_UT2k4_ForceRespawn): + szOptionName = "ONS_ForceRespawn" + szSectionName = "Onslaught.ONSOnslaughtGame" + szDefaultValue = "True" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_DOM_ForceRespawn(GameOption_UT2k4_ForceRespawn): + szOptionName = "DOM_ForceRespawn" + szSectionName = "XGame.xDoubleDom" + szDefaultValue = "True" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_BR_ForceRespawn(GameOption_UT2k4_ForceRespawn): + szOptionName = "BR_ForceRespawn" + szSectionName = "XGame.xBombingRun" + szDefaultValue = "True" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_AS_ForceRespawn(GameOption_UT2k4_ForceRespawn): + szOptionName = "AS_ForceRespawn" + szSectionName = "UT2k4Assault.ASGameInfo" + szDefaultValue = "True" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_LMS_ForceRespawn(GameOption_UT2k4_ForceRespawn): + szOptionName = "LMS_ForceRespawn" + szSectionName = "BonusPack.xLastManStandingGame" + szDefaultValue = "True" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_INV_ForceRespawn(GameOption_UT2k4_ForceRespawn): + szOptionName = "INV_ForceRespawn" + szSectionName = "SkaarjPack.Invasion" + szDefaultValue = "True" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MUT_ForceRespawn(GameOption_UT2k4_ForceRespawn): + szOptionName = "MUT_ForceRespawn" + szSectionName = "BonusPack.xMutantGame" + szDefaultValue = "True" + + +############## +## MaxLives +############# +class GameOption_UT2k4_MaxLives(GameOption_UT2k4): + szOptionName = "MaxLives" + szKeyName = "MaxLives" + TValueType = OptionType.OT_INTEGER + szHelp = "Maximum player lives" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_CTF_MaxLives(GameOption_UT2k4_MaxLives): + szOptionName = "CTF_MaxLives" + szSectionName = "xGame.xCTFGame" + szDefaultValue = "0" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_DM_MaxLives(GameOption_UT2k4_MaxLives): + szOptionName = "DM_MaxLives" + szSectionName = "XGame.xDeathMatch" + szDefaultValue = "0" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_TDM_MaxLives(GameOption_UT2k4_MaxLives): + szOptionName = "TDM_MaxLives" + szSectionName = "XGame.xTeamGame" + szDefaultValue = "0" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_ONS_MaxLives(GameOption_UT2k4_MaxLives): + szOptionName = "ONS_MaxLives" + szSectionName = "Onslaught.ONSOnslaughtGame" + szDefaultValue = "0" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_DOM_MaxLives(GameOption_UT2k4_MaxLives): + szOptionName = "DOM_MaxLives" + szSectionName = "XGame.xDoubleDom" + szDefaultValue = "0" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_BR_MaxLives(GameOption_UT2k4_MaxLives): + szOptionName = "BR_MaxLives" + szSectionName = "XGame.xBombingRun" + szDefaultValue = "0" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_AS_MaxLives(GameOption_UT2k4_MaxLives): + szOptionName = "AS_MaxLives" + szSectionName = "UT2k4Assault.ASGameInfo" + szDefaultValue = "0" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_LMS_MaxLives(GameOption_UT2k4_MaxLives): + szOptionName = "LMS_MaxLives" + szSectionName = "BonusPack.xLastManStandingGame" + szDefaultValue = "4" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_INV_MaxLives(GameOption_UT2k4_MaxLives): + szOptionName = "INV_MaxLives" + szSectionName = "SkaarjPack.Invasion" + szDefaultValue = "4" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MUT_MaxLives(GameOption_UT2k4_MaxLives): + szOptionName = "MUT_MaxLives" + szSectionName = "BonusPack.xMutantGame" + szDefaultValue = "0" + + +############## +## AllowTrans +############# +class GameOption_UT2k4_AllowTrans(GameOption_UT2k4): + szOptionName = "AllowTrans" + szKeyName = "bAllowTrans" + TValueType = OptionType.OT_BOOLEAN + szHelp = "Maximum player lives" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_CTF_AllowTrans(GameOption_UT2k4_AllowTrans): + szOptionName = "CTF_AllowTrans" + szSectionName = "xGame.xCTFGame" + szDefaultValue = "False" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_DM_AllowTrans(GameOption_UT2k4_AllowTrans): + szOptionName = "DM_AllowTrans" + szSectionName = "XGame.xDeathMatch" + szDefaultValue = "False" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_TDM_AllowTrans(GameOption_UT2k4_AllowTrans): + szOptionName = "TDM_AllowTrans" + szSectionName = "XGame.xTeamGame" + szDefaultValue = "False" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_ONS_AllowTrans(GameOption_UT2k4_AllowTrans): + szOptionName = "ONS_AllowTrans" + szSectionName = "Onslaught.ONSOnslaughtGame" + szDefaultValue = "False" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_DOM_AllowTrans(GameOption_UT2k4_AllowTrans): + szOptionName = "DOM_AllowTrans" + szSectionName = "XGame.xDoubleDom" + szDefaultValue = "False" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_BR_AllowTrans(GameOption_UT2k4_AllowTrans): + szOptionName = "BR_AllowTrans" + szSectionName = "XGame.xBombingRun" + szDefaultValue = "True" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_AS_AllowTrans(GameOption_UT2k4_AllowTrans): + szOptionName = "AS_AllowTrans" + szSectionName = "UT2k4Assault.ASGameInfo" + szDefaultValue = "False" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_LMS_AllowTrans(GameOption_UT2k4_AllowTrans): + szOptionName = "LMS_AllowTrans" + szSectionName = "BonusPack.xLastManStandingGame" + szDefaultValue = "False" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_INV_AllowTrans(GameOption_UT2k4_AllowTrans): + szOptionName = "INV_AllowTrans" + szSectionName = "SkaarjPack.Invasion" + szDefaultValue = "False" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_MUT_AllowTrans(GameOption_UT2k4_AllowTrans): + szOptionName = "MUT_AllowTrans" + szSectionName = "BonusPack.xMutantGame" + szDefaultValue = "False" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_WebServer_ServerName(GameOption_UT2k4): + szOptionName = "WebServer_ServerName" + szSectionName = "UWeb.WebServer" + szKeyName = "ServerName" + TValueType = OptionType.OT_STRING + szDefaultValue = "" + szHelp = "Server external IP or Name" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_ServerBehindNAT(GameOption_UT2k4): + szOptionName = "ServerBehindNAT" + szSectionName = "IpDrv.MasterServerUplink" + szKeyName = "ServerBehindNAT" + TValueType = OptionType.OT_BOOLEAN + szDefaultValue = "False" + szHelp = "Is server behind a NAS ?" + + +@GameOptions_Factory_Register +class GameOption_UT2k4_WebServer(GameOption_UT2k4): + szOptionName = "WebServer" + szSectionName = "UWeb.WebServer" + szKeyName = "ListenPort" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "8080" + szHelp = "enable Web Server" + + def set(self, value: str) -> None: + super().set(value) + if int(value) > 0: + self.inifile.setAddKeyValue("UWeb.WebServer", "bEnabled", "True") + else: + self.inifile.setAddKeyValue("UWeb.WebServer", "bEnabled", "False") + self.inifile.writeFile() diff --git a/src/pygamecfg/ut99.py b/src/pygamecfg/ut99.py new file mode 100644 index 0000000..5ebab08 --- /dev/null +++ b/src/pygamecfg/ut99.py @@ -0,0 +1,569 @@ +from __future__ import annotations +from typing import Union + +from pysimpleini import PySimpleINI, KeyNotFoundError, SectionNotFoundError, Section, Key +from os.path import join + +from .core import GameOptions_Factory_Register, GameOption, OptionType + + +class PySimpleINI_UT99(PySimpleINI): + def GroupKeysInSection(self, szSectionName: str, szKeyName: str) -> None: + try: + _section = self.getSection(szSectionName) + if type(_section) is Section: + ar_ServerPackages = _section.getKey(szKeyName) + if isinstance(ar_ServerPackages, Key): + ar_ServerPackages = [ar_ServerPackages] + else: # array + pass + for ServerPackages in ar_ServerPackages: + _section.delKey(ServerPackages.name, None, ServerPackages.value) + for ServerPackages in ar_ServerPackages: + _section.setAddKeyValue(ServerPackages.name, ServerPackages.value, True) + except SectionNotFoundError: + pass + + def writeFile(self, bBeautify: bool = False) -> None: + self.GroupKeysInSection("XC_Engine.XC_GameEngine", "ServerPackages") + self.GroupKeysInSection("XC_Engine.XC_GameEngine", "ServerActors") + self.GroupKeysInSection("Engine.GameEngine", "ServerPackages") + self.GroupKeysInSection("Engine.GameEngine", "ServerActors") + self.GroupKeysInSection("Core.System", "Suppress") + self.GroupKeysInSection("Editor.EditorEngine", "EditPackages") + super().writeFile(bBeautify) + + +class GameOption_UT99(GameOption): + szGameType = "ut99" + TValueType = OptionType.OT_INVALID + + szOptionName = "" + szSectionName = "" + szKeyName = "" + bForceAdd: bool = False + bRemovable: bool = False + + def __init__(self, GameRootDir: str, ConfigFileRelPath: str) -> None: + super().__init__(GameRootDir, ConfigFileRelPath) + self.mainConfigFilePath = join(GameRootDir, ConfigFileRelPath) + self.inifile = PySimpleINI_UT99(self.mainConfigFilePath) + + def set(self, value: str) -> None: + if not self.szOptionName: + raise RuntimeError("szOptionName is not set") + self.format(value) + self.inifile.setAddKeyValue(self.szSectionName, self.szKeyName, self.szFormatedValue, self.bForceAdd) + self.inifile.writeFile() + + def rem(self, value: Union[None, str]) -> None: + if not self.szOptionName: + raise RuntimeError("szOptionName is not set") + if not self.bRemovable: + raise RuntimeError("this options is not removable") + self.inifile.delKeyEx(self.szSectionName, self.szKeyName, None, value) + self.inifile.writeFile() + + def get(self) -> Union[None, str]: + if not self.szOptionName: + raise RuntimeError("szOptionName not set") + # print("get option <{0}>".format(self.szOptionName)) + res = self.inifile.getKeyValue(self.szSectionName, self.szKeyName) + return res + + +class GameOption_UT99_GenAdd(GameOption_UT99): + bForceAdd = True + + def rem(self, value: Union[None, str]) -> None: + try: + super().rem(value) + except KeyNotFoundError: + pass + + def set(self, value: str) -> None: + # force to call the rem method of THIS level + GameOption_UT99_GenAdd.rem(self, value) + super().set(value) + + +class GameOption_UT99_GenAdd__Engine(GameOption_UT99_GenAdd): + szSectionName = "Engine.GameEngine" + TValueType = OptionType.OT_STRING + bRemovable = True + + def set(self, value: str) -> None: + prev = self.szSectionName + + try: + self.inifile.getSection("XC_Engine.XC_GameEngine") + self.szSectionName = "XC_Engine.XC_GameEngine" + super().set(value) + except SectionNotFoundError: + pass + + self.szSectionName = "Engine.GameEngine" + super().set(value) + self.szSectionName = prev + + def rem(self, value: Union[None, str]) -> None: + prev = self.szSectionName + + try: + self.inifile.getSection("XC_Engine.XC_GameEngine") + self.szSectionName = "XC_Engine.XC_GameEngine" + super().rem(value) + except SectionNotFoundError: + pass + self.szSectionName = "Engine.GameEngine" + super().rem(value) + self.szSectionName = prev + + +@GameOptions_Factory_Register +class GameOption_UT99_ServerPackages(GameOption_UT99_GenAdd__Engine): + szOptionName = "ServerPackages" + szKeyName = "ServerPackages" + szHelp = "Add a ServerPackages record" + + +@GameOptions_Factory_Register +class GameOption_UT99_ServerActors(GameOption_UT99_GenAdd__Engine): + szOptionName = "ServerActors" + szKeyName = "ServerActors" + szHelp = "Add a ServerActors record" + + +@GameOptions_Factory_Register +class GameOption_UT99_Port(GameOption_UT99): + szOptionName = "Port" + szSectionName = "URL" + szKeyName = "Port" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "7777" + szHelp = "Server Listening port" + + +@GameOptions_Factory_Register +class GameOption_UT99_Map(GameOption_UT99): + szOptionName = "Map" + szSectionName = "URL" + szKeyName = "Map" + TValueType = OptionType.OT_STRING + szDefaultValue = "dm-deck16][" + szHelp = "Server Map" + + +@GameOptions_Factory_Register +class GameOption_UT99_GameType(GameOption_UT99): + szOptionName = "GameType" + szSectionName = "Engine.Engine" + szKeyName = "DefaultServerGame" + TValueType = OptionType.OT_STRING + szDefaultValue = "Botpack.DeathMatchPlus" + szHelp = "Server Gametype" + + +@GameOptions_Factory_Register +class GameOption_UT99_HostName(GameOption_UT99): + szOptionName = "HostName" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "ServerName" + TValueType = OptionType.OT_STRING + szDefaultValue = "ChaCha Test Server" + szHelp = "Server's HostName" + + def set(self, value: str) -> None: + super().set(value) + self.inifile.setAddKeyValue("Engine.GameReplicationInfo", "ShortName", value) + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT99_MOTD(GameOption_UT99): + szOptionName = "MOTD" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "MOTDLine1" + TValueType = OptionType.OT_STRING + szDefaultValue = "Welcome to ChaCha's server" + szHelp = "Message of the day" + + +@GameOptions_Factory_Register +class GameOption_UT99_MOTD2(GameOption_UT99): + szOptionName = "MOTD2" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "MOTDLine2" + TValueType = OptionType.OT_STRING + szDefaultValue = "Enjoy your stay and have fun" + szHelp = "Message of the day (2)" + + +@GameOptions_Factory_Register +class GameOption_UT99_MOTD3(GameOption_UT99): + szOptionName = "MOTD3" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "MOTDLine3" + TValueType = OptionType.OT_STRING + szDefaultValue = "" + szHelp = "Message of the day (3)" + + +@GameOptions_Factory_Register +class GameOption_UT99_MOTD4(GameOption_UT99): + szOptionName = "MOTD4" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "MOTDLine4" + TValueType = OptionType.OT_STRING + szDefaultValue = "Game Server by ChaCha" + szHelp = "Message of the day (4)" + + +@GameOptions_Factory_Register +class GameOption_UT99_AdminEmail(GameOption_UT99): + szOptionName = "AdminEmail" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "AdminEmail" + TValueType = OptionType.OT_STRING + szDefaultValue = "chachacorp@protonmail.com" + szHelp = "Admin mail" + + +@GameOptions_Factory_Register +class GameOption_UT99_AdminName(GameOption_UT99): + szOptionName = "AdminName" + szSectionName = "Engine.GameReplicationInfo" + szKeyName = "AdminName" + TValueType = OptionType.OT_STRING + szDefaultValue = "chacha" + szHelp = "Admin name" + + def set(self, value: str) -> None: + super().set(value) + self.inifile.setAddKeyValue("UTServerAdmin.UTServerAdmin", "AdminUsername", value) + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT99_HTTPDownloadServer(GameOption_UT99): + szOptionName = "HTTPDownloadServer" + szSectionName = "IpDrv.HTTPDownload" + szKeyName = "RedirectToURL" + TValueType = OptionType.OT_STRING + szDefaultValue = "http://chacha.ddns.net/games/ut99" + szHelp = "FastDL url" + + def set(self, value: str) -> None: + super().set(value) + self.inifile.setAddKeyValue("IpDrv.HTTPDownload", "UseCompression", "True") + self.inifile.delKey("IpDrv.HTTPDownload", "ProxyServerHost") + self.inifile.delKey("IpDrv.HTTPDownload", "ProxyServerPort") + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT99_MaxClientRate(GameOption_UT99): + szOptionName = "MaxClientRate" + szSectionName = "IpDrv.TcpNetDriver" + szKeyName = "MaxClientRate" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "25000" + szHelp = "Max Client Rate" + + +@GameOptions_Factory_Register +class GameOption_UT99_MinClientRate(GameOption_UT99): + szOptionName = "MinClientRate" + szSectionName = "IpDrv.TcpNetDriver" + szKeyName = "MinClientRate" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "12000" + szHelp = "Max Client Rate" + + +@GameOptions_Factory_Register +class GameOption_UT99_NetServerMaxTickRate(GameOption_UT99): + szOptionName = "NetServerMaxTickRate" + szSectionName = "IpDrv.TcpNetDriver" + szKeyName = "NetServerMaxTickRate" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "60" + szHelp = "Server Max TickRate" + + def set(self, value: str) -> None: + super().set(value) + self.inifile.setAddKeyValue("Engine.DemoRecDriver", "NetServerMaxTickRate", value) + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT99_LanServerMaxTickRate(GameOption_UT99): + szOptionName = "LanServerMaxTickRate" + szSectionName = "IpDrv.TcpNetDriver" + szKeyName = "LanServerMaxTickRate" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "60" + szHelp = "Lan Server Max TickRate" + + def set(self, value: str) -> None: + super().set(value) + self.inifile.setAddKeyValue("Engine.DemoRecDriver", "LanServerMaxTickRate", value) + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT99_AdminPassword(GameOption_UT99): + szOptionName = "AdminPassword" + szSectionName = "Engine.GameInfo" + szKeyName = "AdminPassword" + TValueType = OptionType.OT_STRING + szDefaultValue = "cfographut" + szHelp = "Admin password" + + def set(self, value: str) -> None: + super().set(value) + self.inifile.setAddKeyValue("UTServerAdmin.UTServerAdmin", "AdminPassword", value) + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT99_GamePassword(GameOption_UT99): + szOptionName = "GamePassword" + szSectionName = "Engine.GameInfo" + szKeyName = "GamePassword" + TValueType = OptionType.OT_STRING + szDefaultValue = "" + szHelp = "Game password" + + +@GameOptions_Factory_Register +class GameOption_UT99_MaxPlayers(GameOption_UT99): + szOptionName = "MaxPlayers" + szSectionName = "Engine.GameInfo" + szKeyName = "MaxPlayers" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "20" + szHelp = "Game Max Players" + + +@GameOptions_Factory_Register +class GameOption_UT99_MaxSpectators(GameOption_UT99): + szOptionName = "MaxSpectators" + szSectionName = "Engine.GameInfo" + szKeyName = "MaxSpectators" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "20" + szHelp = "Game Max Spectators" + + +@GameOptions_Factory_Register +class GameOption_UT99_AS_TimeLimit(GameOption_UT99): + szOptionName = "AS_TimeLimit" + szSectionName = "Botpack.Assault" + szKeyName = "TimeLimit" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "20" + szHelp = "Game Time Limit" + + +@GameOptions_Factory_Register +class GameOption_UT99_DOM_TimeLimit(GameOption_UT99): + szOptionName = "DOM_TimeLimit" + szSectionName = "Botpack.Domination" + szKeyName = "TimeLimit" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "20" + szHelp = "Game Time Limit" + + +@GameOptions_Factory_Register +class GameOption_UT99_CTF_TimeLimit(GameOption_UT99): + szOptionName = "CTF_TimeLimit" + szSectionName = "Botpack.CTFGame" + szKeyName = "TimeLimit" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "20" + szHelp = "Game Time Limit" + + +@GameOptions_Factory_Register +class GameOption_UT99_DM_TimeLimit(GameOption_UT99): + szOptionName = "DM_TimeLimit" + szSectionName = "Botpack.DeathMatchPlus" + szKeyName = "TimeLimit" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "10" + szHelp = "Game Time Limit" + + +@GameOptions_Factory_Register +class GameOption_UT99_GoalTeamScore(GameOption_UT99): + szOptionName = "GoalTeamScore" + szSectionName = "Botpack.TeamGamePlus" + szKeyName = "GoalTeamScore" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "3" + szHelp = "Game Score Limit" + + +@GameOptions_Factory_Register +class GameOption_UT99_MaxTeamSize(GameOption_UT99): + szOptionName = "MaxTeamSize" + szSectionName = "Botpack.TeamGamePlus" + szKeyName = "MaxTeamSize" + TValueType = OptionType.OT_FLOAT + szDefaultValue = "12" + szHelp = "Maximum Team Size" + + +@GameOptions_Factory_Register +class GameOption_UT99_DM_FragLimit(GameOption_UT99): + szOptionName = "DM_FragLimit" + szSectionName = "Botpack.DeathMatchPlus" + szKeyName = "FragLimit" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "10" + szHelp = "Game Score Limit" + + +@GameOptions_Factory_Register +class GameOption_UT99_ServerLogName(GameOption_UT99): + szOptionName = "ServerLogName" + szSectionName = "Engine.GameInfo" + szKeyName = "ServerLogName" + TValueType = OptionType.OT_STRING + szDefaultValue = "server.log" + szHelp = "Server Log Name" + + +@GameOptions_Factory_Register +class GameOption_UT99_WebServer(GameOption_UT99): + szOptionName = "WebServer" + szSectionName = "UWeb.WebServer" + szKeyName = "ListenPort" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "8076" + szHelp = "enable Web Server" + + def set(self, value: str) -> None: + # fix ut99 v469c + try: + self.inifile.delKey("UWeb.WebServer", "Listenport") + except: + pass + super().set(value) + if int(value) > 0: + self.inifile.setAddKeyValue("UWeb.WebServer", "bEnabled", "True") + else: + self.inifile.setAddKeyValue("UWeb.WebServer", "bEnabled", "False") + self.inifile.writeFile() + + +@GameOptions_Factory_Register +class GameOption_UT99_TournamentMode(GameOption_UT99): + szOptionName = "TournamentMode" + szSectionName = "Botpack.DeathMatchPlus" + szKeyName = "bTournament" + TValueType = OptionType.OT_BOOLEAN + szDefaultValue = "False" + szHelp = "Enable Tournament Mode" + + +@GameOptions_Factory_Register +class GameOption_UT99_InitialBots(GameOption_UT99): + szOptionName = "InitialBots" + szSectionName = "Botpack.DeathMatchPlus" + szKeyName = "InitialBots" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "0" + szHelp = "Initial Bots number" + + +@GameOptions_Factory_Register +class GameOption_UT99_MinPlayers(GameOption_UT99): + szOptionName = "MinPlayers" + szSectionName = "Botpack.DeathMatchPlus" + szKeyName = "MinPlayers" + TValueType = OptionType.OT_INTEGER + szDefaultValue = "0" + szHelp = "Minimum player count (allow to adjust bots)" + + +@GameOptions_Factory_Register +class GameOption_UT99_AS_UseTranslocator(GameOption_UT99): + szOptionName = "AS_UseTranslocator" + szSectionName = "Botpack.CTFGame" + szKeyName = "bUseTranslocator" + TValueType = OptionType.OT_BOOLEAN + szDefaultValue = "True" + szHelp = "Enable Translocator" + + +@GameOptions_Factory_Register +class GameOption_UT99_CTF_UseTranslocator(GameOption_UT99): + szOptionName = "CTF_UseTranslocator" + szSectionName = "Botpack.CTFGame" + szKeyName = "bUseTranslocator" + TValueType = OptionType.OT_BOOLEAN + szDefaultValue = "True" + szHelp = "Enable Translocator" + + +@GameOptions_Factory_Register +class GameOption_UT99_DM_UseTranslocator(GameOption_UT99): + szOptionName = "DM_UseTranslocator" + szSectionName = "Botpack.DeathMatchPlus" + szKeyName = "bUseTranslocator" + TValueType = OptionType.OT_BOOLEAN + szDefaultValue = "True" + szHelp = "Enable Translocator" + + +@GameOptions_Factory_Register +class GameOption_UT99_DOM_UseTranslocator(GameOption_UT99): + szOptionName = "DOM_UseTranslocator" + szSectionName = "Botpack.Domination" + szKeyName = "bUseTranslocator" + TValueType = OptionType.OT_BOOLEAN + szDefaultValue = "True" + szHelp = "Enable Translocator" + + +@GameOptions_Factory_Register +class GameOption_UT99_CTF_ForceRespawn(GameOption_UT99): + szOptionName = "CTF_ForceRespawn" + szSectionName = "Botpack.CTFGame" + szKeyName = "bForceRespawn" + TValueType = OptionType.OT_BOOLEAN + szDefaultValue = "True" + szHelp = "Force Player to respawn" + + +@GameOptions_Factory_Register +class GameOption_UT99_DM_ForceRespawn(GameOption_UT99): + szOptionName = "DM_ForceRespawn" + szSectionName = "Botpack.DeathMatchPlus" + szKeyName = "bForceRespawn" + TValueType = OptionType.OT_BOOLEAN + szDefaultValue = "True" + szHelp = "Force Player to respawn" + + +@GameOptions_Factory_Register +class GameOption_UT99_DOM_ForceRespawn(GameOption_UT99): + szOptionName = "DOM_ForceRespawn" + szSectionName = "Botpack.Domination" + szKeyName = "bForceRespawn" + TValueType = OptionType.OT_BOOLEAN + szDefaultValue = "True" + szHelp = "Force Player to respawn" + + +@GameOptions_Factory_Register +class GameOption_UT99_AS_ForceRespawn(GameOption_UT99): + szOptionName = "AS_ForceRespawn" + szSectionName = "Botpack.Assault" + szKeyName = "bForceRespawn" + TValueType = OptionType.OT_BOOLEAN + szDefaultValue = "True" + szHelp = "Force Player to respawn" diff --git a/test/test_test_module.py b/test/test_ut99.py similarity index 100% rename from test/test_test_module.py rename to test/test_ut99.py