mirror of
https://git.zavage.net/Zavage-Software/smileyface.git
synced 2026-06-25 18:12:48 -06:00
refactor: replace configobj spec with pydantic-settings and Click CLI
Drop the config.spec/configobj configuration in favour of an AppSettings pydantic-settings model (settings.py) loaded from SMILEYFACE_-prefixed env vars or a .env file. Introduce AppContext (context.py) to carry settings plus logging, dispatch commands through a Click CLI (cli.py), and centralise log setup (logging_setup.py). Update hub_machine, datalayer, and scraping modules to consume the new context. Add .env.example and ignore .env. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
183fa960c3
commit
709e6f25fa
15
.env.example
Normal file
15
.env.example
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# SmileyFace UT4 Server Configuration
|
||||||
|
# Copy to .env and fill in your values
|
||||||
|
|
||||||
|
SMILEYFACE_PROJECT_DIR=
|
||||||
|
SMILEYFACE_CONFIG_DIR=
|
||||||
|
SMILEYFACE_DOWNLOAD_URL=
|
||||||
|
SMILEYFACE_DOWNLOAD_FILENAME=
|
||||||
|
SMILEYFACE_DOWNLOAD_MD5=
|
||||||
|
SMILEYFACE_SKIP_VALIDATE=false
|
||||||
|
SMILEYFACE_REDIRECT_PROTOCOL=
|
||||||
|
SMILEYFACE_REDIRECT_URL=
|
||||||
|
SMILEYFACE_REMOTE_GAME_HOST=
|
||||||
|
SMILEYFACE_REMOTE_GAME_DIR=
|
||||||
|
SMILEYFACE_REMOTE_REDIRECT_HOST=
|
||||||
|
SMILEYFACE_SQLITE_FILENAME=smiles.db
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,4 +4,5 @@ dist/
|
|||||||
__pycache__
|
__pycache__
|
||||||
*.egg-info
|
*.egg-info
|
||||||
idea
|
idea
|
||||||
|
.env
|
||||||
|
|
||||||
|
|||||||
@ -1,42 +1,4 @@
|
|||||||
import logging
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# 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_VERSION = "0.1.0"
|
APP_VERSION = "0.1.0"
|
||||||
|
|
||||||
APP_CONFIG_FILENAME = "config.ini"
|
|
||||||
|
|
||||||
# config.spec is relative to the module src directory and is the
|
|
||||||
# config specification (structure, names, and types of config file)
|
|
||||||
APP_CONFIGSPEC_FILENAME = "config.spec"
|
|
||||||
|
|
||||||
# Check and gracefully fail if the user needs to install a 3rd-party dep.
|
|
||||||
required_lib_names = ["appdirs", "configobj", "colorlog"]
|
|
||||||
|
|
||||||
|
|
||||||
def check_env_has_dependencies(required_lib_names):
|
|
||||||
"""
|
|
||||||
Attempts to import each module and gracefully fails if it doesn't
|
|
||||||
exist.
|
|
||||||
"""
|
|
||||||
rc = True
|
|
||||||
for libname in required_lib_names:
|
|
||||||
try:
|
|
||||||
__import__(libname)
|
|
||||||
except ImportError as ex:
|
|
||||||
print("missing third-part library: ", ex, file=sys.stderr)
|
|
||||||
rc = False
|
|
||||||
except Exception as ex:
|
|
||||||
print(ex, type(ex))
|
|
||||||
rc = False
|
|
||||||
return rc
|
|
||||||
|
|
||||||
|
|
||||||
if not check_env_has_dependencies(required_lib_names):
|
|
||||||
print("refusing to load program without installed dependencies", file=sys.stderr)
|
|
||||||
raise ImportError("python environment needs third-party dependencies installed")
|
|
||||||
|
|
||||||
# Exposed from sub-modules:
|
|
||||||
from .app import start_app
|
from .app import start_app
|
||||||
|
|||||||
@ -1,130 +1,5 @@
|
|||||||
import app_skellington
|
from .cli import cli
|
||||||
from app_skellington import _util
|
|
||||||
|
|
||||||
from . import (
|
|
||||||
datalayer,
|
|
||||||
hub_machine,
|
|
||||||
scrape_latest,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SmileyFace(app_skellington.ApplicationContainer):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
filename = "config.spec"
|
|
||||||
self.configspec_filepath = _util.get_asset(__name__, filename)
|
|
||||||
|
|
||||||
config_filepath = self._get_config_filepath("smileyface-ut4", "", "hub-config.ini")
|
|
||||||
|
|
||||||
super().__init__(
|
|
||||||
configspec_filepath=self.configspec_filepath,
|
|
||||||
configini_filepath=config_filepath,
|
|
||||||
app_name="SmileyFace UT4 Server Panel",
|
|
||||||
app_author="Mathew Guest",
|
|
||||||
app_version="0.1",
|
|
||||||
*args,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _cli_options(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _command_menu(self):
|
|
||||||
sm_root = self.cli.init_submenu("command")
|
|
||||||
_util.register_class_as_commands(self, sm_root, hub_machine.UT4ServerMachine)
|
|
||||||
|
|
||||||
sm_scrape = sm_root.create_submenu("scrape")
|
|
||||||
_util.register_class_as_commands(self, sm_scrape, scrape_latest.ScrapeUt4Pugs)
|
|
||||||
|
|
||||||
_util.register_class_as_commands(self, sm_scrape, scrape_latest.ScrapeUtcc)
|
|
||||||
|
|
||||||
_util.register_class_as_commands(self, sm_scrape, scrape_latest.LocalFs)
|
|
||||||
|
|
||||||
def _services(self):
|
|
||||||
self["model"] = lambda: hub_machine.UTServerMachine(self.ctx)
|
|
||||||
self.dal = datalayer.DataLayer(self.ctx)
|
|
||||||
self["dal"] = lambda: self.dal
|
|
||||||
self["datalayer"] = lambda: datalayer.DbFuncs(self.ctx, self.dal)
|
|
||||||
|
|
||||||
# self['localfs'] = lambda: datalayer.LocalFs(self.ctx, datalayer)
|
|
||||||
|
|
||||||
def interactive_shell(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def invoke_from_cli(self):
|
|
||||||
rc = self.load_command()
|
|
||||||
if not rc:
|
|
||||||
print("Invalid command. Try -h for usage")
|
|
||||||
return
|
|
||||||
# load config
|
|
||||||
self.invoke_command()
|
|
||||||
|
|
||||||
def usage(self):
|
|
||||||
s = """
|
|
||||||
Unreal Tournament 4 Server Build and Deploy Script
|
|
||||||
|
|
||||||
A list of commands is shown below.
|
|
||||||
|
|
||||||
List commands and usage:
|
|
||||||
./ut4-server-ctl.sh
|
|
||||||
|
|
||||||
Show help for a specific command:
|
|
||||||
./ut4-server-ctl.sh --help <COMMAND>
|
|
||||||
|
|
||||||
Here is the list of sub-commands with short syntax reminder:
|
|
||||||
./ut4-server-ctl.sh 1click-deploy
|
|
||||||
./ut4-server-ctl.sh clean-instance
|
|
||||||
./ut4-server-ctl.sh create-directories
|
|
||||||
./ut4-server-ctl.sh download-linux-server
|
|
||||||
./ut4-server-ctl.sh download-logs
|
|
||||||
./ut4-server-ctl.sh generate-instance
|
|
||||||
./ut4-server-ctl.sh start-server
|
|
||||||
./ut4-server-ctl.sh stop-server
|
|
||||||
./ut4-server-ctl.sh upload-redirects
|
|
||||||
./ut4-server-ctl.sh upload-server
|
|
||||||
|
|
||||||
Typical Usage:
|
|
||||||
1) You need to either configure the remote server hostnames (both game server and remote redirect server)
|
|
||||||
in the vars files. Edit vars with a text editor, or edit the defaults in this file, or manually type
|
|
||||||
them interactively when prompted* (coming soon).
|
|
||||||
|
|
||||||
e.g.:
|
|
||||||
PROJECT_DIR="/path/to/this/script/directory/on/local/machine"
|
|
||||||
REMOTE_GAME_HOST="54.123.456.10"
|
|
||||||
REMOTE_GAME_DIR="/home/ut4/hub-instance"
|
|
||||||
|
|
||||||
REMOTE_REDIRECT_HOST="45.321.654.10"
|
|
||||||
REMOTE_REDIRECT_DIR="/srv/ut4-redirect/"
|
|
||||||
|
|
||||||
2) You need to download the latest Linux Server release from Epic. Do
|
|
||||||
this with the 'download-server' command.
|
|
||||||
|
|
||||||
e.g.:
|
|
||||||
./ut4-server-ctl.sh download-server
|
|
||||||
|
|
||||||
3) Add and configure custom maps, mutators, rulesets, and hub configuration to your *local* project folder.
|
|
||||||
This is done by modifying the files in the project directory. With the current environment variables,
|
|
||||||
this is set to be:
|
|
||||||
|
|
||||||
"$PROJECT_DIR"
|
|
||||||
|
|
||||||
This is the fun part! Connect with UTCC or UTZONE.DE (unaffiliated) to find custom content to put on
|
|
||||||
your hub.
|
|
||||||
|
|
||||||
4) 1click-deploy to update the remote hub and redirect with your latest content. You're done! Rinse and repeat.
|
|
||||||
|
|
||||||
e.g.:
|
|
||||||
./ut4-server-ctl.sh 1click-deploy
|
|
||||||
|
|
||||||
Alternatively, this command is equivalent to running the separate commands. If you'd like more fine-grained
|
|
||||||
control, you can run them individually.
|
|
||||||
./ut4-server-ctl.sh generate-instance
|
|
||||||
./ut4-server-ctl.sh upload-redirects
|
|
||||||
./ut4-server-ctl.sh upload-server
|
|
||||||
./ut4-server-ctl.sh restart-server
|
|
||||||
"""
|
|
||||||
print(s)
|
|
||||||
|
|
||||||
|
|
||||||
def start_app():
|
def start_app():
|
||||||
app = SmileyFace()
|
cli()
|
||||||
app.invoke_from_cli()
|
|
||||||
|
|||||||
145
smileyface/cli.py
Normal file
145
smileyface/cli.py
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import click
|
||||||
|
|
||||||
|
from smileyface.context import AppContext
|
||||||
|
from smileyface.logging_setup import setup_logging
|
||||||
|
from smileyface.settings import AppSettings
|
||||||
|
|
||||||
|
|
||||||
|
def _make_ctx():
|
||||||
|
"""Build the application context, datalayer, and command instances."""
|
||||||
|
from smileyface import datalayer, hub_machine, scrape_latest
|
||||||
|
|
||||||
|
settings = AppSettings()
|
||||||
|
setup_logging()
|
||||||
|
ctx = AppContext(settings)
|
||||||
|
dal = datalayer.DataLayer(ctx)
|
||||||
|
db = datalayer.DbFuncs(ctx, dal)
|
||||||
|
machine = hub_machine.UT4ServerMachine(ctx, db)
|
||||||
|
scrape_pugs = scrape_latest.ScrapeUt4Pugs(ctx, db)
|
||||||
|
scrape_utcc = scrape_latest.ScrapeUtcc(ctx, db)
|
||||||
|
local_fs = scrape_latest.LocalFs(ctx, db)
|
||||||
|
return machine, scrape_pugs, scrape_utcc, local_fs
|
||||||
|
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def cli():
|
||||||
|
"""SmileyFace UT4 Server Panel"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("oneclickdeploy")
|
||||||
|
def oneclickdeploy():
|
||||||
|
"""Generate instance, upload redirects, and upload server."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.oneclickdeploy()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("clean-instance")
|
||||||
|
def clean_instance():
|
||||||
|
"""Deletes the generated instance on the local machine."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.clean_instance()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("create-directories")
|
||||||
|
def create_directories():
|
||||||
|
"""Create required directories for maps, mutators, and config."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.create_directories()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("download-linux-server")
|
||||||
|
def download_linux_server():
|
||||||
|
"""Download the latest Linux UT4 Server from Epic."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.download_linux_server()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("download-logs")
|
||||||
|
def download_logs():
|
||||||
|
"""Download the logs from the target hub."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.download_logs()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("generate-instance")
|
||||||
|
def generate_instance():
|
||||||
|
"""Build local server instance from current configuration."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.generate_instance()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("restart-server")
|
||||||
|
def restart_server():
|
||||||
|
"""Restart the UT4 server."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.restart_server()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("start-server")
|
||||||
|
def start_server():
|
||||||
|
"""Start the UT4 server."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.start_server()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("stop-server")
|
||||||
|
def stop_server():
|
||||||
|
"""Stop the UT4 server."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.stop_server()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("upload-redirects")
|
||||||
|
def upload_redirects():
|
||||||
|
"""Upload paks to redirect server."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.upload_redirects()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("upload-server")
|
||||||
|
def upload_server():
|
||||||
|
"""Upload game files to the hub server."""
|
||||||
|
machine, *_ = _make_ctx()
|
||||||
|
machine.upload_server()
|
||||||
|
|
||||||
|
|
||||||
|
@cli.group("scrape")
|
||||||
|
def scrape():
|
||||||
|
"""Web scraping subcommands."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@scrape.command("ut4pugs")
|
||||||
|
def scrape_ut4pugs():
|
||||||
|
"""Check ut4pugs.us for latest content."""
|
||||||
|
_, pugs, *_ = _make_ctx()
|
||||||
|
pugs.check_ut4pugs_for_latest()
|
||||||
|
|
||||||
|
|
||||||
|
@scrape.command("utcc")
|
||||||
|
def scrape_utcc_cmd():
|
||||||
|
"""Check utcc.unrealpugs.com for latest content."""
|
||||||
|
*_, utcc, _ = _make_ctx()
|
||||||
|
utcc.check_ut4cc_for_latest()
|
||||||
|
|
||||||
|
|
||||||
|
@scrape.command("create-db-table")
|
||||||
|
def create_db_table():
|
||||||
|
"""Create database tables."""
|
||||||
|
*_, local_fs = _make_ctx()
|
||||||
|
local_fs.create_db_table()
|
||||||
|
|
||||||
|
|
||||||
|
@scrape.command("load-md5s")
|
||||||
|
def load_md5s():
|
||||||
|
"""Load MD5 checksums from local pak files."""
|
||||||
|
*_, local_fs = _make_ctx()
|
||||||
|
local_fs.load_md5s()
|
||||||
|
|
||||||
|
|
||||||
|
@scrape.command("print-invalid")
|
||||||
|
def print_invalid():
|
||||||
|
"""Print pak files that failed validation."""
|
||||||
|
*_, local_fs = _make_ctx()
|
||||||
|
local_fs.print_invalid_filepaks()
|
||||||
@ -1,62 +0,0 @@
|
|||||||
[app]
|
|
||||||
project_dir = string(max=255, default='')
|
|
||||||
config_dir = string(max=255, default='')
|
|
||||||
download_url = string(max=255, default='https://s3.amazonaws.com/unrealtournament/ShippedBuilds/%2B%2BUT%2BRelease-Next-CL-3525360/UnrealTournament-Server-XAN-3525360-Linux.zip')
|
|
||||||
download_filename = string(max=255, default='UnrealTournament-Server-XAN-3525360-Linux.zip')
|
|
||||||
download_md5 = string(max=255, default='cad730ad6793ba6261f9a341ad7396eb')
|
|
||||||
skip_validate = boolean(default=False)
|
|
||||||
redirect_protocol = string(max=255, default='')
|
|
||||||
redirect_url = string(max=255, default='')
|
|
||||||
remote_game_host = string(max=255, default='')
|
|
||||||
remote_game_dir = string(max=255, default='')
|
|
||||||
remote_redirect_host = string(max=255, default='')
|
|
||||||
|
|
||||||
sqlite_filename = string(max=255, default='smiles.db')
|
|
||||||
|
|
||||||
[logging]
|
|
||||||
log_file = string(max=255, default='')
|
|
||||||
log_level = option('critical', 'error', 'warning', 'info', 'debug', default='info')
|
|
||||||
log_fmt = string(max=255, default='')
|
|
||||||
disable_existing_loggers = boolean(default=False)
|
|
||||||
|
|
||||||
[[formatters]]
|
|
||||||
[[[colored]]]
|
|
||||||
() = string(default='colorlog.ColoredFormatter')
|
|
||||||
format = string(max=255, default='%(log_color)s%(levelname)-8s%(reset)s:%(log_color)s%(name)-5s%(reset)s:%(white)s%(message)s')
|
|
||||||
|
|
||||||
[[[basic]]]
|
|
||||||
() = string(max=255, default='logging.Formatter')
|
|
||||||
format = string(max=255, default='%(levelname)s:%(name)s:%(asctime)s:%(message)s')
|
|
||||||
|
|
||||||
[[[forstorage]]]
|
|
||||||
() = string(max=255, default='logging.Formatter')
|
|
||||||
format = string(max=255, default='%(levelname)s:%(name)s:%(asctime)s:%(message)s')
|
|
||||||
|
|
||||||
[[handlers]]
|
|
||||||
[[[stderr]]]
|
|
||||||
class = string(max=255, default='logging.StreamHandler')
|
|
||||||
level = option('critical', 'error', 'warning', 'info', 'debug', default='debug')
|
|
||||||
formatter = string(max=255, default='colored')
|
|
||||||
|
|
||||||
[[[file]]]
|
|
||||||
class = string(max=255, default='logging.handlers.RotatingFileHandler')
|
|
||||||
level = option('critical', 'error', 'warning', 'info', 'debug', default='warning')
|
|
||||||
formatter = string(max=255, default='forstorage')
|
|
||||||
filename = string(max=255, default='cas_admin.log')
|
|
||||||
maxBytes = integer(min=0, max=33554432, default=33554432)
|
|
||||||
backupCount = integer(min=0, max=3, default=1)
|
|
||||||
|
|
||||||
[[loggers]]
|
|
||||||
[[[root]]]
|
|
||||||
level = option('critical', 'error', 'warning', 'info', 'debug', default='debug')
|
|
||||||
handlers = string_list(max=8, default=list('file',))
|
|
||||||
|
|
||||||
[[[ut4]]]
|
|
||||||
level = option('critical', 'error', 'warning', 'info', 'debug', default='debug')
|
|
||||||
handlers = string_list(max=8, default=list('stderr',))
|
|
||||||
propagate = boolean(default=True)
|
|
||||||
|
|
||||||
[[[db]]]
|
|
||||||
level = option('critical', 'error', 'warning', 'info', 'debug', default='debug')
|
|
||||||
handlers = string_list(max=8, default=list('stderr',))
|
|
||||||
propagate = boolean(default=True)
|
|
||||||
16
smileyface/context.py
Normal file
16
smileyface/context.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from smileyface.settings import AppSettings
|
||||||
|
|
||||||
|
|
||||||
|
class AppContext:
|
||||||
|
def __init__(self, settings: AppSettings):
|
||||||
|
self.settings = settings
|
||||||
|
self.log = _LoggerDict()
|
||||||
|
|
||||||
|
|
||||||
|
class _LoggerDict:
|
||||||
|
"""Dict-like logger access: ctx.log["ut4"] -> logging.getLogger("ut4")"""
|
||||||
|
|
||||||
|
def __getitem__(self, name: str) -> logging.Logger:
|
||||||
|
return logging.getLogger(name)
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
import appdirs
|
import platformdirs
|
||||||
|
|
||||||
from smileyface import myutil
|
from smileyface import myutil
|
||||||
|
|
||||||
@ -18,8 +18,8 @@ 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.settings.sqlite_filename
|
||||||
appdir = appdirs.user_data_dir("smileyface")
|
appdir = platformdirs.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)
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import app_skellington._util as apputil
|
from importlib.resources import files
|
||||||
import appdirs
|
|
||||||
import sqlparse
|
import sqlparse
|
||||||
|
|
||||||
from smileyface import myutil, structs
|
from smileyface import myutil, structs
|
||||||
@ -14,8 +14,8 @@ 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_ref = files("smileyface.datalayer").joinpath("create_schema.sql")
|
||||||
with open(sql_filename) as fp:
|
with sql_ref.open("r") as fp:
|
||||||
contents_sql = fp.read()
|
contents_sql = fp.read()
|
||||||
stmts = sqlparse.split(contents_sql)
|
stmts = sqlparse.split(contents_sql)
|
||||||
|
|
||||||
|
|||||||
@ -8,8 +8,6 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import configobj
|
|
||||||
|
|
||||||
from . import myutil, structs
|
from . import myutil, structs
|
||||||
from ._util import md5sum_file
|
from ._util import md5sum_file
|
||||||
from .gameconfig_edit import GameIniSpecial, UnrealIniFile
|
from .gameconfig_edit import GameIniSpecial, UnrealIniFile
|
||||||
@ -28,7 +26,7 @@ class UT4ServerMachine:
|
|||||||
self.upload_redirects()
|
self.upload_redirects()
|
||||||
self.upload_server()
|
self.upload_server()
|
||||||
|
|
||||||
def clean_instance(self, x):
|
def clean_instance(self):
|
||||||
"""
|
"""
|
||||||
Deletes the generated instance on the local machine.
|
Deletes the generated instance on the local machine.
|
||||||
"""
|
"""
|
||||||
@ -41,7 +39,7 @@ 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 = ("base", "files/config", "files/maps", "files/mutators", "files/rulesets", "files/unused")
|
dirs = ("base", "files/config", "files/maps", "files/mutators", "files/rulesets", "files/unused")
|
||||||
project_dir = self.ctx.config["app"]["project_dir"]
|
project_dir = self.ctx.settings.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)
|
||||||
@ -51,7 +49,7 @@ class UT4ServerMachine:
|
|||||||
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):
|
||||||
"""
|
"""
|
||||||
Download the latest Linux Unreal Tournament 4 Server from Epic
|
Download the latest Linux Unreal Tournament 4 Server from Epic
|
||||||
"""
|
"""
|
||||||
@ -61,9 +59,9 @@ class UT4ServerMachine:
|
|||||||
"""
|
"""
|
||||||
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.settings.config_dir
|
||||||
remote_game_host = self.ctx.config["app"]["remote_game_host"]
|
remote_game_host = self.ctx.settings.remote_game_host
|
||||||
remote_game_dir = self.ctx.config["app"]["remote_game_dir"]
|
remote_game_dir = self.ctx.settings.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 = """
|
||||||
@ -88,7 +86,7 @@ ssh {remote_game_host} rm {remote_game_dir}/LinuxServer/UnrealTournament/Saved/L
|
|||||||
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.settings.project_dir
|
||||||
|
|
||||||
# rsync
|
# rsync
|
||||||
src = "/".join([project_dir, "base/LinuxServer"])
|
src = "/".join([project_dir, "base/LinuxServer"])
|
||||||
@ -146,10 +144,10 @@ ssh {remote_game_host} {remote_game_dir}/stop-server.sh
|
|||||||
"""
|
"""
|
||||||
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.settings.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.settings.remote_redirect_host
|
||||||
cwd = project_dir
|
cwd = project_dir
|
||||||
cmd = """
|
cmd = """
|
||||||
rsync -rivz \
|
rsync -rivz \
|
||||||
@ -174,8 +172,8 @@ 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.settings.project_dir
|
||||||
remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"]
|
remote_redirect_host = self.ctx.settings.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"
|
||||||
@ -197,8 +195,8 @@ ssh mathewguest.com \
|
|||||||
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.settings.project_dir
|
||||||
remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"]
|
remote_redirect_host = self.ctx.settings.remote_redirect_host
|
||||||
cmd = """
|
cmd = """
|
||||||
rsync -vz \
|
rsync -vz \
|
||||||
{project_dir}/ut4-server-ctl.sh \
|
{project_dir}/ut4-server-ctl.sh \
|
||||||
@ -213,8 +211,8 @@ rsync -vz \
|
|||||||
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.settings.project_dir
|
||||||
remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"]
|
remote_redirect_host = self.ctx.settings.remote_redirect_host
|
||||||
|
|
||||||
cmd = """
|
cmd = """
|
||||||
ssh mathewguest.com \
|
ssh mathewguest.com \
|
||||||
@ -227,9 +225,9 @@ ssh mathewguest.com \
|
|||||||
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.settings.project_dir
|
||||||
remote_game_host = self.ctx.config["app"]["remote_game_host"]
|
remote_game_host = self.ctx.settings.remote_game_host
|
||||||
remote_game_dir = self.ctx.config["app"]["remote_game_dir"]
|
remote_game_dir = self.ctx.settings.remote_game_dir
|
||||||
cwd = None
|
cwd = None
|
||||||
|
|
||||||
# transfer #1
|
# transfer #1
|
||||||
@ -304,7 +302,7 @@ ssh {remote_game_host} \
|
|||||||
|
|
||||||
# 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.settings.project_dir
|
||||||
|
|
||||||
cwd = "{project_dir}/instance/LinuxServer/Engine/Binaries/Linux".format(project_dir=project_dir)
|
cwd = "{project_dir}/instance/LinuxServer/Engine/Binaries/Linux".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)
|
||||||
@ -325,8 +323,8 @@ ssh {remote_game_host} \
|
|||||||
|
|
||||||
def _install_config(self):
|
def _install_config(self):
|
||||||
files = ("Game.ini", "Engine.ini")
|
files = ("Game.ini", "Engine.ini")
|
||||||
project_dir = self.ctx.config["app"]["project_dir"]
|
project_dir = self.ctx.settings.project_dir
|
||||||
config_dir = self.ctx.config["app"]["config_dir"]
|
config_dir = self.ctx.settings.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)
|
||||||
@ -347,7 +345,7 @@ ssh {remote_game_host} \
|
|||||||
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.settings.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(
|
||||||
@ -370,9 +368,9 @@ ssh {remote_game_host} \
|
|||||||
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.settings.redirect_protocol
|
||||||
redirect_url = self.ctx.config["app"]["redirect_url"]
|
redirect_url = self.ctx.settings.redirect_url
|
||||||
project_dir = self.ctx.config["app"]["project_dir"]
|
project_dir = self.ctx.settings.project_dir
|
||||||
mod_dir = "/".join([project_dir, "files"])
|
mod_dir = "/".join([project_dir, "files"])
|
||||||
|
|
||||||
game_ini_filepath = "/".join(
|
game_ini_filepath = "/".join(
|
||||||
@ -415,7 +413,7 @@ ssh {remote_game_host} \
|
|||||||
|
|
||||||
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.settings.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"])
|
||||||
@ -464,7 +462,7 @@ ssh {remote_game_host} \
|
|||||||
"remote_redirect_host",
|
"remote_redirect_host",
|
||||||
)
|
)
|
||||||
for name in variable_names:
|
for name in variable_names:
|
||||||
value = self.ctx.config["app"][name]
|
value = getattr(self.ctx.settings, 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):")
|
||||||
|
|||||||
41
smileyface/logging_setup.py
Normal file
41
smileyface/logging_setup.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import logging
|
||||||
|
import logging.config
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging(log_file: str = ""):
|
||||||
|
config = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": False,
|
||||||
|
"formatters": {
|
||||||
|
"colored": {
|
||||||
|
"format": "%(levelname)-8s:%(name)-5s:%(message)s",
|
||||||
|
},
|
||||||
|
"basic": {
|
||||||
|
"format": "%(levelname)s:%(name)s:%(asctime)s:%(message)s",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"stderr": {
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"level": "DEBUG",
|
||||||
|
"formatter": "colored",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"ut4": {"level": "DEBUG", "handlers": ["stderr"], "propagate": True},
|
||||||
|
"db": {"level": "DEBUG", "handlers": ["stderr"], "propagate": True},
|
||||||
|
},
|
||||||
|
"root": {"level": "DEBUG", "handlers": []},
|
||||||
|
}
|
||||||
|
if log_file:
|
||||||
|
config["handlers"]["file"] = {
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"level": "WARNING",
|
||||||
|
"formatter": "basic",
|
||||||
|
"filename": log_file,
|
||||||
|
"maxBytes": 33554432,
|
||||||
|
"backupCount": 1,
|
||||||
|
}
|
||||||
|
config["root"]["handlers"].append("file")
|
||||||
|
|
||||||
|
logging.config.dictConfig(config)
|
||||||
@ -13,7 +13,7 @@ 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.settings.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)
|
||||||
|
|||||||
22
smileyface/settings.py
Normal file
22
smileyface/settings.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
|
class AppSettings(BaseSettings):
|
||||||
|
model_config = SettingsConfigDict(
|
||||||
|
env_prefix="SMILEYFACE_",
|
||||||
|
env_file=".env",
|
||||||
|
env_file_encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
project_dir: str = ""
|
||||||
|
config_dir: str = ""
|
||||||
|
download_url: str = ""
|
||||||
|
download_filename: str = ""
|
||||||
|
download_md5: str = ""
|
||||||
|
skip_validate: bool = False
|
||||||
|
redirect_protocol: str = ""
|
||||||
|
redirect_url: str = ""
|
||||||
|
remote_game_host: str = ""
|
||||||
|
remote_game_dir: str = ""
|
||||||
|
remote_redirect_host: str = ""
|
||||||
|
sqlite_filename: str = "smiles.db"
|
||||||
Loading…
Reference in New Issue
Block a user