Core: change yaml_output to output a full csv (#3653)
* make yaml_output arg a bool instead of number * make yaml_output dump all player options as csv * it sorts by game so swap those columns * capitalize game and name headers * use a list and just add an if before adding instead of sorting * skip options that the world doesn't want displayed * check if the class is a subclass of Removed specifically instead of the none flag * don't create empty rows * add a header for every game option that isn't from the common ones even if they have the same name * add to webhost gen args so it can still gen
This commit is contained in:
parent
f73c0d9894
commit
69487661dd
31
Generate.py
31
Generate.py
|
@ -43,10 +43,10 @@ def mystery_argparse():
|
||||||
parser.add_argument('--race', action='store_true', default=defaults.race)
|
parser.add_argument('--race', action='store_true', default=defaults.race)
|
||||||
parser.add_argument('--meta_file_path', default=defaults.meta_file_path)
|
parser.add_argument('--meta_file_path', default=defaults.meta_file_path)
|
||||||
parser.add_argument('--log_level', default='info', help='Sets log level')
|
parser.add_argument('--log_level', default='info', help='Sets log level')
|
||||||
parser.add_argument('--yaml_output', default=0, type=lambda value: max(int(value), 0),
|
parser.add_argument("--yaml_output", action="store_true",
|
||||||
help='Output rolled mystery results to yaml up to specified number (made for async multiworld)')
|
help="Output rolled player options to csv (made for async multiworld).")
|
||||||
parser.add_argument('--plando', default=defaults.plando_options,
|
parser.add_argument("--plando", default=defaults.plando_options,
|
||||||
help='List of options that can be set manually. Can be combined, for example "bosses, items"')
|
help="List of options that can be set manually. Can be combined, for example \"bosses, items\"")
|
||||||
parser.add_argument("--skip_prog_balancing", action="store_true",
|
parser.add_argument("--skip_prog_balancing", action="store_true",
|
||||||
help="Skip progression balancing step during generation.")
|
help="Skip progression balancing step during generation.")
|
||||||
parser.add_argument("--skip_output", action="store_true",
|
parser.add_argument("--skip_output", action="store_true",
|
||||||
|
@ -156,6 +156,7 @@ def main(args=None) -> Tuple[argparse.Namespace, int]:
|
||||||
erargs.skip_prog_balancing = args.skip_prog_balancing
|
erargs.skip_prog_balancing = args.skip_prog_balancing
|
||||||
erargs.skip_output = args.skip_output
|
erargs.skip_output = args.skip_output
|
||||||
erargs.name = {}
|
erargs.name = {}
|
||||||
|
erargs.yaml_output = args.yaml_output
|
||||||
|
|
||||||
settings_cache: Dict[str, Tuple[argparse.Namespace, ...]] = \
|
settings_cache: Dict[str, Tuple[argparse.Namespace, ...]] = \
|
||||||
{fname: (tuple(roll_settings(yaml, args.plando) for yaml in yamls) if args.sameoptions else None)
|
{fname: (tuple(roll_settings(yaml, args.plando) for yaml in yamls) if args.sameoptions else None)
|
||||||
|
@ -216,28 +217,6 @@ def main(args=None) -> Tuple[argparse.Namespace, int]:
|
||||||
if len(set(name.lower() for name in erargs.name.values())) != len(erargs.name):
|
if len(set(name.lower() for name in erargs.name.values())) != len(erargs.name):
|
||||||
raise Exception(f"Names have to be unique. Names: {Counter(name.lower() for name in erargs.name.values())}")
|
raise Exception(f"Names have to be unique. Names: {Counter(name.lower() for name in erargs.name.values())}")
|
||||||
|
|
||||||
if args.yaml_output:
|
|
||||||
import yaml
|
|
||||||
important = {}
|
|
||||||
for option, player_settings in vars(erargs).items():
|
|
||||||
if type(player_settings) == dict:
|
|
||||||
if all(type(value) != list for value in player_settings.values()):
|
|
||||||
if len(player_settings.values()) > 1:
|
|
||||||
important[option] = {player: value for player, value in player_settings.items() if
|
|
||||||
player <= args.yaml_output}
|
|
||||||
else:
|
|
||||||
logging.debug(f"No player settings defined for option '{option}'")
|
|
||||||
|
|
||||||
else:
|
|
||||||
if player_settings != "": # is not empty name
|
|
||||||
important[option] = player_settings
|
|
||||||
else:
|
|
||||||
logging.debug(f"No player settings defined for option '{option}'")
|
|
||||||
if args.outputpath:
|
|
||||||
os.makedirs(args.outputpath, exist_ok=True)
|
|
||||||
with open(os.path.join(args.outputpath if args.outputpath else ".", f"generate_{seed_name}.yaml"), "wt") as f:
|
|
||||||
yaml.dump(important, f)
|
|
||||||
|
|
||||||
return erargs, seed
|
return erargs, seed
|
||||||
|
|
||||||
|
|
||||||
|
|
3
Main.py
3
Main.py
|
@ -46,6 +46,9 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||||
multiworld.sprite_pool = args.sprite_pool.copy()
|
multiworld.sprite_pool = args.sprite_pool.copy()
|
||||||
|
|
||||||
multiworld.set_options(args)
|
multiworld.set_options(args)
|
||||||
|
if args.yaml_output:
|
||||||
|
from Options import dump_player_options
|
||||||
|
dump_player_options(multiworld)
|
||||||
multiworld.set_item_links()
|
multiworld.set_item_links()
|
||||||
multiworld.state = CollectionState(multiworld)
|
multiworld.state = CollectionState(multiworld)
|
||||||
logger.info('Archipelago Version %s - Seed: %s\n', __version__, multiworld.seed)
|
logger.info('Archipelago Version %s - Seed: %s\n', __version__, multiworld.seed)
|
||||||
|
|
44
Options.py
44
Options.py
|
@ -8,16 +8,17 @@ import numbers
|
||||||
import random
|
import random
|
||||||
import typing
|
import typing
|
||||||
import enum
|
import enum
|
||||||
|
from collections import defaultdict
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from schema import And, Optional, Or, Schema
|
from schema import And, Optional, Or, Schema
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from Utils import get_fuzzy_results, is_iterable_except_str
|
from Utils import get_fuzzy_results, is_iterable_except_str, output_path
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from BaseClasses import PlandoOptions
|
from BaseClasses import MultiWorld, PlandoOptions
|
||||||
from worlds.AutoWorld import World
|
from worlds.AutoWorld import World
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
@ -1532,3 +1533,42 @@ def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], ge
|
||||||
|
|
||||||
with open(os.path.join(target_folder, game_name + ".yaml"), "w", encoding="utf-8-sig") as f:
|
with open(os.path.join(target_folder, game_name + ".yaml"), "w", encoding="utf-8-sig") as f:
|
||||||
f.write(res)
|
f.write(res)
|
||||||
|
|
||||||
|
|
||||||
|
def dump_player_options(multiworld: MultiWorld) -> None:
|
||||||
|
from csv import DictWriter
|
||||||
|
|
||||||
|
game_players = defaultdict(list)
|
||||||
|
for player, game in multiworld.game.items():
|
||||||
|
game_players[game].append(player)
|
||||||
|
game_players = dict(sorted(game_players.items()))
|
||||||
|
|
||||||
|
output = []
|
||||||
|
per_game_option_names = [
|
||||||
|
getattr(option, "display_name", option_key)
|
||||||
|
for option_key, option in PerGameCommonOptions.type_hints.items()
|
||||||
|
]
|
||||||
|
all_option_names = per_game_option_names.copy()
|
||||||
|
for game, players in game_players.items():
|
||||||
|
game_option_names = per_game_option_names.copy()
|
||||||
|
for player in players:
|
||||||
|
world = multiworld.worlds[player]
|
||||||
|
player_output = {
|
||||||
|
"Game": multiworld.game[player],
|
||||||
|
"Name": multiworld.get_player_name(player),
|
||||||
|
}
|
||||||
|
output.append(player_output)
|
||||||
|
for option_key, option in world.options_dataclass.type_hints.items():
|
||||||
|
if issubclass(Removed, option):
|
||||||
|
continue
|
||||||
|
display_name = getattr(option, "display_name", option_key)
|
||||||
|
player_output[display_name] = getattr(world.options, option_key).current_option_name
|
||||||
|
if display_name not in game_option_names:
|
||||||
|
all_option_names.append(display_name)
|
||||||
|
game_option_names.append(display_name)
|
||||||
|
|
||||||
|
with open(output_path(f"generate_{multiworld.seed_name}.csv"), mode="w", newline="") as file:
|
||||||
|
fields = ["Game", "Name", *all_option_names]
|
||||||
|
writer = DictWriter(file, fields)
|
||||||
|
writer.writeheader()
|
||||||
|
writer.writerows(output)
|
||||||
|
|
|
@ -134,6 +134,7 @@ def gen_game(gen_options: dict, meta: Optional[Dict[str, Any]] = None, owner=Non
|
||||||
{"bosses", "items", "connections", "texts"}))
|
{"bosses", "items", "connections", "texts"}))
|
||||||
erargs.skip_prog_balancing = False
|
erargs.skip_prog_balancing = False
|
||||||
erargs.skip_output = False
|
erargs.skip_output = False
|
||||||
|
erargs.yaml_output = False
|
||||||
|
|
||||||
name_counter = Counter()
|
name_counter = Counter()
|
||||||
for player, (playerfile, settings) in enumerate(gen_options.items(), 1):
|
for player, (playerfile, settings) in enumerate(gen_options.items(), 1):
|
||||||
|
|
Loading…
Reference in New Issue