first py impl of redirect lines, misc improvements

This commit is contained in:
Mathew Guest 2020-02-24 03:16:20 -07:00
parent 7b2643cf45
commit a81e2d4c46
13 changed files with 422 additions and 108 deletions

5
.gitignore vendored

@ -1 +1,6 @@
*.pak *.pak
build/
dist/
__pycache__
*.egg-info

0
edit-config.sh Normal file → Executable file

@ -0,0 +1,51 @@
{
"uniqueTag": "UTPlusDUELBeginner",
"categories": [
"Duel"
],
"title": "Duel (Beginners)",
"toolTip": "Ultimate 1v1 test of deathmatch skill",
"description": "1v1 Duel with shield timers\n\n<UT.Hub.RulesText_Small>Mutators: UT+, TeamSkins, WeaponSkins, HitSounds, CustomHUD</>\n<UT.Hub.RulesText_Small>Options: MaxPlayers=2, TimeLimit=10, WeaponStay=OFF, Amp=OFF, PickupTimers=ON</>",
"mapPrefixes": [],
"maxMapsInList": 0,
"epicMaps": "/Game/RestrictedAssets/Maps/DM-Chill,/Game/RestrictedAssets/Maps/WIP/DM-ASDF,/Game/EpicInternal/Lea/DM-Lea,/Game/RestrictedAssets/Maps/WIP/DM-Solo,/Game/RestrictedAssets/Maps/WIP/DM-Decktest",
"defaultMap": "/Game/RestrictedAssets/Maps/WIP/DM-ASDF",
"customMapList": [
"/Game/DM-BoneBone_Textured",
"/Game/DM-Campgrounds-G1E/DM-Campgrounds-G1E",
"/Game/DM-Coma/Maps/DM-Coma_A015",
"/Game/Maps/DM-1/DM-Pure_vrc_3",
"/Game/Maps/DM-Deep-03g",
"/Game/Maps/DM-Erase/DM-EraseV2_a04",
"/Game/Maps/DM-Fervor/DM-Fervor_V8",
"/Game/Rankin/DM-Rankin-LE",
"/Game/RestrictedAssets/Maps/DM-1on1-Roughinery",
"/Game/RestrictedAssets/Maps/WIP/DM-BoneCrusher_MC2",
"/Game/RestrictedAssets/Maps/WIP/DM-Echo_b7",
"/Game/RestrictedAssets/Maps/WIP/DM-Focus-LE",
"/Game/RestrictedAssets/Maps/WIP/DM-HyperBlast-UT99/DM-HyperBlast-UT99_metal-1",
"/Game/RestrictedAssets/Maps/WIP/DM-Idoma-UT3/DM-Idoma",
"/Game/_Ransom/Maps/DM-ChillShell-Abs-A/DM-ChillShell-Abs-A",
"/Game/dc/Ironic/DM-Alanis"
],
"maxPlayers": 2,
"maxTeamCount": 2,
"maxTeamSize": 1,
"maxPartySize": 1,
"displayTexture": "Texture2D'/Game/RestrictedAssets/UI/GameModeBadges/GB_Duel.GB_Duel'",
"gameMode" : "/Script/UnrealTournament.UTDuelGame",
"gameOptions" : "?MaxPlayers=2?TimeLimit=10?ignoreidle=1?GoalScore=0?ForceNoBots=1?ForceRespawn=1?mutator=UTPlus,UTPlusMovement,MutHitsounds,MutTeamskins,WeaponSkins,CustomHUD,WeaponReplacement?WTR=/Game/UT+/UTPlus/UT+GrenadeLauncher.UT+GrenadeLauncher_C:/Game/UT+/UTPlus/UT+BioRifle.UT+BioRifle_C?",
"requiredPackages" : [
"CustomHUD",
"MutHitSounds",
"MutTeamSkins",
"WeaponSkins",
"UTPlus",
"UTPlusMovement"
],
"bTeamGame": true,
"bCompetitiveMatch": false,
"optionFlags": 65535,
"bHideFromUI": false,
"epicForceUIVisibility": 0
},

