mirror of
https://git.zavage.net/Zavage-Software/smileyface.git
synced 2024-12-22 20:59:21 -07:00
Merge branch 'master' of git.zavage.net:Zavage-Software/smileyface
This commit is contained in:
commit
ff06cc59eb
11
.flake8
Normal file
11
.flake8
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[flake8]
|
||||||
|
max-line-length=120
|
||||||
|
ignore =
|
||||||
|
E121,
|
||||||
|
E123,
|
||||||
|
E126,
|
||||||
|
E226,
|
||||||
|
E24,
|
||||||
|
E704,
|
||||||
|
W605
|
||||||
|
exclude = ./tests
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,4 +3,5 @@ build/
|
|||||||
dist/
|
dist/
|
||||||
__pycache__
|
__pycache__
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
idea
|
||||||
|
|
||||||
|
37
.pre-commit-config.yaml
Normal file
37
.pre-commit-config.yaml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v4.5.0
|
||||||
|
hooks:
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-added-large-files
|
||||||
|
args: ['--maxkb=10240']
|
||||||
|
- id: check-merge-conflict
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: trailing-whitespace
|
||||||
|
|
||||||
|
# isort -- sorts imports
|
||||||
|
- repo: https://github.com/timothycrosley/isort
|
||||||
|
rev: 5.13.2
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
args: ["--profile", "black", "--filter-files"]
|
||||||
|
|
||||||
|
# Flake8
|
||||||
|
#- repo: https://github.com/pycqa/flake8
|
||||||
|
# rev: '7.0.0'
|
||||||
|
# hooks:
|
||||||
|
# - id: flake8
|
||||||
|
|
||||||
|
# Black
|
||||||
|
# Using this mirror lets us use mypyc-compiled black, which is about 2x faster
|
||||||
|
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||||
|
rev: 24.2.0
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
language_version: python3.8
|
||||||
|
|
||||||
|
# Poetry
|
||||||
|
- repo: https://github.com/python-poetry/poetry
|
||||||
|
rev: 1.8.2
|
||||||
|
hooks:
|
||||||
|
- id: poetry-lock
|
1
.python-version
Normal file
1
.python-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
3.8.19
|
@ -1,6 +1,6 @@
|
|||||||
SmileyFace Unreal Tournament 4 Hub Automator
|
SmileyFace Unreal Tournament 4 Hub Automator
|
||||||
|
|
||||||
Copyright (c) 2020 Mathew Guest
|
Copyright (c) 2024 Mathew Guest
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person
|
Permission is hereby granted, free of charge, to any person
|
||||||
obtaining a copy of this software and associated documentation
|
obtaining a copy of this software and associated documentation
|
||||||
|
440
poetry.lock
generated
Normal file
440
poetry.lock
generated
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "app-skellington"
|
||||||
|
version = "0.1.1"
|
||||||
|
description = "A high-powered command line menu framework."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3"
|
||||||
|
files = [
|
||||||
|
{file = "app_skellington-0.1.1-py3-none-any.whl", hash = "sha256:61853a63b6683a3bccbca87c34d19352c1c5c4ab80fb4ff5fa77ebc0cf3348af"},
|
||||||
|
{file = "app_skellington-0.1.1.tar.gz", hash = "sha256:d9ea7423f8e9434724065e528f89b35182abe9b338e6997397d46e98e7f87a0a"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
appdirs = "*"
|
||||||
|
colorlog = "*"
|
||||||
|
configobj = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "appdirs"
|
||||||
|
version = "1.4.4"
|
||||||
|
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
|
||||||
|
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "black"
|
||||||
|
version = "24.4.2"
|
||||||
|
description = "The uncompromising code formatter."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"},
|
||||||
|
{file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"},
|
||||||
|
{file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"},
|
||||||
|
{file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"},
|
||||||
|
{file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"},
|
||||||
|
{file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"},
|
||||||
|
{file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"},
|
||||||
|
{file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"},
|
||||||
|
{file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"},
|
||||||
|
{file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"},
|
||||||
|
{file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"},
|
||||||
|
{file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"},
|
||||||
|
{file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"},
|
||||||
|
{file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"},
|
||||||
|
{file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"},
|
||||||
|
{file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"},
|
||||||
|
{file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"},
|
||||||
|
{file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"},
|
||||||
|
{file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"},
|
||||||
|
{file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"},
|
||||||
|
{file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"},
|
||||||
|
{file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
click = ">=8.0.0"
|
||||||
|
mypy-extensions = ">=0.4.3"
|
||||||
|
packaging = ">=22.0"
|
||||||
|
pathspec = ">=0.9.0"
|
||||||
|
platformdirs = ">=2"
|
||||||
|
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||||
|
typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
colorama = ["colorama (>=0.4.3)"]
|
||||||
|
d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
|
||||||
|
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||||
|
uvloop = ["uvloop (>=0.15.2)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfgv"
|
||||||
|
version = "3.4.0"
|
||||||
|
description = "Validate configuration and produce human readable error messages."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
|
||||||
|
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click"
|
||||||
|
version = "8.1.7"
|
||||||
|
description = "Composable command line interface toolkit"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
|
||||||
|
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorama"
|
||||||
|
version = "0.4.6"
|
||||||
|
description = "Cross-platform colored terminal text."
|
||||||
|
optional = false
|
||||||
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||||
|
files = [
|
||||||
|
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||||
|
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorlog"
|
||||||
|
version = "6.8.2"
|
||||||
|
description = "Add colours to the output of Python's logging module."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "colorlog-6.8.2-py3-none-any.whl", hash = "sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33"},
|
||||||
|
{file = "colorlog-6.8.2.tar.gz", hash = "sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
development = ["black", "flake8", "mypy", "pytest", "types-colorama"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "configobj"
|
||||||
|
version = "5.0.8"
|
||||||
|
description = "Config file reading, writing and validation."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
|
files = [
|
||||||
|
{file = "configobj-5.0.8-py2.py3-none-any.whl", hash = "sha256:a7a8c6ab7daade85c3f329931a807c8aee750a2494363934f8ea84d8a54c87ea"},
|
||||||
|
{file = "configobj-5.0.8.tar.gz", hash = "sha256:6f704434a07dc4f4dc7c9a745172c1cad449feb548febd9f7fe362629c627a97"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
six = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "distlib"
|
||||||
|
version = "0.3.8"
|
||||||
|
description = "Distribution utilities"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
|
||||||
|
{file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filelock"
|
||||||
|
version = "3.15.4"
|
||||||
|
description = "A platform independent file lock."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"},
|
||||||
|
{file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||||
|
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"]
|
||||||
|
typing = ["typing-extensions (>=4.8)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flake8"
|
||||||
|
version = "5.0.4"
|
||||||
|
description = "the modular source code checker: pep8 pyflakes and co"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6.1"
|
||||||
|
files = [
|
||||||
|
{file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
|
||||||
|
{file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
mccabe = ">=0.7.0,<0.8.0"
|
||||||
|
pycodestyle = ">=2.9.0,<2.10.0"
|
||||||
|
pyflakes = ">=2.5.0,<2.6.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "identify"
|
||||||
|
version = "2.6.0"
|
||||||
|
description = "File identification library for Python"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"},
|
||||||
|
{file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
license = ["ukkonen"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "isort"
|
||||||
|
version = "5.13.2"
|
||||||
|
description = "A Python utility / library to sort Python imports."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8.0"
|
||||||
|
files = [
|
||||||
|
{file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
|
||||||
|
{file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
colors = ["colorama (>=0.4.6)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mccabe"
|
||||||
|
version = "0.7.0"
|
||||||
|
description = "McCabe checker, plugin for flake8"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
||||||
|
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mypy-extensions"
|
||||||
|
version = "1.0.0"
|
||||||
|
description = "Type system extensions for programs checked with the mypy type checker."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5"
|
||||||
|
files = [
|
||||||
|
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||||
|
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nodeenv"
|
||||||
|
version = "1.9.1"
|
||||||
|
description = "Node.js virtual environment builder"
|
||||||
|
optional = false
|
||||||
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||||
|
files = [
|
||||||
|
{file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
|
||||||
|
{file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "24.1"
|
||||||
|
description = "Core utilities for Python packages"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
|
||||||
|
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathspec"
|
||||||
|
version = "0.12.1"
|
||||||
|
description = "Utility library for gitignore style pattern matching of file paths."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||||
|
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platformdirs"
|
||||||
|
version = "4.2.2"
|
||||||
|
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
|
||||||
|
{file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||||
|
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
|
||||||
|
type = ["mypy (>=1.8)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pre-commit"
|
||||||
|
version = "3.5.0"
|
||||||
|
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"},
|
||||||
|
{file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
cfgv = ">=2.0.0"
|
||||||
|
identify = ">=1.0.0"
|
||||||
|
nodeenv = ">=0.11.1"
|
||||||
|
pyyaml = ">=5.1"
|
||||||
|
virtualenv = ">=20.10.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pycodestyle"
|
||||||
|
version = "2.9.1"
|
||||||
|
description = "Python style guide checker"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
|
||||||
|
{file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyflakes"
|
||||||
|
version = "2.5.0"
|
||||||
|
description = "passive checker of Python programs"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"},
|
||||||
|
{file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyyaml"
|
||||||
|
version = "6.0.1"
|
||||||
|
description = "YAML parser and emitter for Python"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
|
||||||
|
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
|
||||||
|
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
|
||||||
|
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
|
||||||
|
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
|
||||||
|
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
|
||||||
|
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
|
||||||
|
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
|
||||||
|
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
|
||||||
|
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
|
||||||
|
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
|
||||||
|
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
|
||||||
|
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
|
||||||
|
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
|
||||||
|
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
|
||||||
|
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||||
|
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
|
||||||
|
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
|
||||||
|
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
|
||||||
|
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
|
||||||
|
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
|
||||||
|
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
|
||||||
|
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
|
||||||
|
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
|
||||||
|
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
|
||||||
|
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
|
||||||
|
{file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
|
||||||
|
{file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
|
||||||
|
{file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
|
||||||
|
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
|
||||||
|
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
|
||||||
|
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
|
||||||
|
{file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
|
||||||
|
{file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
|
||||||
|
{file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
|
||||||
|
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
|
||||||
|
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
|
||||||
|
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
|
||||||
|
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
|
||||||
|
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
|
||||||
|
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
|
||||||
|
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
|
||||||
|
{file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
|
||||||
|
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
|
||||||
|
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
|
||||||
|
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
|
||||||
|
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
|
||||||
|
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
|
||||||
|
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
|
||||||
|
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "six"
|
||||||
|
version = "1.16.0"
|
||||||
|
description = "Python 2 and 3 compatibility utilities"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||||
|
files = [
|
||||||
|
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||||
|
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tomli"
|
||||||
|
version = "2.0.1"
|
||||||
|
description = "A lil' TOML parser"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||||
|
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typing-extensions"
|
||||||
|
version = "4.12.2"
|
||||||
|
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
|
||||||
|
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtualenv"
|
||||||
|
version = "20.26.3"
|
||||||
|
description = "Virtual Python Environment builder"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"},
|
||||||
|
{file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
distlib = ">=0.3.7,<1"
|
||||||
|
filelock = ">=3.12.2,<4"
|
||||||
|
platformdirs = ">=3.9.1,<5"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
|
||||||
|
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "2.0"
|
||||||
|
python-versions = "^3.8"
|
||||||
|
content-hash = "c2001f64c789fcc98f1fc1702a4a511e788776a0c7d4c438d66193f0a1241c0c"
|
51
pyproject.toml
Normal file
51
pyproject.toml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
[tool.poetry]
|
||||||
|
name = "smileyface"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "smileyface UT4 hub automator hosting"
|
||||||
|
authors = [
|
||||||
|
"Mathew Guest <mat@zavage.net>",
|
||||||
|
]
|
||||||
|
license = "MIT"
|
||||||
|
readme = "README.md"
|
||||||
|
homepage = "https://zavage-software.com/portfolio/smileyface"
|
||||||
|
repository = "https://git-mirror.zavage.net/zavage-software/smileyface"
|
||||||
|
documentation = "https://git-mirror.zavage.net/zavage-software/smileyface"
|
||||||
|
keywords = ["cas"]
|
||||||
|
|
||||||
|
packages = [{ include = "smileyface" }]
|
||||||
|
include = [
|
||||||
|
"README.md",
|
||||||
|
]
|
||||||
|
|
||||||
|
# [tool.poetry.scripts]
|
||||||
|
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.8"
|
||||||
|
app_skellington = "*"
|
||||||
|
configobj = "*"
|
||||||
|
colorlog = "*"
|
||||||
|
appdirs = "*"
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
black = "*"
|
||||||
|
pre-commit = "*"
|
||||||
|
isort = "*"
|
||||||
|
flake8 = "*"
|
||||||
|
#Sphinx = "^5.3.0"
|
||||||
|
#sphinx-rtd-theme = "^1.3.0"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line-length = 120
|
||||||
|
target-version = ['py38']
|
||||||
|
|
||||||
|
[tool.isort]
|
||||||
|
multi_line_output = 3
|
||||||
|
combine_as_imports = true
|
||||||
|
include_trailing_comma = true
|
||||||
|
force_grid_wrap = 3
|
||||||
|
ensure_newline_before_comments = true
|
40
setup.py
40
setup.py
@ -1,35 +1,25 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from setuptools import setup, find_packages
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
__project__ = 'SmileyFace UT4 Hub Automator'
|
__project__ = "SmileyFace UT4 Hub Automator"
|
||||||
__version__ = '0.1.0'
|
__version__ = "0.1.0"
|
||||||
|
|
||||||
app_skellington_requirements = (
|
app_skellington_requirements = (
|
||||||
'appdirs',
|
"appdirs",
|
||||||
'colorlog',
|
"colorlog",
|
||||||
'configobj',
|
"configobj",
|
||||||
)
|
)
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = __project__,
|
name=__project__,
|
||||||
version = __version__,
|
version=__version__,
|
||||||
description = 'Unreal Tournament 4 Server Admin and Control Panel',
|
description="Unreal Tournament 4 Server Admin and Control Panel",
|
||||||
author = 'Mathew Guest',
|
author="Mathew Guest",
|
||||||
author_email = 't3h.zavage@gmail.com',
|
author_email="t3h.zavage@gmail.com",
|
||||||
url = 'https://git-mirror.zavage-software.com',
|
url="https://git-mirror.zavage-software.com",
|
||||||
|
|
||||||
# Third-party dependencies; will be automatically installed
|
# Third-party dependencies; will be automatically installed
|
||||||
install_requires = (
|
install_requires=("rdiff-backup", "app_skellington", "appdirs", "sqlparse") + app_skellington_requirements,
|
||||||
'rdiff-backup',
|
packages=find_packages(),
|
||||||
'app_skellington',
|
package_dir={"app_skellington": "lib/app_skellington"},
|
||||||
'appdirs',
|
|
||||||
'sqlparse'
|
|
||||||
) + app_skellington_requirements,
|
|
||||||
|
|
||||||
packages = find_packages(),
|
|
||||||
package_dir = {
|
|
||||||
'app_skellington': 'lib/app_skellington'
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import smileyface
|
import smileyface
|
||||||
smileyface.start_app()
|
|
||||||
|
|
||||||
|
smileyface.start_app()
|
||||||
|
@ -2,18 +2,20 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
# Module parameters and constants
|
# Module parameters and constants
|
||||||
APP_NAME = 'SmileyFace Unreal Tournament 4 Server Panel'
|
APP_NAME = "SmileyFace Unreal Tournament 4 Server Panel"
|
||||||
APP_AUTHOR = 'Mathew Guest'
|
APP_AUTHOR = "Mathew Guest"
|
||||||
APP_VERSION = '0.1.0'
|
APP_VERSION = "0.1.0"
|
||||||
|
|
||||||
APP_CONFIG_FILENAME = 'config.ini'
|
APP_CONFIG_FILENAME = "config.ini"
|
||||||
|
|
||||||
# config.spec is relative to the module src directory and is the
|
# config.spec is relative to the module src directory and is the
|
||||||
# config specification (structure, names, and types of config file)
|
# config specification (structure, names, and types of config file)
|
||||||
APP_CONFIGSPEC_FILENAME = 'config.spec'
|
APP_CONFIGSPEC_FILENAME = "config.spec"
|
||||||
|
|
||||||
# Check and gracefully fail if the user needs to install a 3rd-party dep.
|
# Check and gracefully fail if the user needs to install a 3rd-party dep.
|
||||||
required_lib_names = ['appdirs', 'configobj', 'colorlog']
|
required_lib_names = ["appdirs", "configobj", "colorlog"]
|
||||||
|
|
||||||
|
|
||||||
def check_env_has_dependencies(required_lib_names):
|
def check_env_has_dependencies(required_lib_names):
|
||||||
"""
|
"""
|
||||||
Attempts to import each module and gracefully fails if it doesn't
|
Attempts to import each module and gracefully fails if it doesn't
|
||||||
@ -24,15 +26,17 @@ def check_env_has_dependencies(required_lib_names):
|
|||||||
try:
|
try:
|
||||||
__import__(libname)
|
__import__(libname)
|
||||||
except ImportError as ex:
|
except ImportError as ex:
|
||||||
print('missing third-part library: ', ex, file=sys.stderr)
|
print("missing third-part library: ", ex, file=sys.stderr)
|
||||||
rc = False
|
rc = False
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print(ex, type(ex))
|
print(ex, type(ex))
|
||||||
rc = False
|
rc = False
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
|
|
||||||
if not check_env_has_dependencies(required_lib_names):
|
if not check_env_has_dependencies(required_lib_names):
|
||||||
print('refusing to load program without installed dependencies', file=sys.stderr)
|
print("refusing to load program without installed dependencies", file=sys.stderr)
|
||||||
raise ImportError('python environment needs third-party dependencies installed')
|
raise ImportError("python environment needs third-party dependencies installed")
|
||||||
|
|
||||||
# Exposed from sub-modules:
|
# Exposed from sub-modules:
|
||||||
from .app import start_app
|
from .app import start_app
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import hashlib
|
|
||||||
import functools
|
import functools
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
def md5sum_file(filename):
|
def md5sum_file(filename):
|
||||||
with open(filename, mode='rb') as f:
|
with open(filename, mode="rb") as f:
|
||||||
d = hashlib.md5()
|
d = hashlib.md5()
|
||||||
for buf in iter(functools.partial(f.read, 128), b''):
|
for buf in iter(functools.partial(f.read, 128), b""):
|
||||||
d.update(buf)
|
d.update(buf)
|
||||||
h = d.hexdigest()
|
h = d.hexdigest()
|
||||||
return h
|
return h
|
||||||
|
|
||||||
|
@ -1,63 +1,49 @@
|
|||||||
from . import hub_machine
|
|
||||||
from . import datalayer
|
|
||||||
from . import scrape_latest
|
|
||||||
|
|
||||||
import app_skellington
|
import app_skellington
|
||||||
from app_skellington import _util
|
from app_skellington import _util
|
||||||
|
|
||||||
|
from . import (
|
||||||
|
datalayer,
|
||||||
|
hub_machine,
|
||||||
|
scrape_latest,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SmileyFace(app_skellington.ApplicationContainer):
|
class SmileyFace(app_skellington.ApplicationContainer):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
filename = 'config.spec'
|
filename = "config.spec"
|
||||||
self.configspec_filepath = _util.get_asset(__name__, filename)
|
self.configspec_filepath = _util.get_asset(__name__, filename)
|
||||||
|
|
||||||
config_filepath = self._get_config_filepath(
|
config_filepath = self._get_config_filepath("smileyface-ut4", "", "hub-config.ini")
|
||||||
'smileyface-ut4',
|
|
||||||
'',
|
|
||||||
'hub-config.ini'
|
|
||||||
)
|
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
configspec_filepath=self.configspec_filepath,
|
configspec_filepath=self.configspec_filepath,
|
||||||
configini_filepath=config_filepath,
|
configini_filepath=config_filepath,
|
||||||
app_name = 'SmileyFace UT4 Server Panel',
|
app_name="SmileyFace UT4 Server Panel",
|
||||||
app_author = 'Mathew Guest',
|
app_author="Mathew Guest",
|
||||||
app_version = '0.1',
|
app_version="0.1",
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _cli_options(self):
|
def _cli_options(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _command_menu(self):
|
def _command_menu(self):
|
||||||
sm_root = self.cli.init_submenu('command')
|
sm_root = self.cli.init_submenu("command")
|
||||||
_util.register_class_as_commands(
|
_util.register_class_as_commands(self, sm_root, hub_machine.UT4ServerMachine)
|
||||||
self, sm_root,
|
|
||||||
hub_machine.UT4ServerMachine
|
|
||||||
)
|
|
||||||
|
|
||||||
sm_scrape = sm_root.create_submenu('scrape')
|
sm_scrape = sm_root.create_submenu("scrape")
|
||||||
_util.register_class_as_commands(
|
_util.register_class_as_commands(self, sm_scrape, scrape_latest.ScrapeUt4Pugs)
|
||||||
self, sm_scrape,
|
|
||||||
scrape_latest.ScrapeUt4Pugs
|
|
||||||
)
|
|
||||||
|
|
||||||
_util.register_class_as_commands(
|
_util.register_class_as_commands(self, sm_scrape, scrape_latest.ScrapeUtcc)
|
||||||
self, sm_scrape,
|
|
||||||
scrape_latest.ScrapeUtcc
|
|
||||||
)
|
|
||||||
|
|
||||||
_util.register_class_as_commands(
|
_util.register_class_as_commands(self, sm_scrape, scrape_latest.LocalFs)
|
||||||
self, sm_scrape,
|
|
||||||
scrape_latest.LocalFs
|
|
||||||
)
|
|
||||||
|
|
||||||
def _services(self):
|
def _services(self):
|
||||||
self['model'] = lambda: hub_machine.UTServerMachine(self.ctx)
|
self["model"] = lambda: hub_machine.UTServerMachine(self.ctx)
|
||||||
self.dal = datalayer.DataLayer(self.ctx)
|
self.dal = datalayer.DataLayer(self.ctx)
|
||||||
self['dal'] = lambda: self.dal
|
self["dal"] = lambda: self.dal
|
||||||
self['datalayer'] = lambda: datalayer.DbFuncs(self.ctx, self.dal)
|
self["datalayer"] = lambda: datalayer.DbFuncs(self.ctx, self.dal)
|
||||||
|
|
||||||
# self['localfs'] = lambda: datalayer.LocalFs(self.ctx, datalayer)
|
# self['localfs'] = lambda: datalayer.LocalFs(self.ctx, datalayer)
|
||||||
|
|
||||||
@ -67,7 +53,7 @@ class SmileyFace(app_skellington.ApplicationContainer):
|
|||||||
def invoke_from_cli(self):
|
def invoke_from_cli(self):
|
||||||
rc = self.load_command()
|
rc = self.load_command()
|
||||||
if not rc:
|
if not rc:
|
||||||
print('Invalid command. Try -h for usage')
|
print("Invalid command. Try -h for usage")
|
||||||
return
|
return
|
||||||
# load config
|
# load config
|
||||||
self.invoke_command()
|
self.invoke_command()
|
||||||
@ -142,4 +128,3 @@ Typical Usage:
|
|||||||
def start_app():
|
def start_app():
|
||||||
app = SmileyFace()
|
app = SmileyFace()
|
||||||
app.invoke_from_cli()
|
app.invoke_from_cli()
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from smileyface import myutil
|
import os
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
import appdirs
|
import appdirs
|
||||||
import sqlite3
|
|
||||||
import os
|
from smileyface import myutil
|
||||||
|
|
||||||
|
|
||||||
class DataLayer:
|
class DataLayer:
|
||||||
@ -17,10 +18,10 @@ class DataLayer:
|
|||||||
return self._db_conn
|
return self._db_conn
|
||||||
|
|
||||||
def _create_db_connection(self):
|
def _create_db_connection(self):
|
||||||
local_db_filename = self.ctx.config['app']['sqlite_filename']
|
local_db_filename = self.ctx.config["app"]["sqlite_filename"]
|
||||||
appdir = appdirs.user_data_dir('smileyface')
|
appdir = appdirs.user_data_dir("smileyface")
|
||||||
fullpath = os.path.join(appdir, local_db_filename)
|
fullpath = os.path.join(appdir, local_db_filename)
|
||||||
self.ctx.log['ut4'].info('sqlite3 filename: %s', fullpath)
|
self.ctx.log["ut4"].info("sqlite3 filename: %s", fullpath)
|
||||||
|
|
||||||
myutil.ensure_dir_exists(fullpath)
|
myutil.ensure_dir_exists(fullpath)
|
||||||
|
|
||||||
@ -28,9 +29,5 @@ class DataLayer:
|
|||||||
return db
|
return db
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
self.ctx.log['db'].info('commit()')
|
self.ctx.log["db"].info("commit()")
|
||||||
self.db_conn.commit()
|
self.db_conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
from smileyface import myutil
|
import datetime
|
||||||
from smileyface import structs
|
import os
|
||||||
|
|
||||||
import app_skellington._util as apputil
|
import app_skellington._util as apputil
|
||||||
import appdirs
|
import appdirs
|
||||||
import datetime
|
|
||||||
import os
|
|
||||||
import sqlparse
|
import sqlparse
|
||||||
|
|
||||||
|
from smileyface import myutil, structs
|
||||||
|
|
||||||
|
|
||||||
class DbFuncs:
|
class DbFuncs:
|
||||||
def __init__(self, ctx, dal):
|
def __init__(self, ctx, dal):
|
||||||
@ -14,7 +14,7 @@ class DbFuncs:
|
|||||||
self.dal = dal
|
self.dal = dal
|
||||||
|
|
||||||
def create_tables(self):
|
def create_tables(self):
|
||||||
sql_filename = apputil.get_asset(__name__, 'create_schema.sql')
|
sql_filename = apputil.get_asset(__name__, "create_schema.sql")
|
||||||
with open(sql_filename) as fp:
|
with open(sql_filename) as fp:
|
||||||
contents_sql = fp.read()
|
contents_sql = fp.read()
|
||||||
stmts = sqlparse.split(contents_sql)
|
stmts = sqlparse.split(contents_sql)
|
||||||
@ -22,7 +22,7 @@ class DbFuncs:
|
|||||||
conn = self.dal.db_conn
|
conn = self.dal.db_conn
|
||||||
curs = conn.cursor()
|
curs = conn.cursor()
|
||||||
for stmt in stmts:
|
for stmt in stmts:
|
||||||
print('----')
|
print("----")
|
||||||
print(stmt)
|
print(stmt)
|
||||||
curs.execute(stmt)
|
curs.execute(stmt)
|
||||||
|
|
||||||
@ -31,9 +31,9 @@ class DbFuncs:
|
|||||||
def truncate_tables(self):
|
def truncate_tables(self):
|
||||||
conn = self.dal.db_conn
|
conn = self.dal.db_conn
|
||||||
curs = conn.cursor()
|
curs = conn.cursor()
|
||||||
sql = '''
|
sql = """
|
||||||
truncate file_paks;
|
truncate file_paks;
|
||||||
'''
|
"""
|
||||||
for stmt in sqlparse.split(sql):
|
for stmt in sqlparse.split(sql):
|
||||||
curs.execute(stmt)
|
curs.execute(stmt)
|
||||||
self.dal.commit()
|
self.dal.commit()
|
||||||
@ -43,7 +43,7 @@ truncate file_paks;
|
|||||||
# NOTE(MG) datetime parameter logic could be improved and more complete
|
# NOTE(MG) datetime parameter logic could be improved and more complete
|
||||||
conn = self.dal.db_conn
|
conn = self.dal.db_conn
|
||||||
curs = conn.cursor()
|
curs = conn.cursor()
|
||||||
sql = '''
|
sql = """
|
||||||
insert into file_paks (
|
insert into file_paks (
|
||||||
file_pak_id,
|
file_pak_id,
|
||||||
fullpath,
|
fullpath,
|
||||||
@ -63,12 +63,12 @@ on conflict(filename) do update set
|
|||||||
md5sum = excluded.md5sum,
|
md5sum = excluded.md5sum,
|
||||||
--created at does not update
|
--created at does not update
|
||||||
record_updated_at = datetime('now', 'localtime')
|
record_updated_at = datetime('now', 'localtime')
|
||||||
'''
|
"""
|
||||||
args = (
|
args = (
|
||||||
record.file_pak_id,
|
record.file_pak_id,
|
||||||
record.fullpath,
|
record.fullpath,
|
||||||
record.filename,
|
record.filename,
|
||||||
record.md5sum
|
record.md5sum,
|
||||||
# record.record_created_at,
|
# record.record_created_at,
|
||||||
# record.record_updated_at
|
# record.record_updated_at
|
||||||
)
|
)
|
||||||
@ -80,7 +80,7 @@ on conflict(filename) do update set
|
|||||||
# validation data src
|
# validation data src
|
||||||
conn = self.dal.db_conn
|
conn = self.dal.db_conn
|
||||||
curs = conn.cursor()
|
curs = conn.cursor()
|
||||||
sql = '''
|
sql = """
|
||||||
update file_paks
|
update file_paks
|
||||||
set
|
set
|
||||||
validated_state = ?,
|
validated_state = ?,
|
||||||
@ -90,7 +90,7 @@ set
|
|||||||
where
|
where
|
||||||
file_pak_id = ?
|
file_pak_id = ?
|
||||||
|
|
||||||
'''
|
"""
|
||||||
args = (validate_state, src, remote_src_md5, rec_id)
|
args = (validate_state, src, remote_src_md5, rec_id)
|
||||||
curs.execute(sql, args)
|
curs.execute(sql, args)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@ -98,14 +98,14 @@ where
|
|||||||
def query_filepak(self, filename):
|
def query_filepak(self, filename):
|
||||||
conn = self.dal.db_conn
|
conn = self.dal.db_conn
|
||||||
curs = conn.cursor()
|
curs = conn.cursor()
|
||||||
sql = '''
|
sql = """
|
||||||
select
|
select
|
||||||
file_pak_id, fullpath, filename,
|
file_pak_id, fullpath, filename,
|
||||||
md5sum, record_created_at, record_updated_at
|
md5sum, record_created_at, record_updated_at
|
||||||
from
|
from
|
||||||
file_paks
|
file_paks
|
||||||
where
|
where
|
||||||
lower(filename) = lower(?)'''
|
lower(filename) = lower(?)"""
|
||||||
args = (filename,)
|
args = (filename,)
|
||||||
# print(sql)
|
# print(sql)
|
||||||
curs.execute(sql, args)
|
curs.execute(sql, args)
|
||||||
@ -126,15 +126,14 @@ where
|
|||||||
elif len(output) == 1:
|
elif len(output) == 1:
|
||||||
return output[0]
|
return output[0]
|
||||||
elif len(output) > 1:
|
elif len(output) > 1:
|
||||||
input('<breakpoint> unexpected two rows returned from db when expecting to be unique')
|
input("<breakpoint> unexpected two rows returned from db when expecting to be unique")
|
||||||
return output
|
return output
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def query_invalid_filepaks(self):
|
def query_invalid_filepaks(self):
|
||||||
conn = self.dal.db_conn
|
conn = self.dal.db_conn
|
||||||
curs = conn.cursor()
|
curs = conn.cursor()
|
||||||
sql = '''
|
sql = """
|
||||||
select
|
select
|
||||||
file_pak_id, fullpath, filename,
|
file_pak_id, fullpath, filename,
|
||||||
md5sum,
|
md5sum,
|
||||||
@ -143,7 +142,7 @@ select
|
|||||||
from
|
from
|
||||||
file_paks
|
file_paks
|
||||||
where
|
where
|
||||||
lower(filename) = lower(?)'''
|
lower(filename) = lower(?)"""
|
||||||
args = (filename,)
|
args = (filename,)
|
||||||
# print(sql)
|
# print(sql)
|
||||||
curs.execute(sql, args)
|
curs.execute(sql, args)
|
||||||
@ -160,7 +159,3 @@ where
|
|||||||
filepak.record_updated_at = r[8]
|
filepak.record_updated_at = r[8]
|
||||||
output.append(filepak)
|
output.append(filepak)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import configparser
|
import configparser
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
class UnrealIniFile:
|
class UnrealIniFile:
|
||||||
def __init__(self, filename=None):
|
def __init__(self, filename=None):
|
||||||
self._config = None
|
self._config = None
|
||||||
@ -14,12 +15,11 @@ class UnrealIniFile:
|
|||||||
def filename(self, val):
|
def filename(self, val):
|
||||||
self._filename = val
|
self._filename = val
|
||||||
if val is not None:
|
if val is not None:
|
||||||
self._config = configparser.RawConfigParser(
|
self._config = configparser.RawConfigParser(strict=False)
|
||||||
strict=False
|
|
||||||
)
|
|
||||||
self._config.optionxform = str # Trick to preserve case in key names
|
self._config.optionxform = str # Trick to preserve case in key names
|
||||||
self._config.read(self._filename)
|
self._config.read(self._filename)
|
||||||
|
|
||||||
|
|
||||||
class GameIniSpecial:
|
class GameIniSpecial:
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
self._redirect_lines = []
|
self._redirect_lines = []
|
||||||
@ -37,24 +37,23 @@ class GameIniSpecial:
|
|||||||
def clear_redirect_references(self):
|
def clear_redirect_references(self):
|
||||||
self._redirect_lines = []
|
self._redirect_lines = []
|
||||||
|
|
||||||
def add_redirect_reference(
|
def add_redirect_reference(self, pkg_basename, redirect_url, redirect_protocol, relative_path, md5sum):
|
||||||
self, pkg_basename, redirect_url, redirect_protocol,
|
|
||||||
relative_path, md5sum
|
|
||||||
):
|
|
||||||
args = {
|
args = {
|
||||||
'pkg_basename': pkg_basename,
|
"pkg_basename": pkg_basename,
|
||||||
'redirect_protocol': redirect_protocol,
|
"redirect_protocol": redirect_protocol,
|
||||||
'redirect_url': redirect_url,
|
"redirect_url": redirect_url,
|
||||||
'relative_path': relative_path,
|
"relative_path": relative_path,
|
||||||
'md5sum': md5sum
|
"md5sum": md5sum,
|
||||||
}
|
}
|
||||||
########### START multi-line awkward indent
|
########### START multi-line awkward indent
|
||||||
line = '\
|
line = '\
|
||||||
RedirectReferences=(PackageName="{pkg_basename}",\
|
RedirectReferences=(PackageName="{pkg_basename}",\
|
||||||
PackageURLProtocol="{redirect_protocol}",\
|
PackageURLProtocol="{redirect_protocol}",\
|
||||||
PackageURL="{redirect_url}/{relative_path}",\
|
PackageURL="{redirect_url}/{relative_path}",\
|
||||||
PackageChecksum="{md5sum}")'.format(**args)
|
PackageChecksum="{md5sum}")'.format(
|
||||||
########### END multi-line awkward indent
|
**args
|
||||||
|
)
|
||||||
|
########### END multi-line awkward indent
|
||||||
|
|
||||||
return self.add_redirect_reference_line(line)
|
return self.add_redirect_reference_line(line)
|
||||||
|
|
||||||
@ -65,18 +64,13 @@ PackageChecksum="{md5sum}")'.format(**args)
|
|||||||
def write(self, fp):
|
def write(self, fp):
|
||||||
newcontents = None
|
newcontents = None
|
||||||
|
|
||||||
with open(self.filename, 'r') as inifile:
|
with open(self.filename, "r") as inifile:
|
||||||
curcontents = inifile.read()
|
curcontents = inifile.read()
|
||||||
lines_str = '\n'.join(self._redirect_lines)
|
lines_str = "\n".join(self._redirect_lines)
|
||||||
|
|
||||||
newcontents = re.sub(
|
newcontents = re.sub("RedirectReferences = :PARAM:", lines_str, curcontents)
|
||||||
'RedirectReferences = :PARAM:',
|
|
||||||
lines_str,
|
|
||||||
curcontents
|
|
||||||
)
|
|
||||||
has_data = True
|
has_data = True
|
||||||
|
|
||||||
if has_data:
|
if has_data:
|
||||||
with open(self.filename, 'w') as fp:
|
with open(self.filename, "w") as fp:
|
||||||
fp.write(newcontents)
|
fp.write(newcontents)
|
||||||
|
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
from .gameconfig_edit import UnrealIniFile, GameIniSpecial
|
|
||||||
from ._util import md5sum_file
|
|
||||||
from . import myutil
|
|
||||||
from . import structs
|
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import configobj
|
|
||||||
import configparser
|
import configparser
|
||||||
import datetime
|
import datetime
|
||||||
import glob
|
import glob
|
||||||
@ -14,6 +8,12 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import configobj
|
||||||
|
|
||||||
|
from . import myutil, structs
|
||||||
|
from ._util import md5sum_file
|
||||||
|
from .gameconfig_edit import GameIniSpecial, UnrealIniFile
|
||||||
|
|
||||||
|
|
||||||
class UT4ServerMachine:
|
class UT4ServerMachine:
|
||||||
def __init__(self, ctx, datalayer):
|
def __init__(self, ctx, datalayer):
|
||||||
@ -23,8 +23,6 @@ class UT4ServerMachine:
|
|||||||
if not self._validate_env_vars():
|
if not self._validate_env_vars():
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def oneclickdeploy(self):
|
def oneclickdeploy(self):
|
||||||
self.generate_instance()
|
self.generate_instance()
|
||||||
self.upload_redirects()
|
self.upload_redirects()
|
||||||
@ -34,7 +32,7 @@ class UT4ServerMachine:
|
|||||||
"""
|
"""
|
||||||
Deletes the generated instance on the local machine.
|
Deletes the generated instance on the local machine.
|
||||||
"""
|
"""
|
||||||
self.ctx.log['ut4'].info('Clearing .pak folder...')
|
self.ctx.log["ut4"].info("Clearing .pak folder...")
|
||||||
cmd = 'rm -rv "$PROJECT_DIR"/instance/LinuxServer/UnrealTournament/Content/Paks/*'
|
cmd = 'rm -rv "$PROJECT_DIR"/instance/LinuxServer/UnrealTournament/Content/Paks/*'
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
@ -42,61 +40,46 @@ class UT4ServerMachine:
|
|||||||
"""
|
"""
|
||||||
Create required directories which the user installs maps, mutators, and config to.
|
Create required directories which the user installs maps, mutators, and config to.
|
||||||
"""
|
"""
|
||||||
dirs = (
|
dirs = ("base", "files/config", "files/maps", "files/mutators", "files/rulesets", "files/unused")
|
||||||
'base',
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
'files/config',
|
|
||||||
'files/maps',
|
|
||||||
'files/mutators',
|
|
||||||
'files/rulesets',
|
|
||||||
'files/unused'
|
|
||||||
)
|
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
|
||||||
if len(project_dir.strip()) == 0:
|
if len(project_dir.strip()) == 0:
|
||||||
project_dir = '.'
|
project_dir = "."
|
||||||
print('project_dir:', project_dir)
|
print("project_dir:", project_dir)
|
||||||
fullpaths = [
|
fullpaths = ["/".join([project_dir, d]) for d in dirs]
|
||||||
'/'.join([project_dir, d]) for d in dirs
|
|
||||||
]
|
|
||||||
|
|
||||||
for fp in fullpaths:
|
for fp in fullpaths:
|
||||||
cmd = 'mkdir -p {}'.format(fp)
|
cmd = "mkdir -p {}".format(fp)
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
def download_linux_server(self, x):
|
def download_linux_server(self, x):
|
||||||
"""
|
"""
|
||||||
Download the latest Linux Unreal Tournament 4 Server from Epic
|
Download the latest Linux Unreal Tournament 4 Server from Epic
|
||||||
"""
|
"""
|
||||||
self.ctx.log['ut4'].info('Downloading Linux Server Binary from Epic.')
|
self.ctx.log["ut4"].info("Downloading Linux Server Binary from Epic.")
|
||||||
|
|
||||||
def download_logs(self):
|
def download_logs(self):
|
||||||
"""
|
"""
|
||||||
Download the logs from the target hub.
|
Download the logs from the target hub.
|
||||||
"""
|
"""
|
||||||
config_dir = self.ctx.config['app']['config_dir']
|
config_dir = self.ctx.config["app"]["config_dir"]
|
||||||
remote_game_host = self.ctx.config['app']['remote_game_host']
|
remote_game_host = self.ctx.config["app"]["remote_game_host"]
|
||||||
remote_game_dir = self.ctx.config['app']['remote_game_dir']
|
remote_game_dir = self.ctx.config["app"]["remote_game_dir"]
|
||||||
|
|
||||||
self.ctx.log['ut4'].info('Downloading instance logs from target hub.')
|
self.ctx.log["ut4"].info("Downloading instance logs from target hub.")
|
||||||
cmd = '''
|
cmd = """
|
||||||
rsync -ravzp {remote_game_host}:{remote_game_dir}/LinuxServer/UnrealTournament/Saved/Logs/ {config_dir}/downloaded-logs/
|
rsync -ravzp {remote_game_host}:{remote_game_dir}/LinuxServer/UnrealTournament/Saved/Logs/ {config_dir}/downloaded-logs/
|
||||||
'''\
|
""".format(
|
||||||
.format(**{
|
**{"config_dir": config_dir, "remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir}
|
||||||
'config_dir': config_dir,
|
)
|
||||||
'remote_game_host': remote_game_host,
|
|
||||||
'remote_game_dir': remote_game_dir
|
|
||||||
})
|
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
# Delete logs on remote game server if successfully transferred to local:
|
# Delete logs on remote game server if successfully transferred to local:
|
||||||
self.ctx.log['ut4'].info('')
|
self.ctx.log["ut4"].info("")
|
||||||
cmd = '''
|
cmd = """
|
||||||
ssh {remote_game_host} rm {remote_game_dir}/LinuxServer/UnrealTournament/Saved/Logs/* -r'
|
ssh {remote_game_host} rm {remote_game_dir}/LinuxServer/UnrealTournament/Saved/Logs/* -r'
|
||||||
'''\
|
""".format(
|
||||||
.format(**{
|
**{"config_dir": config_dir, "remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir}
|
||||||
'config_dir': config_dir,
|
)
|
||||||
'remote_game_host': remote_game_host,
|
|
||||||
'remote_game_dir': remote_game_dir
|
|
||||||
})
|
|
||||||
# self._invoke_command(cmd)
|
# self._invoke_command(cmd)
|
||||||
|
|
||||||
def generate_instance(self):
|
def generate_instance(self):
|
||||||
@ -104,34 +87,25 @@ ssh {remote_game_host} rm {remote_game_dir}/LinuxServer/UnrealTournament/Saved/L
|
|||||||
Takes the current coniguration and outputs the application files which
|
Takes the current coniguration and outputs the application files which
|
||||||
can be copied to the server.
|
can be copied to the server.
|
||||||
"""
|
"""
|
||||||
self.ctx.log['ut4'].info('Generating server instance from custom files...')
|
self.ctx.log["ut4"].info("Generating server instance from custom files...")
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
|
|
||||||
# rsync
|
# rsync
|
||||||
src = '/'.join([project_dir, 'base/LinuxServer'])
|
src = "/".join([project_dir, "base/LinuxServer"])
|
||||||
dst = '/'.join([project_dir, 'instance/'])
|
dst = "/".join([project_dir, "instance/"])
|
||||||
cmd = 'rsync -ravzp {src} {dst}'.format(**{
|
cmd = "rsync -ravzp {src} {dst}".format(**{"src": src, "dst": dst})
|
||||||
'src': src,
|
|
||||||
'dst': dst
|
|
||||||
})
|
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
# cp 1
|
# cp 1
|
||||||
src = '/'.join([project_dir, 'start-server.sh'])
|
src = "/".join([project_dir, "start-server.sh"])
|
||||||
dst = '/'.join([project_dir, 'instance/'])
|
dst = "/".join([project_dir, "instance/"])
|
||||||
cmd = 'cp {src} {dst}'.format(**{
|
cmd = "cp {src} {dst}".format(**{"src": src, "dst": dst})
|
||||||
'src': src,
|
|
||||||
'dst': dst
|
|
||||||
})
|
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
# cp 2
|
# cp 2
|
||||||
src = '/'.join([project_dir, 'stop-server.sh'])
|
src = "/".join([project_dir, "stop-server.sh"])
|
||||||
dst = '/'.join([project_dir, 'instance/'])
|
dst = "/".join([project_dir, "instance/"])
|
||||||
cmd = 'cp {src} {dst}'.format(**{
|
cmd = "cp {src} {dst}".format(**{"src": src, "dst": dst})
|
||||||
'src': src,
|
|
||||||
'dst': dst
|
|
||||||
})
|
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
if self._needs_first_run():
|
if self._needs_first_run():
|
||||||
@ -149,35 +123,35 @@ ssh {remote_game_host} rm {remote_game_dir}/LinuxServer/UnrealTournament/Saved/L
|
|||||||
"""
|
"""
|
||||||
Flip on the target hub on for Fragging!
|
Flip on the target hub on for Fragging!
|
||||||
"""
|
"""
|
||||||
self.ctx.log['ut4'].info('Starting hub...')
|
self.ctx.log["ut4"].info("Starting hub...")
|
||||||
|
|
||||||
cmd = '''
|
cmd = """
|
||||||
ssh {remote_game_host} {remote_game_dir}/start-server.sh
|
ssh {remote_game_host} {remote_game_dir}/start-server.sh
|
||||||
'''
|
"""
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
def stop_server(self):
|
def stop_server(self):
|
||||||
"""
|
"""
|
||||||
Stop UT4 Hub processes on the server.
|
Stop UT4 Hub processes on the server.
|
||||||
"""
|
"""
|
||||||
self.ctx.log['ut4'].info('Stopping hub.')
|
self.ctx.log["ut4"].info("Stopping hub.")
|
||||||
cmd = '''
|
cmd = """
|
||||||
ssh {remote_game_host} {remote_game_dir}/stop-server.sh
|
ssh {remote_game_host} {remote_game_dir}/stop-server.sh
|
||||||
'''
|
"""
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
def upload_redirects(self):
|
def upload_redirects(self):
|
||||||
"""
|
"""
|
||||||
Upload paks to redirect server.
|
Upload paks to redirect server.
|
||||||
"""
|
"""
|
||||||
self.ctx.log['ut4'].info('Uploading redirects (maps, mutators, etc.) to target hub.')
|
self.ctx.log["ut4"].info("Uploading redirects (maps, mutators, etc.) to target hub.")
|
||||||
|
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
# paks_dir = os.path.join(project_dir, 'instance/LinuxServer/UnrealTournament/Content/Paks/')
|
# paks_dir = os.path.join(project_dir, 'instance/LinuxServer/UnrealTournament/Content/Paks/')
|
||||||
paks_dir = os.path.join(project_dir, 'files/') # trailing slash required
|
paks_dir = os.path.join(project_dir, "files/") # trailing slash required
|
||||||
remote_redirect_host = self.ctx.config['app']['remote_redirect_host']
|
remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"]
|
||||||
cwd = project_dir
|
cwd = project_dir
|
||||||
cmd = '''
|
cmd = """
|
||||||
rsync -rivz \
|
rsync -rivz \
|
||||||
--delete \
|
--delete \
|
||||||
--exclude "*.md5" \
|
--exclude "*.md5" \
|
||||||
@ -186,11 +160,12 @@ rsync -rivz \
|
|||||||
--exclude Mods.db \
|
--exclude Mods.db \
|
||||||
{paks_dir} {remote_redirect_host}
|
{paks_dir} {remote_redirect_host}
|
||||||
|
|
||||||
'''\
|
""".format(
|
||||||
.format(**{
|
**{
|
||||||
'paks_dir': paks_dir,
|
"paks_dir": paks_dir,
|
||||||
'remote_redirect_host': remote_redirect_host,
|
"remote_redirect_host": remote_redirect_host,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
# subprocess.run(cmd, cwd=cwd) # should be invoke_command? no because gui will need subprocess.run
|
# subprocess.run(cmd, cwd=cwd) # should be invoke_command? no because gui will need subprocess.run
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
@ -199,61 +174,66 @@ rsync -rivz \
|
|||||||
self._redirect_chown()
|
self._redirect_chown()
|
||||||
|
|
||||||
def _redirect_hide_passwords(self):
|
def _redirect_hide_passwords(self):
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
remote_redirect_host = self.ctx.config['app']['remote_redirect_host']
|
remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"]
|
||||||
# (on the server):
|
# (on the server):
|
||||||
gameini="/srv/ut4-redirect.zavage.net/config/Game.ini"
|
gameini = "/srv/ut4-redirect.zavage.net/config/Game.ini"
|
||||||
engineini="/srv/ut4-redirect.zavage.net/config/Engine.ini"
|
engineini = "/srv/ut4-redirect.zavage.net/config/Engine.ini"
|
||||||
|
|
||||||
cmd = '''
|
cmd = """
|
||||||
ssh mathewguest.com \
|
ssh mathewguest.com \
|
||||||
sed -i /ServerInstanceID=/c\ServerInstanceID=Hidden {gameini}
|
sed -i /ServerInstanceID=/c\ServerInstanceID=Hidden {gameini}
|
||||||
'''.format(gameini=gameini)
|
""".format(
|
||||||
|
gameini=gameini
|
||||||
|
)
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
cmd = '''
|
cmd = """
|
||||||
ssh mathewguest.com \
|
ssh mathewguest.com \
|
||||||
sed -i /RconPassword=/c\RconPassword=Hidden {engineini}
|
sed -i /RconPassword=/c\RconPassword=Hidden {engineini}
|
||||||
'''.format(engineini=engineini)
|
""".format(
|
||||||
|
engineini=engineini
|
||||||
|
)
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
def _redirect_upload_script(self):
|
def _redirect_upload_script(self):
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
remote_redirect_host = self.ctx.config['app']['remote_redirect_host']
|
remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"]
|
||||||
cmd = '''
|
cmd = """
|
||||||
rsync -vz \
|
rsync -vz \
|
||||||
{project_dir}/ut4-server-ctl.sh \
|
{project_dir}/ut4-server-ctl.sh \
|
||||||
{remote_redirect_host}
|
{remote_redirect_host}
|
||||||
'''\
|
""".format(
|
||||||
.format(**{
|
**{
|
||||||
'project_dir': project_dir,
|
"project_dir": project_dir,
|
||||||
'remote_redirect_host': remote_redirect_host,
|
"remote_redirect_host": remote_redirect_host,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
# subprocess.run(cmd, cwd=cwd) # should be invoke_command? no because gui will need subprocess.run
|
# subprocess.run(cmd, cwd=cwd) # should be invoke_command? no because gui will need subprocess.run
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
def _redirect_chown(self):
|
def _redirect_chown(self):
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
remote_redirect_host = self.ctx.config['app']['remote_redirect_host']
|
remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"]
|
||||||
|
|
||||||
cmd = '''
|
cmd = """
|
||||||
ssh mathewguest.com \
|
ssh mathewguest.com \
|
||||||
chown http:http /srv/ut4-redirect.zavage.net -R
|
chown http:http /srv/ut4-redirect.zavage.net -R
|
||||||
'''
|
"""
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
def upload_server(self):
|
def upload_server(self):
|
||||||
"""
|
"""
|
||||||
Upload all required game files to the hub server.
|
Upload all required game files to the hub server.
|
||||||
"""
|
"""
|
||||||
self.ctx.log['ut4'].info('Uploading customized server')
|
self.ctx.log["ut4"].info("Uploading customized server")
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
remote_game_host = self.ctx.config['app']['remote_game_host']
|
remote_game_host = self.ctx.config["app"]["remote_game_host"]
|
||||||
remote_game_dir = self.ctx.config['app']['remote_game_dir']
|
remote_game_dir = self.ctx.config["app"]["remote_game_dir"]
|
||||||
cwd = None
|
cwd = None
|
||||||
|
|
||||||
# transfer #1
|
# transfer #1
|
||||||
cmd = '''
|
cmd = """
|
||||||
rsync -raivzp \
|
rsync -raivzp \
|
||||||
--delete \
|
--delete \
|
||||||
--exclude ".KEEP" \
|
--exclude ".KEEP" \
|
||||||
@ -266,88 +246,72 @@ rsync -raivzp \
|
|||||||
--exclude "Saved/Logs/*" \
|
--exclude "Saved/Logs/*" \
|
||||||
{project_dir}/instance/ \
|
{project_dir}/instance/ \
|
||||||
{remote_game_host}:{remote_game_dir}
|
{remote_game_host}:{remote_game_dir}
|
||||||
'''\
|
""".format(
|
||||||
.format(**{
|
**{"project_dir": project_dir, "remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir}
|
||||||
'project_dir': project_dir,
|
)
|
||||||
'remote_game_host': remote_game_host,
|
cmd = cmd.replace(" ", "")
|
||||||
'remote_game_dir': remote_game_dir
|
|
||||||
})
|
|
||||||
cmd = cmd.replace(' ', '')
|
|
||||||
# subprocess.run(cmd, cwd=cwd) # should be invoke_command? no because gui will need subprocess.run
|
# subprocess.run(cmd, cwd=cwd) # should be invoke_command? no because gui will need subprocess.run
|
||||||
|
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
# transfer #2
|
# transfer #2
|
||||||
cmd = '''
|
cmd = """
|
||||||
rsync -avzp \
|
rsync -avzp \
|
||||||
{project_dir}/ut4-server-ctl.sh \
|
{project_dir}/ut4-server-ctl.sh \
|
||||||
{remote_game_host}:{remote_game_dir}
|
{remote_game_host}:{remote_game_dir}
|
||||||
'''\
|
""".format(
|
||||||
.format(**{
|
**{"project_dir": project_dir, "remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir}
|
||||||
'project_dir': project_dir,
|
)
|
||||||
'remote_game_host': remote_game_host,
|
|
||||||
'remote_game_dir': remote_game_dir
|
|
||||||
})
|
|
||||||
# subprocess.run(cmd, cwd=cwd)
|
# subprocess.run(cmd, cwd=cwd)
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
# transfer #3
|
# transfer #3
|
||||||
cmd = '''
|
cmd = """
|
||||||
scp \
|
scp \
|
||||||
{project_dir}/instance/ut4-server.service \
|
{project_dir}/instance/ut4-server.service \
|
||||||
{remote_game_host}:/etc/systemd/system/
|
{remote_game_host}:/etc/systemd/system/
|
||||||
'''\
|
""".format(
|
||||||
.format(**{
|
**{"project_dir": project_dir, "remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir}
|
||||||
'project_dir': project_dir,
|
)
|
||||||
'remote_game_host': remote_game_host,
|
|
||||||
'remote_game_dir': remote_game_dir
|
|
||||||
})
|
|
||||||
# subprocess.run(cmd, cwd=cwd)
|
# subprocess.run(cmd, cwd=cwd)
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
# transfer #4
|
# transfer #4
|
||||||
cmd = '''
|
cmd = """
|
||||||
ssh {remote_game_host} \
|
ssh {remote_game_host} \
|
||||||
chown ut4:ut4 {remote_game_dir} -R
|
chown ut4:ut4 {remote_game_dir} -R
|
||||||
'''.format(**{
|
""".format(
|
||||||
'remote_game_host': remote_game_host,
|
**{"remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir}
|
||||||
'remote_game_dir': remote_game_dir
|
)
|
||||||
|
|
||||||
})
|
|
||||||
# subprocess.run(cmd, cwd=cwd)
|
# subprocess.run(cmd, cwd=cwd)
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Fix +x permissions on bash scripts
|
# Fix +x permissions on bash scripts
|
||||||
cmd = '''
|
cmd = """
|
||||||
ssh {remote_game_host} \
|
ssh {remote_game_host} \
|
||||||
chmod +x \
|
chmod +x \
|
||||||
{remote_game_dir}/start-server.sh \
|
{remote_game_dir}/start-server.sh \
|
||||||
{remote_game_dir}/stop-server.sh
|
{remote_game_dir}/stop-server.sh
|
||||||
'''.format(**{
|
""".format(
|
||||||
'remote_game_host': remote_game_host,
|
**{"remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir}
|
||||||
'remote_game_dir': remote_game_dir
|
)
|
||||||
|
|
||||||
})
|
|
||||||
# subprocess.run(cmd, cwd=cwd)
|
# subprocess.run(cmd, cwd=cwd)
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
def _first_run(self):
|
def _first_run(self):
|
||||||
self.ctx.log['ut4'].info('Starting instance once to get UID.')
|
self.ctx.log["ut4"].info("Starting instance once to get UID.")
|
||||||
self.ctx.log['ut4'].info('Unfortunately, this takes 20 seconds. Just wait.')
|
self.ctx.log["ut4"].info("Unfortunately, this takes 20 seconds. Just wait.")
|
||||||
|
|
||||||
# Make binary executable:
|
# Make binary executable:
|
||||||
bin_name = 'UE4Server-Linux-Shipping'
|
bin_name = "UE4Server-Linux-Shipping"
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
|
|
||||||
cwd = '{project_dir}/instance/LinuxServer/Engine/Binaries/Linux'\
|
cwd = "{project_dir}/instance/LinuxServer/Engine/Binaries/Linux".format(project_dir=project_dir)
|
||||||
.format(project_dir=project_dir)
|
target_file = "{cwd}/{bin_name}".format(cwd=cwd, bin_name=bin_name)
|
||||||
target_file = '{cwd}/{bin_name}'.format(cwd=cwd, bin_name=bin_name)
|
cmd = ["chmod", "770", target_file]
|
||||||
cmd = ['chmod', '770', target_file]
|
|
||||||
p = subprocess.run(cmd)
|
p = subprocess.run(cmd)
|
||||||
|
|
||||||
cmd = ['./'+bin_name, 'UnrealTournament', 'UT-Entry?Game=Lobby', '-log']
|
cmd = ["./" + bin_name, "UnrealTournament", "UT-Entry?Game=Lobby", "-log"]
|
||||||
p = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE)
|
p = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -356,68 +320,68 @@ ssh {remote_game_host} \
|
|||||||
p.kill()
|
p.kill()
|
||||||
stdout, stderr = p.communicate()
|
stdout, stderr = p.communicate()
|
||||||
|
|
||||||
self.ctx.log['ut4'].info('sleeping 20 seconds and then we\'ll kill the server we started just now.')
|
self.ctx.log["ut4"].info("sleeping 20 seconds and then we'll kill the server we started just now.")
|
||||||
# # TODO(MG) get uid and export
|
# # TODO(MG) get uid and export
|
||||||
|
|
||||||
def _install_config(self):
|
def _install_config(self):
|
||||||
files = (
|
files = ("Game.ini", "Engine.ini")
|
||||||
'Game.ini',
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
'Engine.ini'
|
config_dir = self.ctx.config["app"]["config_dir"]
|
||||||
)
|
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
|
||||||
config_dir = self.ctx.config['app']['config_dir']
|
|
||||||
for fn in files:
|
for fn in files:
|
||||||
self.ctx.log['ut4'].info('Installing file: %s', fn)
|
self.ctx.log["ut4"].info("Installing file: %s", fn)
|
||||||
src = os.path.join(config_dir, fn)
|
src = os.path.join(config_dir, fn)
|
||||||
dst = os.path.join(project_dir, 'instance/LinuxServer/UnrealTournament/Saved/Config/LinuxServer', fn)
|
dst = os.path.join(project_dir, "instance/LinuxServer/UnrealTournament/Saved/Config/LinuxServer", fn)
|
||||||
cmd = 'cp {src} {dst}'.format(**{
|
cmd = "cp {src} {dst}".format(**{"src": src, "dst": dst})
|
||||||
'src': src,
|
|
||||||
'dst': dst
|
|
||||||
})
|
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
# Monkey-patch Game.ini to ensure it has a place for RedirectReferences
|
# Monkey-patch Game.ini to ensure it has a place for RedirectReferences
|
||||||
if fn == 'Game.ini':
|
if fn == "Game.ini":
|
||||||
ini = UnrealIniFile(dst)
|
ini = UnrealIniFile(dst)
|
||||||
sect_name = '/Script/UnrealTournament.UTBaseGameMode'
|
sect_name = "/Script/UnrealTournament.UTBaseGameMode"
|
||||||
opt_name = 'RedirectReferences'
|
opt_name = "RedirectReferences"
|
||||||
if not ini._config.has_section(sect_name):
|
if not ini._config.has_section(sect_name):
|
||||||
ini._config.add_section(sect_name)
|
ini._config.add_section(sect_name)
|
||||||
if not ini._config.has_option(sect_name, opt_name):
|
if not ini._config.has_option(sect_name, opt_name):
|
||||||
ini._config.set(sect_name, opt_name, ':PARAM:')
|
ini._config.set(sect_name, opt_name, ":PARAM:")
|
||||||
with open(dst, 'w') as fp:
|
with open(dst, "w") as fp:
|
||||||
ini._config.write(fp)
|
ini._config.write(fp)
|
||||||
|
|
||||||
def _install_paks(self):
|
def _install_paks(self):
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
|
|
||||||
self.ctx.log['ut4'].info('Installing maps...')
|
self.ctx.log["ut4"].info("Installing maps...")
|
||||||
cmd = 'rsync -ravzp {src} {dst}'.format(**{
|
cmd = "rsync -ravzp {src} {dst}".format(
|
||||||
'src': '/'.join([project_dir, 'files/maps/']),
|
**{
|
||||||
'dst': '/'.join([project_dir, 'instance/LinuxServer/UnrealTournament/Content/Paks/'])
|
"src": "/".join([project_dir, "files/maps/"]),
|
||||||
})
|
"dst": "/".join([project_dir, "instance/LinuxServer/UnrealTournament/Content/Paks/"]),
|
||||||
|
}
|
||||||
|
)
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
self.ctx.log['ut4'].info('Installing mutators...')
|
self.ctx.log["ut4"].info("Installing mutators...")
|
||||||
cmd = 'rsync -ravzp {src} {dst}'.format(**{
|
cmd = "rsync -ravzp {src} {dst}".format(
|
||||||
'src': '/'.join([project_dir, 'files/mutators/']),
|
**{
|
||||||
'dst': '/'.join([project_dir, 'instance/LinuxServer/UnrealTournament/Content/Paks/'])
|
"src": "/".join([project_dir, "files/mutators/"]),
|
||||||
})
|
"dst": "/".join([project_dir, "instance/LinuxServer/UnrealTournament/Content/Paks/"]),
|
||||||
|
}
|
||||||
|
)
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
def _install_redirect_lines(self):
|
def _install_redirect_lines(self):
|
||||||
self.ctx.log['ut4'].info('Generating redirect references...')
|
self.ctx.log["ut4"].info("Generating redirect references...")
|
||||||
|
|
||||||
redirect_protocol = self.ctx.config['app']['redirect_protocol']
|
redirect_protocol = self.ctx.config["app"]["redirect_protocol"]
|
||||||
redirect_url = self.ctx.config['app']['redirect_url']
|
redirect_url = self.ctx.config["app"]["redirect_url"]
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
mod_dir = '/'.join([project_dir, 'files'])
|
mod_dir = "/".join([project_dir, "files"])
|
||||||
|
|
||||||
game_ini_filepath = '/'.join([project_dir, 'instance/LinuxServer/UnrealTournament/Saved/Config/LinuxServer/Game.ini'])
|
game_ini_filepath = "/".join(
|
||||||
|
[project_dir, "instance/LinuxServer/UnrealTournament/Saved/Config/LinuxServer/Game.ini"]
|
||||||
|
)
|
||||||
game_ini = GameIniSpecial(game_ini_filepath)
|
game_ini = GameIniSpecial(game_ini_filepath)
|
||||||
game_ini.clear_redirect_references()
|
game_ini.clear_redirect_references()
|
||||||
|
|
||||||
files = glob.glob('{}/**/*.pak'.format(mod_dir))
|
files = glob.glob("{}/**/*.pak".format(mod_dir))
|
||||||
redirect_lines = []
|
redirect_lines = []
|
||||||
for idx, filename in enumerate(files):
|
for idx, filename in enumerate(files):
|
||||||
# if idx > 5:
|
# if idx > 5:
|
||||||
@ -435,57 +399,52 @@ ssh {remote_game_host} \
|
|||||||
print(ex)
|
print(ex)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
line = game_ini.add_redirect_reference(**{
|
line = game_ini.add_redirect_reference(
|
||||||
'pkg_basename': pkg_basename,
|
**{
|
||||||
'redirect_protocol': redirect_protocol,
|
"pkg_basename": pkg_basename,
|
||||||
'redirect_url': redirect_url,
|
"redirect_protocol": redirect_protocol,
|
||||||
'relative_path': relative_path,
|
"redirect_url": redirect_url,
|
||||||
'md5sum': md5sum
|
"relative_path": relative_path,
|
||||||
})
|
"md5sum": md5sum,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
self.ctx.log['ut4'].debug("redirect line = '%s'", line)
|
self.ctx.log["ut4"].debug("redirect line = '%s'", line)
|
||||||
|
|
||||||
data = game_ini.write(sys.stdout)
|
data = game_ini.write(sys.stdout)
|
||||||
|
|
||||||
def _install_rulesets(self):
|
def _install_rulesets(self):
|
||||||
self.ctx.log['ut4'].info('Concatenating rulesets for game modes...')
|
self.ctx.log["ut4"].info("Concatenating rulesets for game modes...")
|
||||||
project_dir = self.ctx.config['app']['project_dir']
|
project_dir = self.ctx.config["app"]["project_dir"]
|
||||||
|
|
||||||
src_dir = '/'.join([project_dir, 'files/rulesets'])
|
src_dir = "/".join([project_dir, "files/rulesets"])
|
||||||
out_dir = '/'.join([project_dir, '/instance/LinuxServer/UnrealTournament/Saved/Config/Rulesets'])
|
out_dir = "/".join([project_dir, "/instance/LinuxServer/UnrealTournament/Saved/Config/Rulesets"])
|
||||||
out_filename='/'.join([out_dir, 'ruleset.json'])
|
out_filename = "/".join([out_dir, "ruleset.json"])
|
||||||
|
|
||||||
cmd = 'mkdir -pv {out_dir}'.format(**{
|
cmd = "mkdir -pv {out_dir}".format(**{"out_dir": out_dir})
|
||||||
'out_dir': out_dir
|
|
||||||
})
|
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
self.ctx.log['ut4'].info('out filename=%s', out_filename)
|
self.ctx.log["ut4"].info("out filename=%s", out_filename)
|
||||||
|
|
||||||
# echo {\"rules\":[ > "$OUT_FILENAME"
|
# echo {\"rules\":[ > "$OUT_FILENAME"
|
||||||
cmd = "echo '{{\"rules\":[' > \"{out_filename}\"".format(out_filename=out_filename)
|
cmd = 'echo \'{{"rules":[\' > "{out_filename}"'.format(out_filename=out_filename)
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
cmd = 'for f in "{src_dir}"/*.json ; do cat "$f" >> "{out_filename}" ; done'.format(
|
cmd = 'for f in "{src_dir}"/*.json ; do cat "$f" >> "{out_filename}" ; done'.format(
|
||||||
**{
|
**{"src_dir": src_dir, "out_filename": out_filename}
|
||||||
'src_dir': src_dir,
|
)
|
||||||
'out_filename': out_filename
|
|
||||||
})
|
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
|
|
||||||
cmd = 'echo "]}}" >> "{out_filename}"'.format(
|
cmd = 'echo "]}}" >> "{out_filename}"'.format(**{"out_filename": out_filename})
|
||||||
**{
|
|
||||||
'out_filename': out_filename
|
|
||||||
})
|
|
||||||
self._invoke_command(cmd)
|
self._invoke_command(cmd)
|
||||||
self.ctx.log['ut4'].info('output ruleset is at "%s"', out_filename)
|
self.ctx.log["ut4"].info('output ruleset is at "%s"', out_filename)
|
||||||
|
|
||||||
def _invoke_command(self, cmd, msg=None):
|
def _invoke_command(self, cmd, msg=None):
|
||||||
assert isinstance(cmd, str), 'cmd input must be string: %s'.format(cmd)
|
assert isinstance(cmd, str), "cmd input must be string: %s".format(cmd)
|
||||||
if msg is None:
|
if msg is None:
|
||||||
msg = cmd
|
msg = cmd
|
||||||
print(msg)
|
print(msg)
|
||||||
self.ctx.log['ut4'].info('running cmd: %s', cmd)
|
self.ctx.log["ut4"].info("running cmd: %s", cmd)
|
||||||
cwd = None # todo(mg) ?
|
cwd = None # todo(mg) ?
|
||||||
os.system(cmd)
|
os.system(cmd)
|
||||||
|
|
||||||
@ -494,31 +453,31 @@ ssh {remote_game_host} \
|
|||||||
|
|
||||||
def _validate_env_vars(self):
|
def _validate_env_vars(self):
|
||||||
variable_names = (
|
variable_names = (
|
||||||
'project_dir',
|
"project_dir",
|
||||||
'download_url',
|
"download_url",
|
||||||
'download_filename',
|
"download_filename",
|
||||||
'download_md5',
|
"download_md5",
|
||||||
'redirect_protocol',
|
"redirect_protocol",
|
||||||
'redirect_url',
|
"redirect_url",
|
||||||
'remote_game_host',
|
"remote_game_host",
|
||||||
'remote_game_dir',
|
"remote_game_dir",
|
||||||
'remote_redirect_host'
|
"remote_redirect_host",
|
||||||
)
|
)
|
||||||
for name in variable_names:
|
for name in variable_names:
|
||||||
value = self.ctx.config['app'][name]
|
value = self.ctx.config["app"][name]
|
||||||
self.ctx.log['ut4'].info('%s: %s', name, value)
|
self.ctx.log["ut4"].info("%s: %s", name, value)
|
||||||
|
|
||||||
i = input('Continue with above configuration? (y/N):')
|
i = input("Continue with above configuration? (y/N):")
|
||||||
if i.lower() != 'y':
|
if i.lower() != "y":
|
||||||
self.ctx.log['ut4'].info('Doing nothing.')
|
self.ctx.log["ut4"].info("Doing nothing.")
|
||||||
return False
|
return False
|
||||||
self.ctx.log['ut4'].info('Continuing.')
|
self.ctx.log["ut4"].info("Continuing.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class MultiOrderedDict(collections.OrderedDict):
|
class MultiOrderedDict(collections.OrderedDict):
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
if isinstance(value, list) and key in self:
|
if isinstance(value, list) and key in self:
|
||||||
self[key].extend(value)
|
self[key].extend(value)
|
||||||
else:
|
else:
|
||||||
super().__setitem__(key, value)
|
super().__setitem__(key, value)
|
||||||
|
|
||||||
|
@ -5,14 +5,14 @@ import os
|
|||||||
def ensure_dir_exists(dirpath):
|
def ensure_dir_exists(dirpath):
|
||||||
if dirpath is None:
|
if dirpath is None:
|
||||||
return
|
return
|
||||||
if dirpath == '':
|
if dirpath == "":
|
||||||
return
|
return
|
||||||
dirpath = os.path.dirname(dirpath)
|
dirpath = os.path.dirname(dirpath)
|
||||||
os.makedirs(dirpath, exist_ok=True)
|
os.makedirs(dirpath, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def md5_file(filename):
|
def md5_file(filename):
|
||||||
with open(filename, 'rb') as fp:
|
with open(filename, "rb") as fp:
|
||||||
data = fp.read()
|
data = fp.read()
|
||||||
h = hashlib.md5(data).hexdigest()
|
h = hashlib.md5(data).hexdigest()
|
||||||
return h
|
return h
|
@ -1,3 +1,3 @@
|
|||||||
from .local_fs import *
|
from .local_fs import *
|
||||||
from .scrape_utcc import *
|
|
||||||
from .scrape_ut4pugs import *
|
from .scrape_ut4pugs import *
|
||||||
|
from .scrape_utcc import *
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
from smileyface import myutil
|
|
||||||
from smileyface import structs
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from smileyface import myutil, structs
|
||||||
|
|
||||||
|
|
||||||
class LocalFs:
|
class LocalFs:
|
||||||
def __init__(self, ctx, datalayer):
|
def __init__(self, ctx, datalayer):
|
||||||
@ -14,12 +13,12 @@ class LocalFs:
|
|||||||
self.datalayer.create_tables()
|
self.datalayer.create_tables()
|
||||||
|
|
||||||
def load_md5s(self):
|
def load_md5s(self):
|
||||||
paks_dir = self.ctx.config['app']['project_dir']
|
paks_dir = self.ctx.config["app"]["project_dir"]
|
||||||
maps_dir = os.path.join(paks_dir, 'files', 'maps')
|
maps_dir = os.path.join(paks_dir, "files", "maps")
|
||||||
print(maps_dir)
|
print(maps_dir)
|
||||||
self._load_md5_one_dir(maps_dir)
|
self._load_md5_one_dir(maps_dir)
|
||||||
|
|
||||||
muts_dir = os.path.join(paks_dir, 'files', 'mutators')
|
muts_dir = os.path.join(paks_dir, "files", "mutators")
|
||||||
print(muts_dir)
|
print(muts_dir)
|
||||||
self._load_md5_one_dir(muts_dir)
|
self._load_md5_one_dir(muts_dir)
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import selenium
|
import selenium
|
||||||
import selenium.webdriver
|
import selenium.webdriver
|
||||||
|
|
||||||
URL_MUTATORS = 'https://ut4pugs.us/redirect-mutators'
|
URL_MUTATORS = "https://ut4pugs.us/redirect-mutators"
|
||||||
URL_MAPS = 'https://ut4pugs.us/redirect-mutators'
|
URL_MAPS = "https://ut4pugs.us/redirect-mutators"
|
||||||
|
|
||||||
|
|
||||||
class ScrapeUt4Pugs:
|
class ScrapeUt4Pugs:
|
||||||
@ -25,14 +25,14 @@ class ScrapeUt4Pugs:
|
|||||||
self._check_pak_md5sums()
|
self._check_pak_md5sums()
|
||||||
|
|
||||||
def _check_pak_md5sums(self):
|
def _check_pak_md5sums(self):
|
||||||
tbl_of_mutators = self.browser.find_element_by_id('myTable')
|
tbl_of_mutators = self.browser.find_element_by_id("myTable")
|
||||||
print(tbl_of_mutators)
|
print(tbl_of_mutators)
|
||||||
|
|
||||||
mut_rows = tbl_of_mutators.find_elements_by_xpath('tbody/tr')
|
mut_rows = tbl_of_mutators.find_elements_by_xpath("tbody/tr")
|
||||||
for r in mut_rows:
|
for r in mut_rows:
|
||||||
mut_cols = r.find_elements_by_xpath('td')
|
mut_cols = r.find_elements_by_xpath("td")
|
||||||
if len(mut_cols) != 3:
|
if len(mut_cols) != 3:
|
||||||
input('<breakpoint> at unexpected columns for mutator. received {}'.format(len(mut_cols)))
|
input("<breakpoint> at unexpected columns for mutator. received {}".format(len(mut_cols)))
|
||||||
mut_file = mut_cols[0]
|
mut_file = mut_cols[0]
|
||||||
mut_md5 = mut_cols[1]
|
mut_md5 = mut_cols[1]
|
||||||
mut_ini_line = mut_cols[2]
|
mut_ini_line = mut_cols[2]
|
||||||
@ -44,22 +44,22 @@ class ScrapeUt4Pugs:
|
|||||||
local_file = self.datalayer.query_filepak(mut_file.text)
|
local_file = self.datalayer.query_filepak(mut_file.text)
|
||||||
print(local_file)
|
print(local_file)
|
||||||
if not local_file:
|
if not local_file:
|
||||||
self.ctx.log['ut4'].warn('pak not found locally: %s', mut_file.text)
|
self.ctx.log["ut4"].warn("pak not found locally: %s", mut_file.text)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
local_md5 = local_file.md5sum
|
local_md5 = local_file.md5sum
|
||||||
remote_md5 = mut_md5.text
|
remote_md5 = mut_md5.text
|
||||||
|
|
||||||
if local_md5 != remote_md5:
|
if local_md5 != remote_md5:
|
||||||
input('<breakpoint> as mismatching md5!')
|
input("<breakpoint> as mismatching md5!")
|
||||||
print('local: ', local_md5)
|
print("local: ", local_md5)
|
||||||
print('remote: ', remote_md5)
|
print("remote: ", remote_md5)
|
||||||
self.datalayer.mark_filepak_validated_state(local_file.file_pak_id, 'mismatch', 'ut4pugs', remote_md5)
|
self.datalayer.mark_filepak_validated_state(local_file.file_pak_id, "mismatch", "ut4pugs", remote_md5)
|
||||||
else:
|
else:
|
||||||
input('<breakpoint> as matching md5! good job')
|
input("<breakpoint> as matching md5! good job")
|
||||||
self.datalayer.mark_filepak_validated_state(local_file.file_pak_id, 'valid', 'ut4pugs', None)
|
self.datalayer.mark_filepak_validated_state(local_file.file_pak_id, "valid", "ut4pugs", None)
|
||||||
|
|
||||||
# print(r)
|
# print(r)
|
||||||
print('xxx')
|
print("xxx")
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import selenium
|
import selenium
|
||||||
import selenium.webdriver
|
import selenium.webdriver
|
||||||
|
|
||||||
URL_CARDS_LIST = 'https://utcc.unrealpugs.com/content'
|
URL_CARDS_LIST = "https://utcc.unrealpugs.com/content"
|
||||||
|
|
||||||
|
|
||||||
class ScrapeUtcc:
|
class ScrapeUtcc:
|
||||||
@ -21,4 +21,3 @@ class ScrapeUtcc:
|
|||||||
|
|
||||||
def _scrape_list_of_content_cards(self):
|
def _scrape_list_of_content_cards(self):
|
||||||
self.browser.get(URL_CARDS_LIST)
|
self.browser.get(URL_CARDS_LIST)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user