dev #1

Merged
chacha merged 13 commits from dev into master 2023-09-24 20:16:01 +02:00
26 changed files with 1982 additions and 418 deletions

View File

@@ -1,3 +1,10 @@
eclipse.preferences.version=1
encoding//src/pygamecfg/__init__.py=utf-8
encoding//src/pygamecfg/__main__.py=utf-8
encoding//src/pygamecfg/common_ut.py=utf-8
encoding//src/pygamecfg/core_gamecfg.py=utf-8
encoding//src/pygamecfg/game_cod4.py=utf-8
encoding//src/pygamecfg/game_ut2k4.py=utf-8
encoding//src/pygamecfg/game_ut99.py=utf-8
encoding//src/pygamecfg/tool_ini.py=utf-8
encoding/<project>=UTF-8

2
Jenkinsfile vendored
View File

@@ -341,7 +341,7 @@ pipeline {
|'''.strip()
|
|import copier
|copier.run_auto("./", "../_gitrepo",vcs_ref="HEAD",use_prereleases=True,defaults=True,cleanup_on_error=False)
|copier.run_copy("./", "../_gitrepo",vcs_ref="HEAD",use_prereleases=True,defaults=True,cleanup_on_error=False)
|
|__EOWRAPPER__
""".stripMargin())

View File

@@ -8,46 +8,8 @@
![](docs-static/Library.jpg)
# Python project template
# pyGameCFG
A nice template to start blank python projets.
This template automate a lot of handy things and allow CI/CD automatic releases generation.
It is also collectings data to feed Jenkins build.
Checkout [Latest Documentation](https://chacha.ddns.net/mkdocs-web/chacha/{{repository}}/{{branch}}/latest/).
## Features
### Generic pipeline skeleton:
- Prepare
- GetCode
- BuildPackage
- Install
- CheckCode
- PlotMetrics
- RunUnitTests
- GenDOC
- PostRelease
### CI/CD Environment
- Jenkins
- Gitea (with patch for dynamic Readme variables: https://chacha.ddns.net/gitea/chacha/GiteaMarkupVariable)
- Docker
- MkDocsWeb
### CI/CD Helper libs
- VirtualEnv
- Changelog generation based on commits
- copier
- pylint + pylint_json2html
- mypy
- unittest + xmlrunner + junitparser + junit2htmlreport
- mkdocs
### Python project
- Full .toml implementation
- .whl automatic generation
- dynamic versionning using git repository
- embedded unit-test

View File

@@ -1,16 +1 @@
# Usage
## Pulvinar dolor
Donec dapibus est fermentum justo volutpat condimentum. Integer quis nunc neque. Donec dictum vehicula justo, in facilisis ex tincidunt in.
Vivamus sollicitudin sem dui, id mollis orci facilisis ut. Proin sed pulvinar dolor. Donec volutpat commodo urna imperdiet pulvinar. Fusce eget aliquam risus.
Vivamus viverra luctus ex, in finibus mi. Nullam elementum dapibus mollis. Ut suscipit volutpat ex, quis feugiat lacus consectetur eu.
## Condimentum faucibus
Quisque auctor egestas sem, luctus suscipit ex maximus vitae. Duis facilisis augue et condimentum faucibus.
Donec cursus, enim a sagittis egestas, lectus lorem eleifend libero, at tincidunt leo magna at libero.
Nunc eros velit, suscipit luctus tempor vel, finibus et est. Curabitur efficitur pretium pulvinar.
Donec urna lectus, vulputate quis turpis sed, placerat congue urna. Phasellus aliquet fermentum quam, non auctor elit porta nec. Morbi eu ligula at nisl ultricies condimentum vitae id ante.
## Aliquam lacinia
In volutpat lorem ex, et fringilla nibh faucibus quis. Mauris et arcu elementum, auctor dui vitae, egestas arcu. Duis sit amet aliquam quam.
Phasellus a odio turpis. Etiam tristique mi eu enim varius, eget facilisis est vestibulum. Aliquam lacinia nec purus sed luctus. Cras at laoreet erat.

View File

@@ -50,6 +50,7 @@ class doc_gen(helper_withresults_base):
reference_path = doc_path / "reference"
cls._reset_dir(reference_path)
# create one .md per python module
for path in sorted((cls.project_rootdir_path / "src").rglob("*.py")):
module_path = path.relative_to(cls.project_rootdir_path / "src").with_suffix("")
doc_path = path.relative_to(cls.project_rootdir_path / "src").with_suffix(".md")
@@ -57,14 +58,12 @@ class doc_gen(helper_withresults_base):
parts = list(module_path.parts)
if parts[-1] == "__init__":
parts = parts[:-1]
elif parts[-1] == "__main__":
if parts[-1] in ("__init__", "__main__"):
continue
cls._reset_dir(os.path.dirname(full_doc_path))
cls._create_dir(full_doc_path.parent.resolve())
with open(full_doc_path, "w+") as fd:
identifier = "src." + ".".join(parts)
identifier = ".".join(parts)
print("::: " + identifier, file=fd)
cmdopts = [f"{sys.executable}", "-m", "mkdocs", "-v", "build", "--site-dir", str(site_path), "--clean"]
@@ -93,6 +92,7 @@ class doc_gen(helper_withresults_base):
with open(cls.project_rootdir_path / "mkdocs.yml", "w") as mkdocsCfgFile:
mkdocsCfgFile.write(yaml.dump(mkdocsCfg, Dumper=Dumper, default_flow_style=False, sort_keys=False))
print(" !! start doc generation")
res = cls.run_cmd(cmdopts)
print(res.decode())
print(" !! done")

View File

@@ -11,6 +11,7 @@ from typing import TYPE_CHECKING
from abc import ABC, abstractmethod
import os
import shutil
from pathlib import Path
import subprocess
@@ -34,11 +35,17 @@ class helper_base(ABC):
return None
@staticmethod
def _reset_dir(dirpath: Path):
def _create_dir(dirpath: Path):
dirpath = Path(dirpath)
if not os.path.exists(dirpath):
os.makedirs(dirpath)
[f.unlink() for f in Path(dirpath).glob("*") if f.is_file()]
@staticmethod
def _reset_dir(dirpath: Path):
dirpath = Path(dirpath)
if os.path.exists(dirpath):
shutil.rmtree(dirpath)
os.makedirs(dirpath)
@classmethod
def reset_result_dir(cls):

View File

@@ -63,7 +63,7 @@ class quality_check(helper_withresults_base):
[
"--load-plugins=pylint.extensions.mccabe",
"--output-format=json,parseable",
"--disable=invalid-name",
"--disable=invalid-name,too-few-public-methods,too-many-arguments", # ignore
"--ignore=_version.py",
"--reports=y",
"--score=yes",

View File

@@ -1,19 +1,11 @@
# pyChaChaDummyProject (c) by chacha
#
# pyChaChaDummyProject 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 <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
docs_dir: docs
site_name: 'pygamecfg'
site_url: 'https://chacha.ddns.net/mkdocs-web/chacha/pygamecfg/latest/'
site_description: 'A simple game config tool that provide bash API to read / write game config files (cod, ut ...)'
site_author: 'chacha'
repo_url: 'https://chacha.ddns.net/gitea/chacha/pygamecfg'
site_name: pygamecfg
site_url: https://chacha.ddns.net/mkdocs-web/chacha/pygamecfg/latest/
site_description: A simple ini parser / factory
site_author: chacha
repo_url: https://chacha.ddns.net/gitea/chacha/pygamecfg
use_directory_urls: false
copyright: 'CC BY-NC-SA 4.0'
copyright: CC BY-NC-SA 4.0
theme:
name: material
features:
@@ -22,11 +14,11 @@ theme:
- navigation.tabs
- navigation.tabs.sticky
- navigation.footer
- toc.integrate
- navigation.path
- navigation.top
- navigation.section
- content.code.annotate
- navigation.prune
- navigation.expand
- toc.follow
palette:
- media: '(prefers-color-scheme: dark)'
@@ -52,19 +44,30 @@ plugins:
default_handler: python
handlers:
python:
path:
- src
options:
filters:
- '!^_[^_]'
inherited_members: true
inherited_members: false
show_if_no_docstring: true
show_signature_annotations: true
show_source: false
show_category_heading: true
group_by_category: true
docstring_section_style: spacy
show_root_full_path: false
merge_init_into_class: true
separate_signature: true
heading_level: 2
docstring_section_style: spacy
show_root_toc_entry: false
- with-pdf:
cover_subtitle: User Manual
cover_logo: C:\Users\chacha\git\pygamecfg\docs-static\Library.jpg
verbose: false
exclude_pages:
- LICENSE
output_path: C:\Users\chacha\git\pygamecfg\helpers-results\doc_gen\site\pdf\manual.pdf
markdown_extensions:
- def_list
- tables
@@ -115,4 +118,4 @@ markdown_extensions:
emoji_generator: !!python/name:materialx.emoji.to_svg
extra:
branch: master
repository: pygitversionhelper
repository: pygamecfg

View File

@@ -35,7 +35,8 @@ classifiers = [
dependencies = [
'importlib-metadata; python_version<"3.9"',
'packaging',
'pysimpleini>=0.3.1'
'pysimpleini>=0.3.1',
'typed-argument-parser==1.*'
]
dynamic = ["version"]
@@ -48,6 +49,7 @@ where = ["src"]
[tool.setuptools.package-data]
"pygamecfg.data" = ["*.*"]
"pysimpleini" = ["py.typed"]
[project.urls]
Homepage = "https://chacha.ddns.net/gitea/chacha/pygamecfg"
@@ -60,8 +62,8 @@ coverage-check = ["coverage>=7.0"]
complexity-check = ["radon>=5.1"]
quality-check = ["pylint>=2.15","pylint-json2html>=0.4","pandas>=1.5"]
type-check = ["mypy[reports]>=0.99" ]
doc-gen = ["mkdocs>=1.4.0", "mkdocs-material>=8.5","mkdocs-pymdownx-material-extras", "mkdocs-localsearch>=0.9.0", "mkdocstrings[python]>=0.19", "mkdocs-with-pdf>=0.9.3","pyyaml>=6.0","pymdown-extensions>=9","mkdocs-markdownextradata-plugin","mkdocs-mermaid2-plugin"]
doc-gen = ["mkdocs>=1.4.0", "mkdocs-material>=8.5","mkdocs-material-extensions","mkdocs-pymdownx-material-extras", "mkdocs-localsearch>=0.9.0", "mkdocstrings[python]>=0.19", "mkdocs-with-pdf>=0.9.3","pyyaml>=6.0","pymdown-extensions>=9","mkdocs-markdownextradata-plugin","mkdocs-mermaid2-plugin","mkdocs-autorefs"]
#[project.scripts]
#my-script = "my_package.module:function"
# [project.scripts]
# my-script = "my_package.module:function"

68
pyproject.toml.bak Normal file
View File

@@ -0,0 +1,68 @@
# pyChaChaDummyProject (c) by chacha
#
# pyChaChaDummyProject 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 <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
[build-system]
requires = ["setuptools>=63", "wheel", "setuptools_scm"]
build-backend = "setuptools.build_meta"
[tool.setuptools_scm]
version_scheme= "post-release"
[project]
name = "pygamecfg"
description = "A simple game config tool that provide bash API to read / write game config files (cod, ut ...)"
readme = "README.md"
requires-python = ">=3.9"
keywords = ["chacha","chacha","template","pygamecfg"]
license = { file = "LICENSE.md" }
authors = [
{name="chacha",email="1000CHACHA0001@gmail.com"},
]
maintainers = [
{name="chacha",email="1000CHACHA0001@gmail.com"},
]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
]
dependencies = [
'importlib-metadata; python_version<"3.9"',
'packaging',
'pysimpleini>=0.3.1',
'typed-argument-parser==1.*'
]
dynamic = ["version"]
[tool.setuptools]
platforms = ["any"]
include-package-data = true
[tool.setuptools.packages.find]
where = ["src"]
[tool.setuptools.package-data]
"pygamecfg.data" = ["*.*"]
[project.urls]
Homepage = "https://chacha.ddns.net/gitea/chacha/pygamecfg"
Documentation = "https://chacha.ddns.net/mkdocs-web/chacha/pygamecfg/master/latest/"
Tracker = "https://chacha.ddns.net/gitea/chacha/pygamecfg/issues"
[project.optional-dependencies]
test = ["junitparser>=2.8","junit2html>=30.1","xmlrunner>=1.7","mypy>=0.99" ]
coverage-check = ["coverage>=7.0"]
complexity-check = ["radon>=5.1"]
quality-check = ["pylint>=2.15","pylint-json2html>=0.4","pandas>=1.5"]
type-check = ["mypy[reports]>=0.99" ]
doc-gen = ["mkdocs>=1.4.0", "mkdocs-material>=8.5","mkdocs-material-extensions","mkdocs-pymdownx-material-extras", "mkdocs-localsearch>=0.9.0", "mkdocstrings[python]>=0.19", "mkdocs-with-pdf>=0.9.3","pyyaml>=6.0","pymdown-extensions>=9","mkdocs-markdownextradata-plugin","mkdocs-mermaid2-plugin","mkdocs-autorefs"]
# [project.scripts]
# my-script = "my_package.module:function"

View File

@@ -1,6 +1,9 @@
# pygamecfg (c) by chacha
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pyGameCFG(c) by chacha
#
# pygamecfg is licensed under a
# 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
@@ -13,7 +16,6 @@ Main module __init__ file.
from importlib.metadata import distribution, version, PackageNotFoundError
import warnings
from .core import GameOptions_Factory
try: # pragma: no cover
__version__ = version("pygamecfg")
@@ -34,3 +36,9 @@ try: # pragma: no cover
except PackageNotFoundError: # pragma: no cover
warnings.warn('can not read dist.metadata["Name"], assuming local test context, setting it to <pygamecfg>')
__Name__ = "pygamecfg"
from pygamecfg.core_gamecfg import GameOptions_Factory
from . import game_cod4
from . import game_ut99
from . import game_ut2k4

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# PySimpleINI (c) by chacha
# pyGameCFG(c) by chacha
#
# PySimpleINI is licensed under a
# 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
@@ -10,52 +11,102 @@
"""CLI interface module"""
from __future__ import annotations
from typing import Literal, cast, Union
from argparse import ArgumentParser
import sys
from tap import Tap
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)
class pygamecfg_args_SetOption(Tap):
"""SetOption CLI arg subparser"""
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="?")
option: str
value: str = ""
def configure(self) -> None:
self.add_argument("option")
self.add_argument("value")
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="?")
class pygamecfg_args_RemOption(Tap):
"""RemOption CLI arg subparser"""
GetOption_subparser = subparsers.add_parser("GetOption", help="Get a game config file option value")
GetOption_subparser.add_argument("option")
option: str
value: str = ""
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)
def configure(self) -> None:
self.add_argument("option")
self.add_argument("value")
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")
class pygamecfg_args_GetOption(Tap):
"""GetOption CLI arg subparser"""
option: str
def configure(self) -> None:
self.add_argument("option")
class pygamecfg_args(Tap):
"""Main CLI arg parser"""
verbosity: int = 0
basegamedir: str = "./"
configfile: str = ""
game: Literal["ut99", "cod4", "ut2k4"]
def configure(self) -> None:
self.add_argument("-v", "--verbosity", action="count", help="increase output verbosity")
self.add_argument("-b", "--basegamedir", help="set the game base dir")
self.add_argument("-c", "--configfile", help="set the default config file")
self.add_argument("-g", "--game", help="the target game")
self.add_subparsers(dest="command", help="command type", required=True)
self.add_subparser("SetOption", pygamecfg_args_SetOption, help="Set/Add a game config file option value")
self.add_subparser("RemOption", pygamecfg_args_RemOption, help="Remove a game config file option, w or w/o value")
self.add_subparser("GetOption", pygamecfg_args_GetOption, help="Get a game config file option value")
def process_args(self) -> None:
"""dynamically add self.command to avoid conflict with Tap/argparse while keep pylint happy"""
self.command: Union[str, None] = cast(Union[str, None], self.command) # pylint: disable=attribute-defined-outside-init
def fct_main(i_args: list[str]) -> None:
"""CLI main function"""
parser: pygamecfg_args = pygamecfg_args(prog=__Name__, description=__Summuary__)
args: pygamecfg_args = parser.parse_args(i_args)
if args.verbosity:
print(f"Using base game dir: {args.basegamedir}")
print(f"Using config file: {args.configfile}")
if args.configfile == "":
if args.game == "ut99":
args.configfile = "./System/UnrealTournament.ini"
if args.game == "ut2k4":
args.configfile = "./System/UT2004.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(
cast(pygamecfg_args_SetOption, args).option, cast(pygamecfg_args_SetOption, args).value # pylint: disable=no-member
)
elif args.command == "RemOption":
GameOptions.rem(
cast(pygamecfg_args_RemOption, args).option, cast(pygamecfg_args_RemOption, args).value # pylint: disable=no-member
)
elif args.command == "GetOption":
res = GameOptions.get(cast(pygamecfg_args_GetOption, args).option) # pylint: disable=no-member
print(res)
else:
raise RuntimeError("Invalid argument")
if __name__ == "__main__":
fct_main(sys.argv[1:])

View File

@@ -0,0 +1,72 @@
#!/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 <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
"""common UT functions"""
from __future__ import annotations
from typing import Union
from os.path import join
from pathlib import Path
from pysimpleini import PySimpleINI
from .core_gamecfg import GameOption, OptionType
class GameOption_UT(GameOption):
"""generic UT Option class"""
szGameType = ""
TValueType = OptionType.OT_INVALID
szOptionName = ""
szSectionName = ""
szKeyName = ""
bForceAdd: bool = False
bRemovable: bool = False
cachedFile: Union[None, PySimpleINI] = None
cachedFilePath: Union[None, Path] = None
Cls_PySimpleINI: type[PySimpleINI] = PySimpleINI
@classmethod
def openFile(cls, filepath: Path) -> PySimpleINI:
"""Open the file"""
if (not cls.cachedFile) or (filepath != cls.cachedFilePath):
cls.cachedFilePath = filepath
cls.cachedFile = cls.Cls_PySimpleINI(filepath)
return cls.cachedFile
def __init__(self, GameRootDir: str, ConfigFileRelPath: str) -> None:
super().__init__(GameRootDir, ConfigFileRelPath)
self.mainConfigFilePath: Path = Path(join(GameRootDir, ConfigFileRelPath))
self.inifile = self.openFile(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.delkey_ex(self.szSectionName, self.szKeyName, None, value)
self.inifile.writefile()
def get(self) -> Union[str, list[str]]:
if not self.szOptionName:
raise RuntimeError("szOptionName not set")
return self.inifile.getkeyvalue(self.szSectionName, self.szKeyName)

View File

@@ -1,97 +0,0 @@
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

View File

@@ -0,0 +1,154 @@
#!/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 <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
""" Core file of pygamecfg
contain generic management code for GameOption
"""
from __future__ import annotations
from typing import Union
from abc import ABCMeta, abstractmethod
from enum import Enum
class OptionType(Enum):
"""Supported option data type"""
OT_INVALID = 0
OT_STRING = 1
OT_INTEGER = 2
OT_BOOLEAN = 3
OT_FLOAT = 4
class GameOption(metaclass=ABCMeta):
"""Game option base type"""
szGameType: str = ""
szOptionName: str = ""
TValueType: OptionType = OptionType.OT_INVALID
szDefaultValue: str = ""
szHelp: str = ""
szFormatedValue: str = ""
def __init__(self, GameRootDir: str, ConfigFileRelPath: Union[None, str] = None):
"""GameOption constructor.
///warning
This object does not aim to be created
///
Args:
GameRootDir: root dir of the game
ConfigFileRelPath: path to the configfile (relative to rootdir)
"""
self.GameRootDir = GameRootDir
self.ConfigFileRelPath = ConfigFileRelPath
def __enter__(self) -> GameOption:
"""contextlib enter hook"""
return self
def __exit__(self, exception_type, exception_value, exception_traceback) -> None:
"""contextlib exit hook"""
self.close()
def close(self) -> None:
"""user-define close() function (for subclassing)"""
def format(self, value: Union[int, str, float]) -> None:
"""standard function to format options before writing it to file (overloadable)"""
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 ValueError:
self.szFormatedValue = str(True) if str(value).lower() == "true" else str(False)
elif self.TValueType == OptionType.OT_FLOAT:
self.szFormatedValue = str(float(value))
else:
raise RuntimeError("Invalid Option TValueType")
print(f"setting option <{self.szOptionName}> to: {self.szFormatedValue}")
@abstractmethod
def set(self, value: str) -> None:
"""generic set function"""
raise NotImplementedError("method not implemented")
@abstractmethod
def rem(self, value: Union[None, str]) -> None:
"""generic rem function"""
raise NotImplementedError("method not implemented")
@abstractmethod
def get(self) -> Union[str, list[str]]:
"""generic get function"""
raise NotImplementedError("method not implemented")
class GameOptions_Factory:
"""factory that manage game options based on Game and the option itself"""
ar_Options_cls: list[type[GameOption]] = []
ar_Options_cls_filtered: list[type[GameOption]] = []
GameRootDir: str = "./"
ConfigFileRelPath: str = ""
def __init__(self, szGameType: str, GameRootDir: str, ConfigFileRelPath: str) -> None:
self.szGameType = szGameType
self.GameRootDir = GameRootDir
self.ConfigFileRelPath = ConfigFileRelPath
for Options_cls in GameOptions_Factory.ar_Options_cls:
if Options_cls.szGameType == szGameType:
self.ar_Options_cls_filtered.append(Options_cls)
@classmethod
def GameOptionRegister(cls, Option: type[GameOption]) -> None:
"""interface option used by decorator to register option implementation classes"""
GameOptions_Factory.ar_Options_cls.append(Option)
def set(self, OptionName: str, value: str) -> None:
"""generic set function (API call)"""
for _option in GameOptions_Factory.ar_Options_cls_filtered:
if _option.szOptionName == OptionName:
with _option(self.GameRootDir, self.ConfigFileRelPath) as _optionInst:
_optionInst.set(value)
return
raise RuntimeError("Option not found")
def rem(self, OptionName: str, value: Union[None, str]) -> None:
"""generic rem function (API call)"""
for _option in self.ar_Options_cls_filtered:
if _option.szOptionName == OptionName:
with _option(self.GameRootDir, self.ConfigFileRelPath) as _optionInst:
_optionInst.rem(value)
return
raise RuntimeError("Option not found")
def get(self, OptionName: str) -> Union[str, list[str]]:
"""generic get function (API call)"""
for _option in self.ar_Options_cls_filtered:
if _option.szOptionName == OptionName:
with _option(self.GameRootDir, self.ConfigFileRelPath) as _optionInst:
return _optionInst.get()
raise RuntimeError("Option not found")
def GameOptions_Factory_Register(cls: type[GameOption]) -> type[GameOption]:
"""decorator to register game option concrete implementation"""
GameOptions_Factory.GameOptionRegister(cls)
return cls

View File

@@ -1,7 +0,0 @@
# 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 <https://creativecommons.org/licenses/by-nc-sa/4.0/>.

View File

@@ -1,10 +1,22 @@
#!/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 <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,duplicate-code
from __future__ import annotations
from typing import Union
import re
from os.path import join
from .core import GameOptions_Factory_Register, GameOption, OptionType
from .core_gamecfg import GameOptions_Factory_Register, GameOption, OptionType
class GameOption_COD4(GameOption):
@@ -19,7 +31,10 @@ class GameOption_COD4(GameOption):
def __init__(self, GameRootDir: str, ConfigFileRelPath: str) -> None:
super().__init__(GameRootDir, ConfigFileRelPath)
self.mainConfigFilePath = join(GameRootDir, ConfigFileRelPath)
self.cfgfile = open(self.mainConfigFilePath, "r")
self.cfgfile = open(self.mainConfigFilePath, "r", encoding="utf8") # pylint: disable=consider-using-with
def close(self) -> None:
self.cfgfile.close()
def set(self, value: str) -> None:
if not self.szOptionName:
@@ -31,7 +46,7 @@ class GameOption_COD4(GameOption):
value = self.szPrefix + " " + self.szKeyName + " " + value
for line in self.cfgfile.readlines():
if re.search(r"\s+" + self.szPrefix + r"\s+"):
if re.search(r"\s+" + self.szPrefix + r"\s+", line):
print(f"found: {line}")
def rem(self, value: Union[str, None] = None) -> None:
@@ -54,11 +69,11 @@ class GameOption_COD4(GameOption):
raise RuntimeError("Option not found in file")
self.cfgfile.close()
with open(self.mainConfigFilePath, "w") as ofile:
with open(self.mainConfigFilePath, "w", encoding="utf8") as ofile:
ofile.write(newFile)
self.cfgfile = open(self.mainConfigFilePath, "r")
self.cfgfile = open(self.mainConfigFilePath, "r", encoding="utf8") # pylint: disable=consider-using-with
def get(self) -> Union[None, str]:
def get(self) -> str:
if not self.szOptionName:
raise RuntimeError("szOptionName not set")
@@ -67,7 +82,7 @@ class GameOption_COD4(GameOption):
else:
regex = r"^\s*" + self.szPrefix + r"\s+" + self.szOptionName + r"\s*(?P<value>.*)"
bfound = False
res = None
for line in self.cfgfile.readlines():
if result := re.search(regex, line):
if bfound:

View File

@@ -1,75 +1,41 @@
#!/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 <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
# pylint: disable=missing-class-docstring,missing-function-docstring,duplicate-code
"""UT2k4 command set"""
from __future__ import annotations
from typing import Union
from pysimpleini import PySimpleINI, KeyNotFoundError, SectionNotFoundError, Section, Key
from os.path import join
from pysimpleini import KeyNotFoundError
from .core import GameOptions_Factory_Register, GameOption, OptionType
from .core_gamecfg import GameOptions_Factory_Register, OptionType
from .tool_ini import PySimpleINI_GroupKeysInSection
from .common_ut import GameOption_UT
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 PySimpleINI_UT2k4(PySimpleINI_GroupKeysInSection):
GroupRules = [
("Engine.GameEngine", "ServerPackages"),
("Engine.GameEngine", "ServerActors"),
("Core.System", "Suppress"),
("Core.System", "Paths"),
("Core.System", "Paths"),
("Editor.EditorEngine", "EditPackages"),
("Editor.EditorEngine", "CutdownPackages"),
]
class GameOption_UT2k4(GameOption):
class GameOption_UT2k4(GameOption_UT):
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
Cls_PySimpleINI = PySimpleINI_UT2k4
class GameOption_UT2k4_GenAdd(GameOption_UT2k4):
@@ -156,10 +122,10 @@ class GameOption_UT2k4_HostName(GameOption_UT2k4):
szDefaultValue = "ChaCha Test Server"
szHelp = "Server's HostName"
def set(self, value: str):
def set(self, value: str) -> None:
super().set(value)
self.inifile.setAddKeyValue("Engine.GameReplicationInfo", "ShortName", value)
self.inifile.writeFile()
self.inifile.setaddkeyvalue("Engine.GameReplicationInfo", "ShortName", value)
self.inifile.writefile()
@GameOptions_Factory_Register
@@ -201,12 +167,12 @@ class GameOption_UT2k4_HTTPDownloadServer(GameOption_UT2k4):
szDefaultValue = "http://chacha.ddns.net/games/ut2k4"
szHelp = "FastDL url"
def set(self, value: str):
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()
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
@@ -238,10 +204,10 @@ class GameOption_UT2k4_NetServerMaxTickRate(GameOption_UT2k4):
szDefaultValue = "60"
szHelp = "Server Max TickRate"
def set(self, value: str):
def set(self, value: str) -> None:
super().set(value)
self.inifile.setAddKeyValue("Engine.DemoRecDrive", "NetServerMaxTickRate", value)
self.inifile.writeFile()
self.inifile.setaddkeyvalue("Engine.DemoRecDrive", "NetServerMaxTickRate", value)
self.inifile.writefile()
@GameOptions_Factory_Register
@@ -253,10 +219,10 @@ class GameOption_UT2k4_LanServerMaxTickRate(GameOption_UT2k4):
szDefaultValue = "60"
szHelp = "Lan Server Max TickRate"
def set(self, value: str):
def set(self, value: str) -> None:
super().set(value)
self.inifile.setAddKeyValue("Engine.DemoRecDrive", "LanServerMaxTickRate", value)
self.inifile.writeFile()
self.inifile.setaddkeyvalue("Engine.DemoRecDrive", "LanServerMaxTickRate", value)
self.inifile.writefile()
@GameOptions_Factory_Register
@@ -731,7 +697,7 @@ class GameOption_UT2k4_WebServer(GameOption_UT2k4):
def set(self, value: str) -> None:
super().set(value)
if int(value) > 0:
self.inifile.setAddKeyValue("UWeb.WebServer", "bEnabled", "True")
self.inifile.setaddkeyvalue("UWeb.WebServer", "bEnabled", "True")
else:
self.inifile.setAddKeyValue("UWeb.WebServer", "bEnabled", "False")
self.inifile.writeFile()
self.inifile.setaddkeyvalue("UWeb.WebServer", "bEnabled", "False")
self.inifile.writefile()

View File

@@ -1,75 +1,40 @@
#!/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 <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
# pylint: disable=missing-class-docstring,missing-function-docstring,duplicate-code
"""UT99 command set"""
from __future__ import annotations
from typing import Union
from pysimpleini import PySimpleINI, KeyNotFoundError, SectionNotFoundError, Section, Key
from os.path import join
from pysimpleini import KeyNotFoundError, SectionNotFoundError
from .core import GameOptions_Factory_Register, GameOption, OptionType
from .core_gamecfg import GameOptions_Factory_Register, OptionType
from .tool_ini import PySimpleINI_GroupKeysInSection
from .common_ut import GameOption_UT
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 PySimpleINI_UT99(PySimpleINI_GroupKeysInSection):
GroupRules = [
("XC_Engine.XC_GameEngine", "ServerPackages"),
("XC_Engine.XC_GameEngine", "ServerActors"),
("Engine.GameEngine", "ServerPackages"),
("Engine.GameEngine", "ServerActors"),
("Core.System", "Suppress"),
("Editor.EditorEngine", "EditPackages"),
]
class GameOption_UT99(GameOption):
class GameOption_UT99(GameOption_UT):
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
Cls_PySimpleINI = PySimpleINI_UT99
class GameOption_UT99_GenAdd(GameOption_UT99):
@@ -96,7 +61,7 @@ class GameOption_UT99_GenAdd__Engine(GameOption_UT99_GenAdd):
prev = self.szSectionName
try:
self.inifile.getSection("XC_Engine.XC_GameEngine")
self.inifile.getsection("XC_Engine.XC_GameEngine")
self.szSectionName = "XC_Engine.XC_GameEngine"
super().set(value)
except SectionNotFoundError:
@@ -110,7 +75,7 @@ class GameOption_UT99_GenAdd__Engine(GameOption_UT99_GenAdd):
prev = self.szSectionName
try:
self.inifile.getSection("XC_Engine.XC_GameEngine")
self.inifile.getsection("XC_Engine.XC_GameEngine")
self.szSectionName = "XC_Engine.XC_GameEngine"
super().rem(value)
except SectionNotFoundError:
@@ -175,8 +140,8 @@ class GameOption_UT99_HostName(GameOption_UT99):
def set(self, value: str) -> None:
super().set(value)
self.inifile.setAddKeyValue("Engine.GameReplicationInfo", "ShortName", value)
self.inifile.writeFile()
self.inifile.setaddkeyvalue("Engine.GameReplicationInfo", "ShortName", value)
self.inifile.writefile()
@GameOptions_Factory_Register
@@ -240,8 +205,8 @@ class GameOption_UT99_AdminName(GameOption_UT99):
def set(self, value: str) -> None:
super().set(value)
self.inifile.setAddKeyValue("UTServerAdmin.UTServerAdmin", "AdminUsername", value)
self.inifile.writeFile()
self.inifile.setaddkeyvalue("UTServerAdmin.UTServerAdmin", "AdminUsername", value)
self.inifile.writefile()
@GameOptions_Factory_Register
@@ -255,10 +220,10 @@ class GameOption_UT99_HTTPDownloadServer(GameOption_UT99):
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()
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
@@ -292,8 +257,8 @@ class GameOption_UT99_NetServerMaxTickRate(GameOption_UT99):
def set(self, value: str) -> None:
super().set(value)
self.inifile.setAddKeyValue("Engine.DemoRecDriver", "NetServerMaxTickRate", value)
self.inifile.writeFile()
self.inifile.setaddkeyvalue("Engine.DemoRecDriver", "NetServerMaxTickRate", value)
self.inifile.writefile()
@GameOptions_Factory_Register
@@ -307,8 +272,8 @@ class GameOption_UT99_LanServerMaxTickRate(GameOption_UT99):
def set(self, value: str) -> None:
super().set(value)
self.inifile.setAddKeyValue("Engine.DemoRecDriver", "LanServerMaxTickRate", value)
self.inifile.writeFile()
self.inifile.setaddkeyvalue("Engine.DemoRecDriver", "LanServerMaxTickRate", value)
self.inifile.writefile()
@GameOptions_Factory_Register
@@ -322,8 +287,19 @@ class GameOption_UT99_AdminPassword(GameOption_UT99):
def set(self, value: str) -> None:
super().set(value)
self.inifile.setAddKeyValue("UTServerAdmin.UTServerAdmin", "AdminPassword", value)
self.inifile.writeFile()
self.inifile.setaddkeyvalue("UTServerAdmin.UTServerAdmin", "AdminPassword", value)
self.inifile.writefile()
def get(self) -> Union[str, list[str]]:
try:
return super().get()
except KeyNotFoundError:
return self.inifile.getkeyvalue(
"UTServerAdmin.UTServerAdmin",
"AdminPassword",
)
raise NotImplementedError("method not implemented")
@GameOptions_Factory_Register
@@ -448,15 +424,15 @@ class GameOption_UT99_WebServer(GameOption_UT99):
def set(self, value: str) -> None:
# fix ut99 v469c
try:
self.inifile.delKey("UWeb.WebServer", "Listenport")
except:
self.inifile.delkey("UWeb.WebServer", "Listenport")
except KeyNotFoundError:
pass
super().set(value)
if int(value) > 0:
self.inifile.setAddKeyValue("UWeb.WebServer", "bEnabled", "True")
self.inifile.setaddkeyvalue("UWeb.WebServer", "bEnabled", "True")
else:
self.inifile.setAddKeyValue("UWeb.WebServer", "bEnabled", "False")
self.inifile.writeFile()
self.inifile.setaddkeyvalue("UWeb.WebServer", "bEnabled", "False")
self.inifile.writefile()
@GameOptions_Factory_Register

1
src/pygamecfg/py.typed Normal file
View File

@@ -0,0 +1 @@
# PlaceHolder

44
src/pygamecfg/tool_ini.py Normal file
View File

@@ -0,0 +1,44 @@
#!/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 <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
"""utility module that contain PySimpleINI based helpers"""
from __future__ import annotations
from typing import Union
from pysimpleini import PySimpleINI, SectionNotFoundError, Key, Section
class PySimpleINI_GroupKeysInSection(PySimpleINI):
"""a class base on PySimpleINI that allow user to force some key to be group together in a section"""
GroupRules: list[tuple[str, str]] = []
def groupkeysinsection(self, szSectionName: str, szKeyName: str) -> None:
"""internal function that actually group keys"""
try:
_section: list[Section] = self.getsection(szSectionName)
if len(_section) == 1:
ar_ServerPackages: Union[list[Key], Key] = _section[0].getkey(szKeyName)
if isinstance(ar_ServerPackages, Key):
ar_ServerPackages = [ar_ServerPackages]
else: # array
pass
for ServerPackages in ar_ServerPackages:
_section[0].delkey(ServerPackages.getname(), None, ServerPackages.getvalue())
for ServerPackages in ar_ServerPackages:
_section[0].setaddkeyvalue(ServerPackages.getname(), ServerPackages.getvalue(), True)
except SectionNotFoundError:
pass
def writefile(self, bBeautify: bool = False, bWipeComments: bool = False) -> None:
"""overload of the write function to call the group function before"""
for GroupRule in self.GroupRules:
self.groupkeysinsection(GroupRule[0], GroupRule[1])
super().writefile(bBeautify, bWipeComments)

View File

@@ -0,0 +1,520 @@
[URL]
Protocol=unreal
ProtocolDescription=Unreal Protocol
Name=Player
Map=Index.unr
LocalMap=DM-Codex.unr
Host=
Portal=
MapExt=unr
SaveExt=usa
Port=7777
Class=Botpack.TMale1
[FirstRun]
FirstRun=0
[PackageRemap]
UnrealShare=UnrealI
[Engine.Engine]
GameRenderDevice=SoftDrv.SoftwareRenderDevice
AudioDevice=Galaxy.GalaxyAudioSubsystem
NetworkDevice=IpDrv.TcpNetDriver
DemoRecordingDevice=Engine.DemoRecDriver
Console=UTMenu.UTConsole
Language=int
GameEngine=Engine.GameEngine
EditorEngine=Editor.EditorEngine
WindowedRenderDevice=SoftDrv.SoftwareRenderDevice
RenderDevice=GlideDrv.GlideRenderDevice
DefaultGame=Botpack.DeathMatchPlus
DefaultServerGame=Botpack.DeathMatchPlus
ViewportManager=WinDrv.WindowsClient
Render=Render.Render
Input=Engine.Input
Canvas=Engine.Canvas
[Core.System]
PurgeCacheDays=30
SavePath=../Save
CachePath=../Cache
CacheExt=.uxx
Paths=../System/*.u
Paths=../Maps/*.unr
Paths=../Textures/*.utx
Paths=../Sounds/*.uax
Paths=../Music/*.umx
Suppress=DevLoad
Suppress=DevSave
Suppress=DevNetTraffic
Suppress=DevGarbage
Suppress=DevKill
Suppress=DevReplace
Suppress=DevSound
Suppress=DevCompile
Suppress=DevBind
Suppress=DevBsp
[Engine.GameEngine]
CacheSizeMegs=4
UseSound=True
ServerActors=IpDrv.UdpBeacon
ServerActors=IpServer.UdpServerQuery
ServerActors=IpServer.UdpServerUplink MasterServerAddress=unreal.epicgames.com MasterServerPort=27900
ServerActors=IpServer.UdpServerUplink MasterServerAddress=master0.gamespy.com MasterServerPort=27900
ServerActors=IpServer.UdpServerUplink MasterServerAddress=master.mplayer.com MasterServerPort=27900
ServerActors=UWeb.WebServer
ServerPackages=SoldierSkins
ServerPackages=CommandoSkins
ServerPackages=FCommandoSkins
ServerPackages=SGirlSkins
ServerPackages=BossSkins
ServerPackages=Botpack
[WinDrv.WindowsClient]
WindowedViewportX=640
WindowedViewportY=480
WindowedColorBits=16
FullscreenViewportX=640
FullscreenViewportY=480
FullscreenColorBits=16
Brightness=0.500000
MipFactor=1.000000
UseDirectDraw=True
UseJoystick=False
CaptureMouse=True
StartupFullscreen=True
CurvedSurfaces=False
LowDetailTextures=False
ScreenFlashes=True
NoLighting=False
SlowVideoBuffering=True
DeadZoneXYZ=True
DeadZoneRUV=False
InvertVertical=False
ScaleXYZ=1000.0
ScaleRUV=2000.0
MinDesiredFrameRate=30.0
Decals=True
NoDynamicLights=False
[XDrv.XClient]
WindowedViewportX=640
WindowedViewportY=480
WindowedColorBits=16
FullscreenViewportX=640
FullscreenViewportY=480
FullscreenColorBits=16
Brightness=0.500000
MipFactor=1.000000
SlowVideoBuffering=False
StartupFullscreen=True
CurvedSurfaces=False
CaptureMouse=True
LowDetailTextures=False
ScreenFlashes=True
NoLighting=False
DeadZoneXYZ=True
DeadZoneRUV=False
InvertVertical=False
ScaleXYZ=1000.0
ScaleRUV=2000.0
MinDesiredFrameRate=30.0
[Engine.Player]
ConfiguredInternetSpeed=2600
ConfiguredLanSpeed=20000
[Audio.GenericAudioSubsystem]
UseFilter=True
UseSurround=False
UseStereo=True
UseCDMusic=False
UseDigitalMusic=False
UseSpatial=False
UseReverb=False
Use3dHardware=False
LowSoundQuality=False
ReverseStereo=False
Latency=40
OutputRate=22050Hz
Channels=16
MusicVolume=160
SoundVolume=200
AmbientFactor=0.7
[Galaxy.GalaxyAudioSubsystem]
UseDirectSound=True
UseFilter=True
UseSurround=False
UseStereo=True
UseCDMusic=False
UseDigitalMusic=True
UseSpatial=False
UseReverb=True
Use3dHardware=False
LowSoundQuality=False
ReverseStereo=False
Latency=40
OutputRate=22050Hz
EffectsChannels=16
DopplerSpeed=9000.000000
MusicVolume=160
SoundVolume=200
AmbientFactor=0.700000
[IpDrv.TcpNetDriver]
AllowDownloads=True
ConnectionTimeout=15.0
InitialConnectTimeout=120.0
AckTimeout=1.0
KeepAliveTime=0.2
MaxClientRate=20000
MaxDownloadSize=0
SimLatency=0
RelevantTimeout=5.0
SpawnPrioritySeconds=1.0
ServerTravelPause=4.0
NetServerMaxTickRate=20
LanServerMaxTickRate=35
DownloadManagers=IpDrv.HTTPDownload
DownloadManagers=Engine.ChannelDownload
[IpDrv.HTTPDownload]
RedirectToURL=http://uz.ut-files.com/
ProxyServerHost=
ProxyServerPort=3128
UseCompression=True
[Engine.DemoRecDriver]
DemoSpectatorClass=Botpack.CHSpectator
MaxClientRate=25000
ConnectionTimeout=15.0
InitialConnectTimeout=500.0
AckTimeout=1.0
KeepAliveTime=1.0
SimLatency=0
RelevantTimeout=5.0
SpawnPrioritySeconds=1.0
ServerTravelPause=4.0
NetServerMaxTickRate=60
LanServerMaxTickRate=60
[Engine.GameReplicationInfo]
ServerName=Alt Serv Name FULL
ShortName=Alt Serv Name SHORT
AdminName=TestAdminName
AdminEmail=TestAdminName@test.com
Region=0
MOTDLine1=TestMOTDLine1
MOTDLine2=TestMOTDLine2
MOTDLine3=TestMOTDLine3
MOTDLine4=TestMOTDLine4
[IpDrv.TcpipConnection]
SimPacketLoss=0
SimLatency=0
[IpServer.UdpServerQuery]
DoUplink=True
UpdateMinutes=1
MasterServerAddress=unreal.epicgames.com
MasterServerPort=27900
Region=0
[IpDrv.UdpBeacon]
DoBeacon=True
BeaconTime=0.50
BeaconTimeout=5.0
BeaconProduct=ut
[SoftDrv.SoftwareRenderDevice]
Translucency=True
VolumetricLighting=True
ShinySurfaces=False
Coronas=False
HighDetailActors=False
HighResTextureSmooth=True
LowResTextureSmooth=False
FastTranslucency=True
[GlideDrv.GlideRenderDevice]
Translucency=True
VolumetricLighting=True
ShinySurfaces=True
Coronas=True
HighDetailActors=True
DetailBias=-1.500000
RefreshRate=60Hz
DetailTextures=True
FastUglyRefresh=False
ScreenSmoothing=True
Resolution=Default
[MetalDrv.MetalRenderDevice]
Translucency=True
VolumetricLighting=True
ShinySurfaces=True
Coronas=True
HighDetailActors=True
DetailTextures=False
[OpenGLDrv.OpenGLRenderDevice]
Translucency=True
VolumetricLighting=False
ShinySurfaces=True
Coronas=True
HighDetailActors=True
DetailTextures=True
[D3DDrv.D3DRenderDevice]
Translucency=True
VolumetricLighting=False
ShinySurfaces=True
Coronas=True
HighDetailActors=True
UseMipmapping=True
UseTrilinear=False
UseMultitexture=True
UsePageFlipping=True
UsePalettes=True
UseFullscreen=True
UseGammaCorrection=True
DetailTextures=False
Use3dfx=False
UseTripleBuffering=True
UsePrecache=True
Use32BitTextures=False
[SglDrv.SglRenderDevice]
Translucency=True
VolumetricLighting=False
ShinySurfaces=False
Coronas=True
HighDetailActors=False
ColorDepth=16
DetailTextures=False
FastUglyRefresh=False
TextureDetailBias=Near
VertexLighting=False
[Editor.EditorEngine]
UseSound=True
CacheSizeMegs=6
GridEnabled=True
SnapVertices=True
SnapDistance=10.000000
GridSize=(X=16.000000,Y=16.000000,Z=16.000000)
RotGridEnabled=True
RotGridSize=(Pitch=1024,Yaw=1024,Roll=1024)
GameCommandLine=-log
FovAngleDegrees=90.000000
GodMode=True
AutoSave=False
AutoSaveTimeMinutes=5
AutoSaveIndex=6
C_WorldBox=(R=0,G=0,B=107,A=0)
C_GroundPlane=(R=0,G=0,B=63,A=0)
C_GroundHighlight=(R=0,G=0,B=127,A=0)
C_BrushWire=(R=255,G=63,B=63,A=0)
C_Pivot=(R=0,G=255,B=0,A=0)
C_Select=(R=0,G=0,B=127,A=0)
C_AddWire=(R=127,G=127,B=255,A=0)
C_SubtractWire=(R=255,G=192,B=63,A=0)
C_GreyWire=(R=163,G=163,B=163,A=0)
C_Invalid=(R=163,G=163,B=163,A=0)
C_ActorWire=(R=127,G=63,B=0,A=0)
C_ActorHiWire=(R=255,G=127,B=0,A=0)
C_White=(R=255,G=255,B=255,A=0)
C_SemiSolidWire=(R=127,G=255,B=0,A=0)
C_NonSolidWire=(R=63,G=192,B=32,A=0)
C_WireGridAxis=(R=119,G=119,B=119,A=0)
C_ActorArrow=(R=163,G=0,B=0,A=0)
C_ScaleBox=(R=151,G=67,B=11,A=0)
C_ScaleBoxHi=(R=223,G=149,B=157,A=0)
C_Mover=(R=255,G=0,B=255,A=0)
C_OrthoBackground=(R=163,G=163,B=163,A=0)
C_Current=(R=0,G=0,B=0,A=0)
C_BrushVertex=(R=0,G=0,B=0,A=0)
C_BrushSnap=(R=0,G=0,B=0,A=0)
C_Black=(R=0,G=0,B=0,A=0)
C_Mask=(R=0,G=0,B=0,A=0)
C_WireBackground=(R=0,G=0,B=0,A=0)
C_ZoneWire=(R=0,G=0,B=0,A=0)
EditPackages=Core
EditPackages=Engine
EditPackages=Editor
EditPackages=UWindow
EditPackages=Fire
EditPackages=IpDrv
EditPackages=UWeb
EditPackages=UBrowser
EditPackages=UnrealShare
EditPackages=UnrealI
EditPackages=UMenu
EditPackages=IpServer
EditPackages=Botpack
EditPackages=UTServerAdmin
EditPackages=UTMenu
EditPackages=UTBrowser
[UMenu.UnrealConsole]
RootWindow=UMenu.UMenuRootWindow
UWindowKey=IK_Esc
ShowDesktop=True
[UMenu.UMenuMenuBar]
ShowHelp=True
GameUMenuDefault="UTMenu.UTGameMenu"
MultiplayerUMenuDefault="UTMenu.UTMultiplayerMenu"
OptionsUMenuDefault="UTMenu.UTOptionsMenu"
[Botpack.ChallengeBotInfo]
Difficulty=1
[Botpack.DeathMatchPlus]
bNoviceMode=True
bHardCoreMode=True
bUseTranslocator=False
bCoopWeaponMode=False
bForceRespawn=True
TimeLimit=323
FragLimit=321
InitialBots=7
MinPlayers=11
bTournament=False
[Botpack.CTFGame]
bUseTranslocator=True
bCoopWeaponMode=True
GoalTeamScore=3
bForceRespawn=False
TimeLimit=223
[Botpack.Domination]
bDumbDown=True
bUseTranslocator=True
bCoopWeaponMode=True
GoalTeamScore=100
bForceRespawn=False
TimeLimit=423
[Botpack.Assault]
bUseTranslocator=False
bCoopWeaponMode=True
bForceRespawn=True
TimeLimit=123
[Botpack.TeamGamePlus]
bBalanceTeams=True
GoalTeamScore=30
bPlayersBalanceTeams=True
MaxTeamSize=4
[Engine.GameInfo]
bLowGore=False
bVeryLowGore=False
MaxSpectators=1
MaxPlayers=4
ServerLogName=server.log
bWorldLog=True
bBatchLocal=False
DemoBuild=0
DemoHasTuts=0
PlayerViewDelay=1.000000
PlayerSpeechDelay=0.300000
PlayerTauntDelay=2.000000
bLogAdminActions=False
LoginDelaySeconds=0.000000
MaxLoginAttempts=0
ActionToTake=DO_Nothing
IPPolicies[0]=ACCEPT,*
IPPolicies[1]=
GamePassword=TestPwd
[UnrealShare.UnrealGameOptionsMenu]
bCanModifyGore=True
[UBrowser.UBrowserMainClientWindow]
LANTabName=UBrowserLAN
ServerListNames[0]=UBrowserUT
ServerListNames[1]=UBrowserLAN
ServerListNames[2]=UBrowserPopulated
ServerListNames[3]=UBrowserDeathmatch
ServerListNames[4]=UBrowserTeamGames
ServerListNames[5]=UBrowserCTF
ServerListNames[6]=UBrowserDOM
ServerListNames[7]=UBrowserAS
ServerListNames[8]=UBrowserLMS
ServerListNames[9]=UBrowserAll
ServerListNames[10]=None
ServerListNames[11]=None
ServerListNames[12]=None
ServerListNames[13]=None
ServerListNames[14]=None
ServerListNames[15]=None
ServerListNames[16]=None
ServerListNames[17]=None
ServerListNames[18]=None
ServerListNames[19]=None
[UBrowserUT]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,bCompatibleServersOnly=True
[UBrowserLAN]
ListFactories[0]=UBrowser.UBrowserLocalFact,BeaconProduct=ut
URLAppend=?LAN
AutoRefreshTime=10
bNoAutoSort=True
[UBrowserPopulated]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,MinPlayers=1,bCompatibleServersOnly=True
[UBrowserDeathmatch]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=DeathMatchPlus,bCompatibleServersOnly=True
[UBrowserTeamGames]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=TeamGamePlus,bCompatibleServersOnly=True
[UBrowserCTF]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=CTFGame,bCompatibleServersOnly=True
[UBrowserDOM]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=Domination,bCompatibleServersOnly=True
[UBrowserAS]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=Assault,bCompatibleServersOnly=True
[UBrowserLMS]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=LastManStanding,bCompatibleServersOnly=True
[UBrowserAll]
ListFactories[0]=UBrowser.UBrowserGSpyFact,MasterServerAddress=unreal.epicgames.com,MasterServerTCPPort=28900,Region=0,GameName=ut
ListFactories[1]=UBrowser.UBrowserGSpyFact,MasterServerAddress=master0.gamespy.com,MasterServerTCPPort=28900,Region=0,GameName=ut
bHidden=True
bFallbackFactories=True
[UTMenu.UTMultiplayerMenu]
OnlineServices[0]=LOCALIZE,MPlayer
OnlineServices[1]=LOCALIZE,Heat
OnlineServices[2]=LOCALIZE,WON
[UWeb.WebServer]
Applications[0]=UTServerAdmin.UTServerAdmin
ApplicationPaths[0]=/ServerAdmin
Applications[1]=UTServerAdmin.UTImageServer
ApplicationPaths[1]=/images
DefaultApplication=0
bEnabled=True
ListenPort=9999
MaxConnections=30
[UBrowser.UBrowserHTTPClient]
ProxyServerAddress=
ProxyServerPort=
[UTServerAdmin.UTServerAdmin]
AdminUsername=TestAdminUser
AdminPassword=TestAdminPwd

View File

@@ -0,0 +1,520 @@
[URL]
Protocol=unreal
ProtocolDescription=Unreal Protocol
Name=Player
Map=Index.unr
LocalMap=DM-Codex.unr
Host=
Portal=
MapExt=unr
SaveExt=usa
Port=7777
Class=Botpack.TMale1
[FirstRun]
FirstRun=0
[PackageRemap]
UnrealShare=UnrealI
[Engine.Engine]
GameRenderDevice=SoftDrv.SoftwareRenderDevice
AudioDevice=Galaxy.GalaxyAudioSubsystem
NetworkDevice=IpDrv.TcpNetDriver
DemoRecordingDevice=Engine.DemoRecDriver
Console=UTMenu.UTConsole
Language=int
GameEngine=Engine.GameEngine
EditorEngine=Editor.EditorEngine
WindowedRenderDevice=SoftDrv.SoftwareRenderDevice
RenderDevice=GlideDrv.GlideRenderDevice
DefaultGame=Botpack.DeathMatchPlus
DefaultServerGame=Botpack.DeathMatchPlus
ViewportManager=WinDrv.WindowsClient
Render=Render.Render
Input=Engine.Input
Canvas=Engine.Canvas
[Core.System]
PurgeCacheDays=30
SavePath=../Save
CachePath=../Cache
CacheExt=.uxx
Paths=../System/*.u
Paths=../Maps/*.unr
Paths=../Textures/*.utx
Paths=../Sounds/*.uax
Paths=../Music/*.umx
Suppress=DevLoad
Suppress=DevSave
Suppress=DevNetTraffic
Suppress=DevGarbage
Suppress=DevKill
Suppress=DevReplace
Suppress=DevSound
Suppress=DevCompile
Suppress=DevBind
Suppress=DevBsp
[Engine.GameEngine]
CacheSizeMegs=4
UseSound=True
ServerActors=IpDrv.UdpBeacon
ServerActors=IpServer.UdpServerQuery
ServerActors=IpServer.UdpServerUplink MasterServerAddress=unreal.epicgames.com MasterServerPort=27900
ServerActors=IpServer.UdpServerUplink MasterServerAddress=master0.gamespy.com MasterServerPort=27900
ServerActors=IpServer.UdpServerUplink MasterServerAddress=master.mplayer.com MasterServerPort=27900
ServerActors=UWeb.WebServer
ServerPackages=SoldierSkins
ServerPackages=CommandoSkins
ServerPackages=FCommandoSkins
ServerPackages=SGirlSkins
ServerPackages=BossSkins
ServerPackages=Botpack
[WinDrv.WindowsClient]
WindowedViewportX=640
WindowedViewportY=480
WindowedColorBits=16
FullscreenViewportX=640
FullscreenViewportY=480
FullscreenColorBits=16
Brightness=0.500000
MipFactor=1.000000
UseDirectDraw=True
UseJoystick=False
CaptureMouse=True
StartupFullscreen=True
CurvedSurfaces=False
LowDetailTextures=False
ScreenFlashes=True
NoLighting=False
SlowVideoBuffering=True
DeadZoneXYZ=True
DeadZoneRUV=False
InvertVertical=False
ScaleXYZ=1000.0
ScaleRUV=2000.0
MinDesiredFrameRate=30.0
Decals=True
NoDynamicLights=False
[XDrv.XClient]
WindowedViewportX=640
WindowedViewportY=480
WindowedColorBits=16
FullscreenViewportX=640
FullscreenViewportY=480
FullscreenColorBits=16
Brightness=0.500000
MipFactor=1.000000
SlowVideoBuffering=False
StartupFullscreen=True
CurvedSurfaces=False
CaptureMouse=True
LowDetailTextures=False
ScreenFlashes=True
NoLighting=False
DeadZoneXYZ=True
DeadZoneRUV=False
InvertVertical=False
ScaleXYZ=1000.0
ScaleRUV=2000.0
MinDesiredFrameRate=30.0
[Engine.Player]
ConfiguredInternetSpeed=2600
ConfiguredLanSpeed=20000
[Audio.GenericAudioSubsystem]
UseFilter=True
UseSurround=False
UseStereo=True
UseCDMusic=False
UseDigitalMusic=False
UseSpatial=False
UseReverb=False
Use3dHardware=False
LowSoundQuality=False
ReverseStereo=False
Latency=40
OutputRate=22050Hz
Channels=16
MusicVolume=160
SoundVolume=200
AmbientFactor=0.7
[Galaxy.GalaxyAudioSubsystem]
UseDirectSound=True
UseFilter=True
UseSurround=False
UseStereo=True
UseCDMusic=False
UseDigitalMusic=True
UseSpatial=False
UseReverb=True
Use3dHardware=False
LowSoundQuality=False
ReverseStereo=False
Latency=40
OutputRate=22050Hz
EffectsChannels=16
DopplerSpeed=9000.000000
MusicVolume=160
SoundVolume=200
AmbientFactor=0.700000
[IpDrv.TcpNetDriver]
AllowDownloads=True
ConnectionTimeout=15.0
InitialConnectTimeout=120.0
AckTimeout=1.0
KeepAliveTime=0.2
MaxClientRate=20000
MaxDownloadSize=0
SimLatency=0
RelevantTimeout=5.0
SpawnPrioritySeconds=1.0
ServerTravelPause=4.0
NetServerMaxTickRate=20
LanServerMaxTickRate=35
DownloadManagers=IpDrv.HTTPDownload
DownloadManagers=Engine.ChannelDownload
[IpDrv.HTTPDownload]
RedirectToURL=http://uz.ut-files.com/
ProxyServerHost=
ProxyServerPort=3128
UseCompression=True
[Engine.DemoRecDriver]
DemoSpectatorClass=Botpack.CHSpectator
MaxClientRate=25000
ConnectionTimeout=15.0
InitialConnectTimeout=500.0
AckTimeout=1.0
KeepAliveTime=1.0
SimLatency=0
RelevantTimeout=5.0
SpawnPrioritySeconds=1.0
ServerTravelPause=4.0
NetServerMaxTickRate=60
LanServerMaxTickRate=60
[Engine.GameReplicationInfo]
ServerName=Test Server Name FULL
ShortName=Test Server Name SHORT
AdminName=TestAdminName
AdminEmail=TestAdminName@test.com
Region=0
MOTDLine1=TestMOTDLine1
MOTDLine2=TestMOTDLine2
MOTDLine3=TestMOTDLine3
MOTDLine4=TestMOTDLine4
[IpDrv.TcpipConnection]
SimPacketLoss=0
SimLatency=0
[IpServer.UdpServerQuery]
DoUplink=True
UpdateMinutes=1
MasterServerAddress=unreal.epicgames.com
MasterServerPort=27900
Region=0
[IpDrv.UdpBeacon]
DoBeacon=True
BeaconTime=0.50
BeaconTimeout=5.0
BeaconProduct=ut
[SoftDrv.SoftwareRenderDevice]
Translucency=True
VolumetricLighting=True
ShinySurfaces=False
Coronas=False
HighDetailActors=False
HighResTextureSmooth=True
LowResTextureSmooth=False
FastTranslucency=True
[GlideDrv.GlideRenderDevice]
Translucency=True
VolumetricLighting=True
ShinySurfaces=True
Coronas=True
HighDetailActors=True
DetailBias=-1.500000
RefreshRate=60Hz
DetailTextures=True
FastUglyRefresh=False
ScreenSmoothing=True
Resolution=Default
[MetalDrv.MetalRenderDevice]
Translucency=True
VolumetricLighting=True
ShinySurfaces=True
Coronas=True
HighDetailActors=True
DetailTextures=False
[OpenGLDrv.OpenGLRenderDevice]
Translucency=True
VolumetricLighting=False
ShinySurfaces=True
Coronas=True
HighDetailActors=True
DetailTextures=True
[D3DDrv.D3DRenderDevice]
Translucency=True
VolumetricLighting=False
ShinySurfaces=True
Coronas=True
HighDetailActors=True
UseMipmapping=True
UseTrilinear=False
UseMultitexture=True
UsePageFlipping=True
UsePalettes=True
UseFullscreen=True
UseGammaCorrection=True
DetailTextures=False
Use3dfx=False
UseTripleBuffering=True
UsePrecache=True
Use32BitTextures=False
[SglDrv.SglRenderDevice]
Translucency=True
VolumetricLighting=False
ShinySurfaces=False
Coronas=True
HighDetailActors=False
ColorDepth=16
DetailTextures=False
FastUglyRefresh=False
TextureDetailBias=Near
VertexLighting=False
[Editor.EditorEngine]
UseSound=True
CacheSizeMegs=6
GridEnabled=True
SnapVertices=True
SnapDistance=10.000000
GridSize=(X=16.000000,Y=16.000000,Z=16.000000)
RotGridEnabled=True
RotGridSize=(Pitch=1024,Yaw=1024,Roll=1024)
GameCommandLine=-log
FovAngleDegrees=90.000000
GodMode=True
AutoSave=False
AutoSaveTimeMinutes=5
AutoSaveIndex=6
C_WorldBox=(R=0,G=0,B=107,A=0)
C_GroundPlane=(R=0,G=0,B=63,A=0)
C_GroundHighlight=(R=0,G=0,B=127,A=0)
C_BrushWire=(R=255,G=63,B=63,A=0)
C_Pivot=(R=0,G=255,B=0,A=0)
C_Select=(R=0,G=0,B=127,A=0)
C_AddWire=(R=127,G=127,B=255,A=0)
C_SubtractWire=(R=255,G=192,B=63,A=0)
C_GreyWire=(R=163,G=163,B=163,A=0)
C_Invalid=(R=163,G=163,B=163,A=0)
C_ActorWire=(R=127,G=63,B=0,A=0)
C_ActorHiWire=(R=255,G=127,B=0,A=0)
C_White=(R=255,G=255,B=255,A=0)
C_SemiSolidWire=(R=127,G=255,B=0,A=0)
C_NonSolidWire=(R=63,G=192,B=32,A=0)
C_WireGridAxis=(R=119,G=119,B=119,A=0)
C_ActorArrow=(R=163,G=0,B=0,A=0)
C_ScaleBox=(R=151,G=67,B=11,A=0)
C_ScaleBoxHi=(R=223,G=149,B=157,A=0)
C_Mover=(R=255,G=0,B=255,A=0)
C_OrthoBackground=(R=163,G=163,B=163,A=0)
C_Current=(R=0,G=0,B=0,A=0)
C_BrushVertex=(R=0,G=0,B=0,A=0)
C_BrushSnap=(R=0,G=0,B=0,A=0)
C_Black=(R=0,G=0,B=0,A=0)
C_Mask=(R=0,G=0,B=0,A=0)
C_WireBackground=(R=0,G=0,B=0,A=0)
C_ZoneWire=(R=0,G=0,B=0,A=0)
EditPackages=Core
EditPackages=Engine
EditPackages=Editor
EditPackages=UWindow
EditPackages=Fire
EditPackages=IpDrv
EditPackages=UWeb
EditPackages=UBrowser
EditPackages=UnrealShare
EditPackages=UnrealI
EditPackages=UMenu
EditPackages=IpServer
EditPackages=Botpack
EditPackages=UTServerAdmin
EditPackages=UTMenu
EditPackages=UTBrowser
[UMenu.UnrealConsole]
RootWindow=UMenu.UMenuRootWindow
UWindowKey=IK_Esc
ShowDesktop=True
[UMenu.UMenuMenuBar]
ShowHelp=True
GameUMenuDefault="UTMenu.UTGameMenu"
MultiplayerUMenuDefault="UTMenu.UTMultiplayerMenu"
OptionsUMenuDefault="UTMenu.UTOptionsMenu"
[Botpack.ChallengeBotInfo]
Difficulty=1
[Botpack.DeathMatchPlus]
bNoviceMode=True
bHardCoreMode=True
bUseTranslocator=False
bCoopWeaponMode=False
bForceRespawn=True
TimeLimit=323
FragLimit=321
InitialBots=7
MinPlayers=11
bTournament=False
[Botpack.CTFGame]
bUseTranslocator=True
bCoopWeaponMode=True
GoalTeamScore=3
bForceRespawn=False
TimeLimit=223
[Botpack.Domination]
bDumbDown=True
bUseTranslocator=True
bCoopWeaponMode=True
GoalTeamScore=100
bForceRespawn=False
TimeLimit=423
[Botpack.Assault]
bUseTranslocator=False
bCoopWeaponMode=True
bForceRespawn=True
TimeLimit=123
[Botpack.TeamGamePlus]
bBalanceTeams=True
GoalTeamScore=30
bPlayersBalanceTeams=True
MaxTeamSize=4
[Engine.GameInfo]
bLowGore=False
bVeryLowGore=False
MaxSpectators=1
MaxPlayers=4
ServerLogName=server.log
bWorldLog=True
bBatchLocal=False
DemoBuild=0
DemoHasTuts=0
PlayerViewDelay=1.000000
PlayerSpeechDelay=0.300000
PlayerTauntDelay=2.000000
bLogAdminActions=False
LoginDelaySeconds=0.000000
MaxLoginAttempts=0
ActionToTake=DO_Nothing
IPPolicies[0]=ACCEPT,*
IPPolicies[1]=
GamePassword=TestPwd
[UnrealShare.UnrealGameOptionsMenu]
bCanModifyGore=True
[UBrowser.UBrowserMainClientWindow]
LANTabName=UBrowserLAN
ServerListNames[0]=UBrowserUT
ServerListNames[1]=UBrowserLAN
ServerListNames[2]=UBrowserPopulated
ServerListNames[3]=UBrowserDeathmatch
ServerListNames[4]=UBrowserTeamGames
ServerListNames[5]=UBrowserCTF
ServerListNames[6]=UBrowserDOM
ServerListNames[7]=UBrowserAS
ServerListNames[8]=UBrowserLMS
ServerListNames[9]=UBrowserAll
ServerListNames[10]=None
ServerListNames[11]=None
ServerListNames[12]=None
ServerListNames[13]=None
ServerListNames[14]=None
ServerListNames[15]=None
ServerListNames[16]=None
ServerListNames[17]=None
ServerListNames[18]=None
ServerListNames[19]=None
[UBrowserUT]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,bCompatibleServersOnly=True
[UBrowserLAN]
ListFactories[0]=UBrowser.UBrowserLocalFact,BeaconProduct=ut
URLAppend=?LAN
AutoRefreshTime=10
bNoAutoSort=True
[UBrowserPopulated]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,MinPlayers=1,bCompatibleServersOnly=True
[UBrowserDeathmatch]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=DeathMatchPlus,bCompatibleServersOnly=True
[UBrowserTeamGames]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=TeamGamePlus,bCompatibleServersOnly=True
[UBrowserCTF]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=CTFGame,bCompatibleServersOnly=True
[UBrowserDOM]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=Domination,bCompatibleServersOnly=True
[UBrowserAS]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=Assault,bCompatibleServersOnly=True
[UBrowserLMS]
ListFactories[0]=UBrowser.UBrowserSubsetFact,SupersetTag=UBrowserAll,GameType=LastManStanding,bCompatibleServersOnly=True
[UBrowserAll]
ListFactories[0]=UBrowser.UBrowserGSpyFact,MasterServerAddress=unreal.epicgames.com,MasterServerTCPPort=28900,Region=0,GameName=ut
ListFactories[1]=UBrowser.UBrowserGSpyFact,MasterServerAddress=master0.gamespy.com,MasterServerTCPPort=28900,Region=0,GameName=ut
bHidden=True
bFallbackFactories=True
[UTMenu.UTMultiplayerMenu]
OnlineServices[0]=LOCALIZE,MPlayer
OnlineServices[1]=LOCALIZE,Heat
OnlineServices[2]=LOCALIZE,WON
[UWeb.WebServer]
Applications[0]=UTServerAdmin.UTServerAdmin
ApplicationPaths[0]=/ServerAdmin
Applications[1]=UTServerAdmin.UTImageServer
ApplicationPaths[1]=/images
DefaultApplication=0
bEnabled=True
ListenPort=9999
MaxConnections=30
[UBrowser.UBrowserHTTPClient]
ProxyServerAddress=
ProxyServerPort=
[UTServerAdmin.UTServerAdmin]
AdminUsername=TestAdminUser
AdminPassword=TestAdminPwd

30
test/test_gen.py Normal file
View File

@@ -0,0 +1,30 @@
# 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 <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
import unittest
from io import StringIO
from contextlib import redirect_stdout, redirect_stderr
from src import pygamecfg
from src.pygamecfg.__main__ import fct_main
class Testtest_gen(unittest.TestCase):
def test_version(self):
self.assertNotEqual(pygamecfg.__version__, "?.?.?")
def test_normal_help(self):
with self.assertRaises(SystemExit) as cm:
fct_main(["-h"])
def test_defect_nogame(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
with self.assertRaises(SystemExit) as cm:
fct_main(["GetOption", "test"])
self.assertIn("pygamecfg: error: the following arguments are required: -g/--game", capted_stderr.getvalue())
self.assertIn("", capted_stdout.getvalue())

View File

@@ -7,26 +7,303 @@
# work. If not, see <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
import unittest
from io import StringIO
from contextlib import redirect_stdout,redirect_stderr
print(__name__)
print(__package__)
from os import linesep
from io import StringIO
from contextlib import redirect_stdout, redirect_stderr
from src import pygamecfg
from src.pygamecfg.__main__ import fct_main
class Testtest_module(unittest.TestCase):
def test_version(self):
self.assertNotEqual(pygamecfg.__version__,"?.?.?")
def test_test_module(self):
class Testtest_ut99(unittest.TestCase):
def test_normal_ServerPackages(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
self.assertEqual(pygamecfg.test_function(41),42)
self.assertEqual(len(capted_stderr.getvalue()),0)
self.assertEqual(capted_stdout.getvalue().strip(),"Hello world !")
self.assertEqual(len(capted_stderr.getvalue()),0)
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "ServerPackages"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual(
"['SoldierSkins', 'CommandoSkins', 'FCommandoSkins', 'SGirlSkins', 'BossSkins', 'Botpack']\n", capted_stdout.getvalue()
)
self.assertEqual("", capted_stderr.getvalue())
def test_normal_ServerActors(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "ServerActors"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual(
"['IpDrv.UdpBeacon', 'IpServer.UdpServerQuery', 'IpServer.UdpServerUplink MasterServerAddress=unreal.epicgames.com MasterServerPort=27900', 'IpServer.UdpServerUplink MasterServerAddress=master0.gamespy.com MasterServerPort=27900', 'IpServer.UdpServerUplink MasterServerAddress=master.mplayer.com MasterServerPort=27900', 'UWeb.WebServer']\n",
capted_stdout.getvalue(),
)
self.assertEqual("", capted_stderr.getvalue())
def test_normal_Port(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "Port"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("7777\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_Map(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "Map"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("Index.unr\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_GameType(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "GameType"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("Botpack.DeathMatchPlus\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_HostName(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "HostName"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("Test Server Name FULL\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_MOTD(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "MOTD"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("TestMOTDLine1\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_MOTD2(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "MOTD2"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("TestMOTDLine2\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_MOTD3(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "MOTD3"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("TestMOTDLine3\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_MOTD4(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "MOTD4"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("TestMOTDLine4\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_AdminEmail(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "AdminEmail"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("TestAdminName@test.com\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_AdminName(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "AdminName"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("TestAdminName\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_HTTPDownloadServer(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "HTTPDownloadServer"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("http://uz.ut-files.com/\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_MaxClientRate(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "MaxClientRate"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("20000\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_NetServerMaxTickRate(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "NetServerMaxTickRate"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("20\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_LanServerMaxTickRate(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "LanServerMaxTickRate"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("35\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_AdminPassword(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "AdminPassword"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("TestAdminPwd\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_GamePassword(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "GamePassword"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("TestPwd\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_MaxPlayers(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "MaxPlayers"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("4\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_MaxSpectators(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "MaxSpectators"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("1\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_AS_TimeLimit(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "AS_TimeLimit"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("123\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_DOM_TimeLimit(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "DOM_TimeLimit"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("423\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_CTF_TimeLimit(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "CTF_TimeLimit"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("223\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_DM_TimeLimit(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "DM_TimeLimit"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("323\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_GoalTeamScore(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "GoalTeamScore"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("30\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_MaxTeamSize(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "MaxTeamSize"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("4\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_DM_FragLimit(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "DM_FragLimit"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("321\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_ServerLogName(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "ServerLogName"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("server.log\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_WebServer(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "WebServer"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("9999\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_TournamentMode(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "TournamentMode"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("False\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_InitialBots(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "InitialBots"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("7\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_MinPlayers(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "MinPlayers"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("11\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_AS_UseTranslocator(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "AS_UseTranslocator"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("True\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_CTF_UseTranslocator(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "CTF_UseTranslocator"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("True\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_DM_UseTranslocator(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "DM_UseTranslocator"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("False\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_DOM_UseTranslocator(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "DOM_UseTranslocator"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("True\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_CTF_ForceRespawn(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "CTF_ForceRespawn"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("False\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_DM_ForceRespawn(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "DM_ForceRespawn"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("True\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_DOM_ForceRespawn(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "DOM_ForceRespawn"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("False\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_AS_ForceRespawn(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "GetOption", "AS_ForceRespawn"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("True\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())
def test_normal_customcfgfile_HostName(self):
with redirect_stdout(StringIO()) as capted_stdout, redirect_stderr(StringIO()) as capted_stderr:
fct_main(["-g", "ut99", "-b", "test/data/UT99", "-c", "System/UT99.ini", "GetOption", "HostName"])
# /!\ add '\n' at the end of the string cause Python terminal newline is always this, regardless Windows / Linux os.linesep
self.assertEqual("Alt Serv Name FULL\n", capted_stdout.getvalue())
self.assertEqual("", capted_stderr.getvalue())