@ -0,0 +1,52 @@
{
"uniqueTag": "MYDUELnogl",
"categories": [
"Duel"
],
"title": "ProMode Duel noGL",
"toolTip": "Ultimate 1v1 test of deathmatch skill",
"description": "1v1 Duel (with ProMode Movement and Weapon Stats)\n\n<UT.Hub.RulesText_Small>Mutators: ProMode, TeamSkins, WeaponSkins, HitSounds, CustomHUD</>\n<UT.Hub.RulesText_Small>Options: MaxPlayers=2, TimeLimit=10, WeaponStay=OFF, Amp=OFF*, PickupTimers=OFF</>",
"mapPrefixes": [],
"maxMapsInList": 0,
"epicMaps": "/Game/RestrictedAssets/Maps/DM-Chill,/Game/RestrictedAssets/Maps/WIP/DM-ASDF,/Game/EpicInternal/Lea/DM-Lea,/Game/RestrictedAssets/Maps/WIP/DM-Solo,/Game/RestrictedAssets/Maps/WIP/DM-Decktest",
"defaultMap": "/Game/RestrictedAssets/Maps/WIP/DM-ASDF",
"customMapList": [
"/Game/DM-BoneBone_Textured",
"/Game/DM-Campgrounds-G1E/DM-Campgrounds-G1E",
"/Game/DM-Coma/Maps/DM-Coma_A015",
"/Game/Maps/DM-1/DM-Pure_vrc_3",
"/Game/Maps/DM-Deep-03g",
"/Game/Maps/DM-Erase/DM-EraseV2_a04",
"/Game/Maps/DM-Fervor/DM-Fervor_V8",
"/Game/Rankin/DM-Rankin-LE",
"/Game/RestrictedAssets/Maps/DM-1on1-Roughinery",
"/Game/RestrictedAssets/Maps/WIP/DM-BoneCrusher_MC2",
"/Game/RestrictedAssets/Maps/WIP/DM-Echo_b7",
"/Game/RestrictedAssets/Maps/WIP/DM-Focus-LE",
"/Game/RestrictedAssets/Maps/WIP/DM-HyperBlast-UT99/DM-HyperBlast-UT99_metal-1",
"/Game/RestrictedAssets/Maps/WIP/DM-Idoma-UT3/DM-Idoma",
"/Game/_Ransom/Maps/DM-ChillShell-Abs-A/DM-ChillShell-Abs-A",
"/Game/dc/Ironic/DM-Alanis"
],
"maxPlayers": 2,
"maxTeamCount": 2,
"maxTeamSize": 1,
"maxPartySize": 1,
"displayTexture": "Texture2D'/Game/RestrictedAssets/UI/GameModeBadges/GB_Duel.GB_Duel'",
"gameMode" : "/Script/UnrealTournament.UTDuelGame",
"gameOptions" : "?MaxPlayers=2?TimeLimit=10?ignoreidle=1?GoalScore=0?ForceNoBots=1?ForceRespawn=1?WTR=/Game/ProMode/Weapons/Pro_GrenadeLauncher.Pro_GrenadeLauncher_C:/Game/Proctf/Weapons/Proctf/Weapons/bp2_Orig_BioRifle.bp2_Orig_BioRifle_C?mutator=ProMode,MutHitsounds,MutTeamSkins,WeaponSkins,CustomHUD,NoPickupTimerMutator,WeaponReplacement",
"requiredPackages" : [
"CustomHUD",
"MutHitSounds",
"MutTeamSkins",
"ProMovement",
"ProWeapons",
"WeaponSkins",
"NoPickupTimerMutator"
],
"bTeamGame": true,
"bCompetitiveMatch": false,
"optionFlags": 65535,
"bHideFromUI": false,
"epicForceUIVisibility": 0
},

