Skip to content

Commit b5ec3b6

Browse files
black-sliverel-uZach Parks
authored andcommitted
Core: Add settings API ("auto settings") for host.yaml (ArchipelagoMW#1871)
* Add settings API ("auto settings") for host.yaml * settings: no BOM when saving * settings: fix saving / groups resetting themselves * settings: fix AutoWorldRegister import Co-authored-by: el-u <[email protected]> * Lufia2: settings: clean up imports * settings: more consistent class naming * Docs: update world api for settings api refactor * settings: fix access from World instance * settings: update migration timeline * Docs: Apply suggestions from code review Co-authored-by: Zach Parks <[email protected]> * Settings: correctly resolve .exe in UserPath and LocalPath --------- Co-authored-by: el-u <[email protected]> Co-authored-by: Zach Parks <[email protected]>
1 parent b2eafab commit b5ec3b6

34 files changed

+1454
-411
lines changed

.github/workflows/unittests.yml

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ jobs:
6565
python -m pip install --upgrade pip
6666
pip install pytest pytest-subtests
6767
python ModuleUpdate.py --yes --force --append "WebHostLib/requirements.txt"
68+
python Launcher.py --update_settings # make sure host.yaml exists for tests
6869
- name: Unittests
6970
run: |
7071
pytest

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ README.html
3737
EnemizerCLI/
3838
/Players/
3939
/SNI/
40+
/host.yaml
4041
/options.yaml
4142
/config.yaml
4243
/logs/

Generate.py

+20-20
Original file line numberDiff line numberDiff line change
@@ -14,44 +14,42 @@
1414

1515
ModuleUpdate.update()
1616

17+
import copy
1718
import Utils
19+
import Options
20+
from BaseClasses import seeddigits, get_seed, PlandoOptions
21+
from Main import main as ERmain
22+
from settings import get_settings
23+
from Utils import parse_yamls, version_tuple, __version__, tuplize_version, user_path
1824
from worlds.alttp import Options as LttPOptions
19-
from worlds.generic import PlandoConnection
20-
from Utils import parse_yamls, version_tuple, __version__, tuplize_version, get_options, user_path
2125
from worlds.alttp.EntranceRandomizer import parse_arguments
22-
from Main import main as ERmain
23-
from BaseClasses import seeddigits, get_seed, PlandoOptions
24-
import Options
2526
from worlds.alttp.Text import TextTable
2627
from worlds.AutoWorld import AutoWorldRegister
27-
import copy
28+
from worlds.generic import PlandoConnection
2829

2930

3031
def mystery_argparse():
31-
options = get_options()
32-
defaults = options["generator"]
33-
34-
def resolve_path(path: str, resolver: Callable[[str], str]) -> str:
35-
return path if os.path.isabs(path) else resolver(path)
32+
options = get_settings()
33+
defaults = options.generator
3634

3735
parser = argparse.ArgumentParser(description="CMD Generation Interface, defaults come from host.yaml.")
38-
parser.add_argument('--weights_file_path', default=defaults["weights_file_path"],
36+
parser.add_argument('--weights_file_path', default=defaults.weights_file_path,
3937
help='Path to the weights file to use for rolling game settings, urls are also valid')
4038
parser.add_argument('--samesettings', help='Rolls settings per weights file rather than per player',
4139
action='store_true')
42-
parser.add_argument('--player_files_path', default=resolve_path(defaults["player_files_path"], user_path),
40+
parser.add_argument('--player_files_path', default=defaults.player_files_path,
4341
help="Input directory for player files.")
4442
parser.add_argument('--seed', help='Define seed number to generate.', type=int)
45-
parser.add_argument('--multi', default=defaults["players"], type=lambda value: max(int(value), 1))
46-
parser.add_argument('--spoiler', type=int, default=defaults["spoiler"])
47-
parser.add_argument('--outputpath', default=resolve_path(options["general_options"]["output_path"], user_path),
43+
parser.add_argument('--multi', default=defaults.players, type=lambda value: max(int(value), 1))
44+
parser.add_argument('--spoiler', type=int, default=defaults.spoiler)
45+
parser.add_argument('--outputpath', default=options.general_options.output_path,
4846
help="Path to output folder. Absolute or relative to cwd.") # absolute or relative to cwd
49-
parser.add_argument('--race', action='store_true', default=defaults["race"])
50-
parser.add_argument('--meta_file_path', default=defaults["meta_file_path"])
47+
parser.add_argument('--race', action='store_true', default=defaults.race)
48+
parser.add_argument('--meta_file_path', default=defaults.meta_file_path)
5149
parser.add_argument('--log_level', default='info', help='Sets log level')
5250
parser.add_argument('--yaml_output', default=0, type=lambda value: max(int(value), 0),
5351
help='Output rolled mystery results to yaml up to specified number (made for async multiworld)')
54-
parser.add_argument('--plando', default=defaults["plando_options"],
52+
parser.add_argument('--plando', default=defaults.plando_options,
5553
help='List of options that can be set manually. Can be combined, for example "bosses, items"')
5654
parser.add_argument("--skip_prog_balancing", action="store_true",
5755
help="Skip progression balancing step during generation.")
@@ -71,6 +69,8 @@ def get_seed_name(random_source) -> str:
7169
def main(args=None, callback=ERmain):
7270
if not args:
7371
args, options = mystery_argparse()
72+
else:
73+
options = get_settings()
7474

7575
seed = get_seed(args.seed)
7676
Utils.init_logging(f"Generate_{seed}", loglevel=args.log_level)
@@ -137,7 +137,7 @@ def main(args=None, callback=ERmain):
137137
erargs = parse_arguments(['--multi', str(args.multi)])
138138
erargs.seed = seed
139139
erargs.plando_options = args.plando
140-
erargs.glitch_triforce = options["generator"]["glitch_triforce_room"]
140+
erargs.glitch_triforce = options.generator.glitch_triforce_room
141141
erargs.spoiler = args.spoiler
142142
erargs.race = args.race
143143
erargs.outputname = seed_name

Launcher.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from typing import Sequence, Union, Optional
2323

2424
import Utils
25+
import settings
2526
from worlds.LauncherComponents import Component, components, Type, SuffixIdentifier, icon_paths
2627

2728
if __name__ == "__main__":
@@ -33,7 +34,8 @@
3334

3435

3536
def open_host_yaml():
36-
file = user_path('host.yaml')
37+
file = settings.get_settings().filename
38+
assert file, "host.yaml missing"
3739
if is_linux:
3840
exe = which('sensible-editor') or which('gedit') or \
3941
which('xdg-open') or which('gnome-open') or which('kde-open')
@@ -84,6 +86,11 @@ def open_folder(folder_path):
8486
webbrowser.open(folder_path)
8587

8688

89+
def update_settings():
90+
from settings import get_settings
91+
get_settings().save()
92+
93+
8794
components.extend([
8895
# Functions
8996
Component("Open host.yaml", func=open_host_yaml),
@@ -256,11 +263,13 @@ def main(args: Optional[Union[argparse.Namespace, dict]] = None):
256263
if not component:
257264
logging.warning(f"Could not identify Component responsible for {args['Patch|Game|Component']}")
258265

266+
if args["update_settings"]:
267+
update_settings()
259268
if 'file' in args:
260269
run_component(args["component"], args["file"], *args["args"])
261270
elif 'component' in args:
262271
run_component(args["component"], *args["args"])
263-
else:
272+
elif not args["update_settings"]:
264273
run_gui()
265274

266275

@@ -269,9 +278,13 @@ def main(args: Optional[Union[argparse.Namespace, dict]] = None):
269278
Utils.freeze_support()
270279
multiprocessing.set_start_method("spawn") # if launched process uses kivy, fork won't work
271280
parser = argparse.ArgumentParser(description='Archipelago Launcher')
272-
parser.add_argument('Patch|Game|Component', type=str, nargs='?',
273-
help="Pass either a patch file, a generated game or the name of a component to run.")
274-
parser.add_argument('args', nargs="*", help="Arguments to pass to component.")
281+
run_group = parser.add_argument_group("Run")
282+
run_group.add_argument("--update_settings", action="store_true",
283+
help="Update host.yaml and exit.")
284+
run_group.add_argument("Patch|Game|Component", type=str, nargs="?",
285+
help="Pass either a patch file, a generated game or the name of a component to run.")
286+
run_group.add_argument("args", nargs="*",
287+
help="Arguments to pass to component.")
275288
main(parser.parse_args())
276289

277290
from worlds.LauncherComponents import processes

Main.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
from BaseClasses import CollectionState, Item, Location, LocationProgressType, MultiWorld, Region
1414
from Fill import balance_multiworld_progression, distribute_items_restrictive, distribute_planned, flood_items
1515
from Options import StartInventoryPool
16-
from Utils import __version__, get_options, output_path, version_tuple
16+
from settings import get_settings
17+
from Utils import __version__, output_path, version_tuple
1718
from worlds import AutoWorld
1819
from worlds.generic.Rules import exclusion_rules, locality_rules
1920

@@ -22,7 +23,7 @@
2223

2324
def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = None):
2425
if not baked_server_options:
25-
baked_server_options = get_options()["server_options"]
26+
baked_server_options = get_settings().server_options
2627
if args.outputpath:
2728
os.makedirs(args.outputpath, exist_ok=True)
2829
output_path.cached_path = args.outputpath
@@ -371,7 +372,7 @@ def precollect_hint(location):
371372
"connect_names": {name: (0, player) for player, name in world.player_name.items()},
372373
"locations": locations_data,
373374
"checks_in_area": checks_in_area,
374-
"server_options": baked_server_options,
375+
"server_options": baked_server_options.as_dict(),
375376
"er_hint_data": er_hint_data,
376377
"precollected_items": precollected_items,
377378
"precollected_hints": precollected_hints,

MinecraftClient.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ def is_correct_forge(forge_dir) -> bool:
299299

300300
versions = get_minecraft_versions(data_version, channel)
301301

302-
forge_dir = Utils.user_path(options["minecraft_options"]["forge_directory"])
302+
forge_dir = options["minecraft_options"]["forge_directory"]
303303
max_heap = options["minecraft_options"]["max_heap_size"]
304304
forge_version = args.forge or versions["forge"]
305305
java_version = args.java or versions["java"]

OoTClient.py

-2
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,6 @@ async def patch_and_run_game(apz5_file):
296296
comp_path = base_name + '.z64'
297297
# Load vanilla ROM, patch file, compress ROM
298298
rom_file_name = Utils.get_options()["oot_options"]["rom_file"]
299-
if not os.path.exists(rom_file_name):
300-
rom_file_name = Utils.user_path(rom_file_name)
301299
rom = Rom(rom_file_name)
302300

303301
sub_file = None

0 commit comments

Comments
 (0)