Skip to content

Commit

Permalink
Webhost: Disallow empty option groups (ArchipelagoMW#3369)
Browse files Browse the repository at this point in the history
* move item_and_loc_options out of the meta class and into the Options module

* don't allow empty world specified option groups

* reuse option_group generation code instead of rewriting it

* delete the default group if it's empty

* indent
  • Loading branch information
alwaysintreble authored and qwint committed Jun 24, 2024
1 parent f8d0c97 commit 31fdbda
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 30 deletions.
44 changes: 32 additions & 12 deletions Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1132,7 +1132,37 @@ class OptionGroup(typing.NamedTuple):
"""Options to be in the defined group."""


def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], generate_hidden: bool = True):
item_and_loc_options = [LocalItems, NonLocalItems, StartInventory, StartInventoryPool, StartHints,
StartLocationHints, ExcludeLocations, PriorityLocations, ItemLinks]
"""
Options that are always populated in "Item & Location Options" Option Group. Cannot be moved to another group.
If desired, a custom "Item & Location Options" Option Group can be defined, but only for adding additional options to
it.
"""


def get_option_groups(world: typing.Type[World], visibility_level: Visibility = Visibility.template) -> typing.Dict[
str, typing.Dict[str, typing.Type[Option[typing.Any]]]]:
"""Generates and returns a dictionary for the option groups of a specified world."""
option_groups = {option: option_group.name
for option_group in world.web.option_groups
for option in option_group.options}
# add a default option group for uncategorized options to get thrown into
ordered_groups = ["Game Options"]
[ordered_groups.append(group) for group in option_groups.values() if group not in ordered_groups]
grouped_options = {group: {} for group in ordered_groups}
for option_name, option in world.options_dataclass.type_hints.items():
if visibility_level & option.visibility:
grouped_options[option_groups.get(option, "Game Options")][option_name] = option

# if the world doesn't have any ungrouped options, this group will be empty so just remove it
if not grouped_options["Game Options"]:
del grouped_options["Game Options"]

return grouped_options


def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], generate_hidden: bool = True) -> None:
import os

import yaml
Expand Down Expand Up @@ -1170,17 +1200,7 @@ def dictify_range(option: Range):

for game_name, world in AutoWorldRegister.world_types.items():
if not world.hidden or generate_hidden:

option_groups = {option: option_group.name
for option_group in world.web.option_groups
for option in option_group.options}
ordered_groups = ["Game Options"]
[ordered_groups.append(group) for group in option_groups.values() if group not in ordered_groups]
grouped_options = {group: {} for group in ordered_groups}
for option_name, option in world.options_dataclass.type_hints.items():
if option.visibility >= Visibility.template:
grouped_options[option_groups.get(option, "Game Options")][option_name] = option

grouped_options = get_option_groups(world)
with open(local_path("data", "options.yaml")) as f:
file_data = f.read()
res = Template(file_data).render(
Expand Down
14 changes: 2 additions & 12 deletions WebHostLib/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,16 @@ def get_world_theme(game_name: str) -> str:


def render_options_page(template: str, world_name: str, is_complex: bool = False) -> Union[Response, str]:
visibility_flag = Options.Visibility.complex_ui if is_complex else Options.Visibility.simple_ui
world = AutoWorldRegister.world_types[world_name]
if world.hidden or world.web.options_page is False:
return redirect("games")

option_groups = {option: option_group.name
for option_group in world.web.option_groups
for option in option_group.options}
ordered_groups = ["Game Options", *[group.name for group in world.web.option_groups]]
grouped_options = {group: {} for group in ordered_groups}
for option_name, option in world.options_dataclass.type_hints.items():
# Exclude settings from options pages if their visibility is disabled
if visibility_flag in option.visibility:
grouped_options[option_groups.get(option, "Game Options")][option_name] = option
visibility_flag = Options.Visibility.complex_ui if is_complex else Options.Visibility.simple_ui

return render_template(
template,
world_name=world_name,
world=world,
option_groups=grouped_options,
option_groups=Options.get_option_groups(world, visibility_level=visibility_flag),
issubclass=issubclass,
Options=Options,
theme=get_world_theme(world_name),
Expand Down
8 changes: 2 additions & 6 deletions worlds/AutoWorld.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
from typing import (Any, Callable, ClassVar, Dict, FrozenSet, List, Mapping, Optional, Set, TextIO, Tuple,
TYPE_CHECKING, Type, Union)

from Options import (
ExcludeLocations, ItemLinks, LocalItems, NonLocalItems, OptionGroup, PerGameCommonOptions,
PriorityLocations, StartHints, StartInventory, StartInventoryPool, StartLocationHints
)
from Options import item_and_loc_options, OptionGroup, PerGameCommonOptions
from BaseClasses import CollectionState

if TYPE_CHECKING:
Expand Down Expand Up @@ -119,12 +116,11 @@ def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> Web
# don't allow an option to appear in multiple groups, allow "Item & Location Options" to appear anywhere by the
# dev, putting it at the end if they don't define options in it
option_groups: List[OptionGroup] = dct.get("option_groups", [])
item_and_loc_options = [LocalItems, NonLocalItems, StartInventory, StartInventoryPool, StartHints,
StartLocationHints, ExcludeLocations, PriorityLocations, ItemLinks]
seen_options = []
item_group_in_list = False
for group in option_groups:
assert group.name != "Game Options", "Game Options is a pre-determined group and can not be defined."
assert group.options, "A custom defined Option Group must contain at least one Option."
if group.name == "Item & Location Options":
group.options.extend(item_and_loc_options)
item_group_in_list = True
Expand Down

0 comments on commit 31fdbda

Please sign in to comment.