@ -0,0 +1,52 @@
{
"uniqueTag": "MYDUEL",
"categories": [
"Duel"
],
"title": "ProMode Duel",
"toolTip": "Ultimate 1v1 test of deathmatch skill",
"description": "1v1 Duel (with ProMode Movement and Weapon Stats)\n\n<UT.Hub.RulesText_Small>Mutators: ProMode, TeamSkins, WeaponSkins, HitSounds, CustomHUD</>\n<UT.Hub.RulesText_Small>Options: MaxPlayers=2, TimeLimit=10, WeaponStay=OFF, Amp=OFF*, PickupTimers=OFF</>",
"mapPrefixes": [],
"maxMapsInList": 0,
"epicMaps": "/Game/RestrictedAssets/Maps/DM-Chill,/Game/RestrictedAssets/Maps/WIP/DM-ASDF,/Game/EpicInternal/Lea/DM-Lea,/Game/RestrictedAssets/Maps/WIP/DM-Solo,/Game/RestrictedAssets/Maps/WIP/DM-Decktest",
"defaultMap": "/Game/RestrictedAssets/Maps/WIP/DM-ASDF",
"customMapList": [
"/Game/DM-BoneBone_Textured",
"/Game/DM-Campgrounds-G1E/DM-Campgrounds-G1E",
"/Game/DM-Coma/Maps/DM-Coma_A015",
"/Game/Maps/DM-1/DM-Pure_vrc_3",
"/Game/Maps/DM-Deep-03g",
"/Game/Maps/DM-Erase/DM-EraseV2_a04",
"/Game/Maps/DM-Fervor/DM-Fervor_V8",
"/Game/Rankin/DM-Rankin-LE",
"/Game/RestrictedAssets/Maps/DM-1on1-Roughinery",
"/Game/RestrictedAssets/Maps/WIP/DM-BoneCrusher_MC2",
"/Game/RestrictedAssets/Maps/WIP/DM-Echo_b7",
"/Game/RestrictedAssets/Maps/WIP/DM-Focus-LE",
"/Game/RestrictedAssets/Maps/WIP/DM-HyperBlast-UT99/DM-HyperBlast-UT99_metal-1",
"/Game/RestrictedAssets/Maps/WIP/DM-Idoma-UT3/DM-Idoma",
"/Game/_Ransom/Maps/DM-ChillShell-Abs-A/DM-ChillShell-Abs-A",
"/Game/dc/Ironic/DM-Alanis"
],
"maxPlayers": 2,
"maxTeamCount": 2,
"maxTeamSize": 1,
"maxPartySize": 1,
"displayTexture": "Texture2D'/Game/RestrictedAssets/UI/GameModeBadges/GB_Duel.GB_Duel'",
"gameMode" : "/Script/UnrealTournament.UTDuelGame",
"gameOptions" : "?MaxPlayers=2?TimeLimit=10?ignoreidle=1?GoalScore=0?ForceNoBots=1?ForceRespawn=1?mutator=ProMovement,ProWeapons,MutHitsounds,MutTeamSkins,WeaponSkins,CustomHUD,NoPickupTimerMutator",
"requiredPackages" : [
"CustomHUD",
"MutHitSounds",
"MutTeamSkins",
"ProMovement",
"ProWeapons",
"WeaponSkins",
"NoPickupTimerMutator"
],
"bTeamGame": true,
"bCompetitiveMatch": false,
"optionFlags": 65535,
"bHideFromUI": false,
"epicForceUIVisibility": 0
},

@ -0,0 +1,53 @@
{
"uniqueTag": "UTPlusDUELnogl",
"categories": [
"Duel"
],
"title": "UT+ Duel noGL",
"toolTip": "Ultimate 1v1 test of deathmatch skill",
"description": "1v1 Duel (with UT+ Movement and Weapon Stats)\n\n<UT.Hub.RulesText_Small>Mutators: UT+, TeamSkins, WeaponSkins, HitSounds, CustomHUD</>\n<UT.Hub.RulesText_Small>Options: MaxPlayers=2, TimeLimit=10, WeaponStay=OFF, Amp=OFF, PickupTimers=OFF</>",
"mapPrefixes": [],
"maxMapsInList": 0,
"epicMaps": "/Game/RestrictedAssets/Maps/DM-Chill,/Game/RestrictedAssets/Maps/WIP/DM-ASDF,/Game/EpicInternal/Lea/DM-Lea,/Game/RestrictedAssets/Maps/WIP/DM-Solo,/Game/RestrictedAssets/Maps/WIP/DM-Decktest",
"defaultMap": "/Game/RestrictedAssets/Maps/WIP/DM-ASDF",
"customMapList": [
"/Game/DM-BoneBone_Textured",
"/Game/DM-Campgrounds-G1E/DM-Campgrounds-G1E",
"/Game/DM-Coma/Maps/DM-Coma_A015",
"/Game/Maps/DM-1/DM-Pure_vrc_3",
"/Game/Maps/DM-Deep-03g",
"/Game/Maps/DM-Erase/DM-EraseV2_a04",
"/Game/Maps/DM-Fervor/DM-Fervor_V8",
"/Game/Rankin/DM-Rankin-LE",
"/Game/RestrictedAssets/Maps/DM-1on1-Roughinery",
"/Game/RestrictedAssets/Maps/WIP/DM-BoneCrusher_MC2",
"/Game/RestrictedAssets/Maps/WIP/DM-Echo_b7",
"/Game/RestrictedAssets/Maps/WIP/DM-Focus-LE",
"/Game/RestrictedAssets/Maps/WIP/DM-HyperBlast-UT99/DM-HyperBlast-UT99_metal-1",
"/Game/RestrictedAssets/Maps/WIP/DM-Idoma-UT3/DM-Idoma",
"/Game/_Ransom/Maps/DM-ChillShell-Abs-A/DM-ChillShell-Abs-A",
"/Game/dc/Ironic/DM-Alanis"
],
"maxPlayers": 2,
"maxTeamCount": 2,
"maxTeamSize": 1,
"maxPartySize": 1,
"displayTexture": "Texture2D'/Game/RestrictedAssets/UI/GameModeBadges/GB_Duel.GB_Duel'",
"gameMode" : "/Script/UnrealTournament.UTDuelGame",
"gameOptions" : "?MaxPlayers=2?TimeLimit=10?ignoreidle=1?GoalScore=0?MaxSpecators=10?ForceNoBots=1?ForceRespawn=1?mutator=UTPlus,UTPlusMovement,MutHitsounds,MutTeamskins,WeaponSkins,CustomHUD,NoPickupTimerMutator,WeaponReplacement?WTR=/Game/UT+/UTPlus/UT+GrenadeLauncher.UT+GrenadeLauncher_C:/Game/UT+/UTPlus/UT+BioRifle.UT+BioRifle_C?",
"requiredPackages" : [
"CustomHUD",
"MutHitSounds",
"MutTeamskins",
"WeaponSkins",
"UTPlus",
"UTPlusMovement",
"NoPickupTimerMutator"
],
"bTeamGame": true,
"bCompetitiveMatch": false,
"optionFlags": 65535,
"bHideFromUI": false,
"minPlayersToStart": 2,
"epicForceUIVisibility": 0
},

@ -0,0 +1,52 @@
{
"uniqueTag": "UTPlusDUEL",
"categories": [
"Duel"
],
"title": "UT+ Duel",
"toolTip": "Ultimate 1v1 test of deathmatch skill",
"description": "1v1 Duel (with UT+ Movement and Weapon Stats)\n\n<UT.Hub.RulesText_Small>Mutators: UT+, TeamSkins, WeaponSkins, HitSounds, CustomHUD</>\n<UT.Hub.RulesText_Small>Options: MaxPlayers=2, TimeLimit=10, WeaponStay=OFF, Amp=OFF, PickupTimers=OFF</>",
"mapPrefixes": [],
"maxMapsInList": 0,
"epicMaps": "/Game/RestrictedAssets/Maps/DM-Chill,/Game/RestrictedAssets/Maps/WIP/DM-ASDF,/Game/EpicInternal/Lea/DM-Lea,/Game/RestrictedAssets/Maps/WIP/DM-Solo,/Game/RestrictedAssets/Maps/WIP/DM-Decktest",
"defaultMap": "/Game/RestrictedAssets/Maps/WIP/DM-ASDF",
"customMapList": [
"/Game/DM-BoneBone_Textured",
"/Game/DM-Campgrounds-G1E/DM-Campgrounds-G1E",
"/Game/DM-Coma/Maps/DM-Coma_A015",
"/Game/Maps/DM-1/DM-Pure_vrc_3",
"/Game/Maps/DM-Deep-03g",
"/Game/Maps/DM-Erase/DM-EraseV2_a04",
"/Game/Maps/DM-Fervor/DM-Fervor_V8",
"/Game/Rankin/DM-Rankin-LE",
"/Game/RestrictedAssets/Maps/DM-1on1-Roughinery",
"/Game/RestrictedAssets/Maps/WIP/DM-BoneCrusher_MC2",
"/Game/RestrictedAssets/Maps/WIP/DM-Echo_b7",
"/Game/RestrictedAssets/Maps/WIP/DM-Focus-LE",
"/Game/RestrictedAssets/Maps/WIP/DM-HyperBlast-UT99/DM-HyperBlast-UT99_metal-1",
"/Game/RestrictedAssets/Maps/WIP/DM-Idoma-UT3/DM-Idoma",
"/Game/_Ransom/Maps/DM-ChillShell-Abs-A/DM-ChillShell-Abs-A",
"/Game/dc/Ironic/DM-Alanis"
],
"maxPlayers": 2,
"maxTeamCount": 2,
"maxTeamSize": 1,
"maxPartySize": 1,
"displayTexture": "Texture2D'/Game/RestrictedAssets/UI/GameModeBadges/GB_Duel.GB_Duel'",
"gameMode" : "/Script/UnrealTournament.UTDuelGame",
"gameOptions" : "?MaxPlayers=2?TimeLimit=10?ignoreidle=1?GoalScore=0?ForceNoBots=1?ForceRespawn=1?mutator=UTPlus,UTPlusMovement,MutHitsounds,MutTeamskins,WeaponSkins,CustomHUD,NoPickupTimerMutator",
"requiredPackages" : [
"CustomHUD",
"MutHitSounds",
"MutTeamSkins",
"WeaponSkins",
"UTPlus",
"UTPlusMovement",
"NoPickupTimerMutator"
],
"bTeamGame": true,
"bCompetitiveMatch": false,
"optionFlags": 65535,
"bHideFromUI": false,
"epicForceUIVisibility": 0
},

@ -1,25 +1,16 @@
#!/usr/bin/env python #!/usr/bin/env python
#
# Usage:
#
# First, enable the python environment you want to install to, or if installing
# system-wide then ensure you're logged in with sufficient permissions
# (admin or root to install to system directories)
#
# installation:
#
# $ ./setup.py install
#
# de-installation:
#
# $ pip uninstall <app>
from setuptools import setup, find_packages
from setuptools import setup __project__ = 'SmileyFace UT4 Hub Automator'
__project__ = 'SmileyFace UT4 Server Panel'
__version__ = '0.1.0' __version__ = '0.1.0'
app_skellington_requirements = (
'appdirs',
'colorlog',
'configobj',
)
setup( setup(
name = __project__, name = __project__,
version = __version__, version = __version__,
@ -30,13 +21,12 @@ setup(
# Third-party dependencies; will be automatically installed # Third-party dependencies; will be automatically installed
install_requires = ( install_requires = (
'app_skellington' 'rdiff-backup',
), ) + app_skellington_requirements,
# Local packages to be installed (our packages)
packages = (
'smileyface',
),
packages = find_packages(),
package_dir = {
'app_skellington': 'lib/app_skellington'
},
) )

10
smileyface/_util.py Normal file

@ -0,0 +1,10 @@
import hashlib
def md5sum_file(filename):
with open(filename, mode='rb') as f:
d = hashlib.md5()
for buf in iter(partial(f.read, 128), b''):
d.update(buf)
h = d.hexdigest()
return h

@ -1,56 +1,41 @@
from . import hub_machine
import app_skellington import app_skellington
from app_skellington import _util from app_skellington import _util
from . import model
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(
'smileyface-ut4',
'app_author',
'hub-config.ini'
)
super().__init__( super().__init__(
configspec_filepath=self.configspec_filepath, configspec_filepath=self.configspec_filepath,
config_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
) )
# super().__init__(
# app_name = 'SmileyFace UT4 Server Panel',
# app_author = 'Mathew Guest',
# app_version = '0.1'
# )
self._load_config()
def _load_config(self, config_file=None):
"""
Parse the config file, (todo) environment variables, and command-line
arguments to determine runtime configuration.
"""
if config_file is None:
config_file = self._get_config_filepath(
'smileyface-ut4',
'app_author',
'hub-config.ini'
)
rc = self.ctx.config.load_config_from_file(config_file)
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')
# sm_root.register_command(model.foo)
# sm_root.register_command(model.bar)
_util.register_class_as_commands( _util.register_class_as_commands(
self, sm_root, self, sm_root,
model.UT4ServerMachine hub_machine.UT4ServerMachine
) )
def _services(self): def _services(self):
self['model'] = lambda: model.UTServerMachine(self.ctx) self['model'] = lambda: hub_machine.UTServerMachine(self.ctx)
def interactive_shell(self): def interactive_shell(self):
pass pass

@ -0,0 +1,82 @@
import configparser
import re
class UnrealIniFile:
def __init__(self, filename=None):
self._config = None
self.filename = filename
@property
def filename(self):
return self._filename
@filename.setter
def filename(self, val):
self._filename = val
if val is not None:
self._config = configparser.RawConfigParser(
strict=False
)
self._config.optionxform = str # Trick to preserve case in key names
self._config.read(self._filename)
class GameIniSpecial:
def __init__(self, filename):
self._redirect_lines = []
self._filename = None
self.filename = filename
@property
def filename(self):
return self._filename
@filename.setter
def filename(self, val):
self._filename = val
def clear_redirect_references(self):
self._redirect_lines = []
def add_redirect_reference(
self, pkg_basename, redirect_url, redirect_protocol,
relative_path, md5sum
):
args = {
'pkg_basename': pkg_basename,
'redirect_protocol': redirect_protocol,
'redirect_url': redirect_url,
'relative_path': relative_path,
'md5sum': md5sum
}
########### START multi-line awkward indent
line = '\
RedirectReferences=(PackageName="{pkg_basename}",\
PackageURLProtocol="{redirect_protocol}",\
PackageURL="{redirect_url}/{relative_path}",\
PackageChecksum="{md5sum}")'.format(**args)
########### END multi-line awkward indent
return self.add_redirect_reference_line(line)
def add_redirect_reference_line(self, line):
self._redirect_lines.append(line)
return line
def write(self, fp):
newcontents = None
with open(self.filename, 'r') as inifile:
curcontents = inifile.read()
lines_str = '\n'.join(self._redirect_lines)
newcontents = re.sub(
'RedirectReferences = :PARAM:',
lines_str,
curcontents
)
has_data = True
if has_data:
with open(self.filename, 'w') as fp:
fp.write(newcontents)

@ -1,3 +1,7 @@
from .config_editor import UnrealIniFile, GameIniSpecial
from . import config_editor
from . import _util
from app_skellington import _util from app_skellington import _util
from functools import partial from functools import partial
import collections import collections
@ -80,7 +84,6 @@ rsync -ravzp {remote_game_host}:{remote_game_dir}/LinuxServer/UnrealTournament/S
}) })
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 = '''
@ -252,14 +255,26 @@ ssh {remote_game_host} chown ut4.ut4 {remote_game_dir} -R
config_dir = self.ctx.config['app']['config_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 = '/'.join([config_dir, fn]) src = os.path.join(config_dir, fn)
# dst = '/'.join([project_dir, 'instance']) # needs corrected dst = os.path.join(project_dir, 'instance/LinuxServer/UnrealTournament/Saved/Config/LinuxServer', fn)
cmd = 'cp {src} {dst}'.format(**{ cmd = 'cp {src} {dst}'.format(**{
'src': '/'.join([config_dir, fn]), 'src': src,
'dst': '/'.join([project_dir, 'instance']) # needs corrected 'dst': dst
}) })
self._invoke_command(cmd) self._invoke_command(cmd)
# Monkey-patch Game.ini to ensure it has a place for RedirectReferences
if fn == 'Game.ini':
ini = UnrealIniFile(dst)
sect_name = '/Script/UnrealTournament.UTBaseGameMode'
opt_name = 'RedirectReferences'
if not ini._config.has_section(sect_name):
ini._config.add_section(sect_name)
if not ini._config.has_option(sect_name, opt_name):
ini._config.set(sect_name, opt_name, ':PARAM:')
with open(dst, 'w') as 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']
@ -278,64 +293,39 @@ ssh {remote_game_host} chown ut4.ut4 {remote_game_dir} -R
self._invoke_command(cmd) self._invoke_command(cmd)
def _install_redirect_lines(self): def _install_redirect_lines(self):
return self.install_redirect_lines()
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'])
files = glob.iglob('{}/**/*.pak'.format(mod_dir))
game_ini_filepath = '/'.join([project_dir, 'instance/LinuxServer/UnrealTournament/Saved/Config/LinuxServer/Game.ini'])
game_ini = GameIniSpecial(game_ini_filepath)
game_ini.clear_redirect_references()
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:
break # break
md5sum = self._md5sum_file(filename) md5sum = _util.md5sum_file(filename)
p = pathlib.Path(filename) p = pathlib.Path(filename)
relative_path = p.relative_to(mod_dir) relative_path = p.relative_to(mod_dir)
basename = p.name pkg_basename = p.name
line = game_ini.add_redirect_reference(**{
'pkg_basename': pkg_basename,
'redirect_protocol': redirect_protocol,
'redirect_url': redirect_url,
'relative_path': relative_path,
'md5sum': md5sum
})
line = '\
RedirectReferences=(PackageName="{basename}",\
PackageURLProtocol="{redirect_protocol}",\
PackageURL="{redirect_url}/{relative_path}",\
PackageChecksum="{md5sum}")'\
.format(**{
'basename': basename,
'redirect_protocol': redirect_protocol,
'redirect_url': redirect_url,
'relative_path': relative_path,
'md5sum': md5sum
})
self.ctx.log['ut4'].debug("redirect line = '%s'", line) self.ctx.log['ut4'].debug("redirect line = '%s'", line)
redirect_lines.append(line)
# end loop - for filename in files:
# START HERE --trying to dynamically add redirect references and data = game_ini.write(sys.stdout)
# gonna need to be able to work with the ini file, including support
# for duplicate keys. Alternatively, maybe do the redirect lines through
# a patch
# I found out the hard way, configobj doesn't seem the best suited for
# duplicate keys...
# install into Game.ini
game_ini_filepath = '/'.join([project_dir, 'files/config/Game.ini'])
# game_ini_data = configobj.ConfigObj(game_ini_filepath)
config = configparser.ConfigParser(
# game_ini_filepath,
# dict_type=MultiOrderedDict,
strict=False
)
s = config.sections()
print(s)
# config.set('/Script/UnrealTournament.UTBaseGameMode', 'RedirectReferences', line)
config.read(game_ini_filepath)
l = config.write(sys.stdout)
print('l', l)
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']
@ -378,14 +368,6 @@ PackageChecksum="{md5sum}")'\
cwd = None # todo(mg) ? cwd = None # todo(mg) ?
os.system(cmd) os.system(cmd)
def _md5sum_file(self, filename):
with open(filename, mode='rb') as f:
d = hashlib.md5()
for buf in iter(partial(f.read, 128), b''):
d.update(buf)
h = d.hexdigest()
return h
def _needs_first_run(self): def _needs_first_run(self):
return False # TODO(MG): Hard-coded return False # TODO(MG): Hard-coded