| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | import json | 
					
						
							| 
									
										
										
										
											2022-01-11 22:01:54 +01:00
										 |  |  | import logging | 
					
						
							| 
									
										
										
										
											2021-07-22 18:21:31 +02:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2022-06-12 23:33:14 +02:00
										 |  |  | import typing | 
					
						
							| 
									
										
										
										
											2021-07-22 18:21:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-17 00:17:54 +02:00
										 |  |  | import Options | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  | from Utils import local_path | 
					
						
							| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | from worlds.AutoWorld import AutoWorldRegister | 
					
						
							| 
									
										
										
										
											2021-07-22 18:21:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-11 22:01:54 +01:00
										 |  |  | handled_in_js = {"start_inventory", "local_items", "non_local_items", "start_hints", "start_location_hints", | 
					
						
							| 
									
										
										
										
											2023-03-29 17:37:39 -04:00
										 |  |  |                  "exclude_locations", "priority_locations"} | 
					
						
							| 
									
										
										
										
											2022-01-11 22:01:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 21:27:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-22 18:21:31 +02:00
										 |  |  | def create(): | 
					
						
							| 
									
										
										
										
											2022-08-23 01:07:17 +02:00
										 |  |  |     target_folder = local_path("WebHostLib", "static", "generated") | 
					
						
							| 
									
										
										
										
											2022-10-02 20:51:14 +02:00
										 |  |  |     yaml_folder = os.path.join(target_folder, "configs") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-16 01:57:52 +02:00
										 |  |  |     Options.generate_yaml_templates(yaml_folder) | 
					
						
							| 
									
										
										
										
											2021-09-17 00:17:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 00:58:46 +02:00
										 |  |  |     def get_html_doc(option_type: type(Options.Option)) -> str: | 
					
						
							|  |  |  |         if not option_type.__doc__: | 
					
						
							|  |  |  |             return "Please document me!" | 
					
						
							|  |  |  |         return "\n".join(line.strip() for line in option_type.__doc__.split("\n")).strip() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  |     weighted_options = { | 
					
						
							| 
									
										
										
										
											2021-12-31 13:22:23 -05:00
										 |  |  |         "baseOptions": { | 
					
						
							|  |  |  |             "description": "Generated by https://archipelago.gg/", | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  |             "name": "", | 
					
						
							| 
									
										
										
										
											2021-12-31 13:22:23 -05:00
										 |  |  |             "game": {}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "games": {}, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-22 18:21:31 +02:00
										 |  |  |     for game_name, world in AutoWorldRegister.world_types.items(): | 
					
						
							| 
									
										
										
										
											2021-12-31 13:22:23 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |         all_options: typing.Dict[str, Options.AssembleOptions] = world.options_dataclass.type_hints | 
					
						
							| 
									
										
										
										
											2021-07-24 21:27:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  |         # Generate JSON files for player-options pages | 
					
						
							|  |  |  |         player_options = { | 
					
						
							| 
									
										
										
										
											2021-07-24 23:09:34 -04:00
										 |  |  |             "baseOptions": { | 
					
						
							| 
									
										
										
										
											2023-05-19 21:48:45 +02:00
										 |  |  |                 "description": f"Generated by https://archipelago.gg/ for {game_name}", | 
					
						
							| 
									
										
										
										
											2021-09-16 17:15:25 -04:00
										 |  |  |                 "game": game_name, | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  |                 "name": "", | 
					
						
							| 
									
										
										
										
											2021-07-24 21:27:56 -04:00
										 |  |  |             }, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         game_options = {} | 
					
						
							| 
									
										
										
										
											2021-12-28 18:43:52 +01:00
										 |  |  |         for option_name, option in all_options.items(): | 
					
						
							| 
									
										
										
										
											2022-01-11 17:36:33 -05:00
										 |  |  |             if option_name in handled_in_js: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-30 15:56:26 -05:00
										 |  |  |             elif issubclass(option, Options.Choice) or issubclass(option, Options.Toggle): | 
					
						
							| 
									
										
										
										
											2021-11-20 17:37:08 +01:00
										 |  |  |                 game_options[option_name] = this_option = { | 
					
						
							| 
									
										
										
										
											2021-07-24 21:27:56 -04:00
										 |  |  |                     "type": "select", | 
					
						
							| 
									
										
										
										
											2022-02-02 16:29:29 +01:00
										 |  |  |                     "displayName": option.display_name if hasattr(option, "display_name") else option_name, | 
					
						
							| 
									
										
										
										
											2022-08-21 00:58:46 +02:00
										 |  |  |                     "description": get_html_doc(option), | 
					
						
							| 
									
										
										
										
											2021-07-24 21:27:56 -04:00
										 |  |  |                     "defaultValue": None, | 
					
						
							|  |  |  |                     "options": [] | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 00:21:57 -04:00
										 |  |  |                 for sub_option_id, sub_option_name in option.name_lookup.items(): | 
					
						
							| 
									
										
										
										
											2023-03-30 15:56:26 -05:00
										 |  |  |                     if sub_option_name != "random": | 
					
						
							|  |  |  |                         this_option["options"].append({ | 
					
						
							|  |  |  |                             "name": option.get_option_name(sub_option_id), | 
					
						
							|  |  |  |                             "value": sub_option_name, | 
					
						
							|  |  |  |                         }) | 
					
						
							| 
									
										
										
										
											2021-07-24 21:27:56 -04:00
										 |  |  |                     if sub_option_id == option.default: | 
					
						
							|  |  |  |                         this_option["defaultValue"] = sub_option_name | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-30 19:01:31 -04:00
										 |  |  |                 if not this_option["defaultValue"]: | 
					
						
							|  |  |  |                     this_option["defaultValue"] = "random" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-06 11:25:37 +00:00
										 |  |  |             elif issubclass(option, Options.Range): | 
					
						
							| 
									
										
										
										
											2021-07-25 18:18:15 -04:00
										 |  |  |                 game_options[option_name] = { | 
					
						
							|  |  |  |                     "type": "range", | 
					
						
							| 
									
										
										
										
											2022-02-02 16:29:29 +01:00
										 |  |  |                     "displayName": option.display_name if hasattr(option, "display_name") else option_name, | 
					
						
							| 
									
										
										
										
											2022-08-21 00:58:46 +02:00
										 |  |  |                     "description": get_html_doc(option), | 
					
						
							| 
									
										
										
										
											2022-06-12 05:48:52 +00:00
										 |  |  |                     "defaultValue": option.default if hasattr( | 
					
						
							|  |  |  |                         option, "default") and option.default != "random" else option.range_start, | 
					
						
							| 
									
										
										
										
											2021-07-25 18:18:15 -04:00
										 |  |  |                     "min": option.range_start, | 
					
						
							|  |  |  |                     "max": option.range_end, | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-01-11 17:36:33 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-25 00:10:52 +01:00
										 |  |  |                 if issubclass(option, Options.NamedRange): | 
					
						
							|  |  |  |                     game_options[option_name]["type"] = 'named_range' | 
					
						
							| 
									
										
										
										
											2022-06-12 23:33:14 +02:00
										 |  |  |                     game_options[option_name]["value_names"] = {} | 
					
						
							|  |  |  |                     for key, val in option.special_range_names.items(): | 
					
						
							|  |  |  |                         game_options[option_name]["value_names"][key] = val | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-29 17:37:39 -04:00
										 |  |  |             elif issubclass(option, Options.ItemSet): | 
					
						
							| 
									
										
										
										
											2022-01-11 17:36:33 -05:00
										 |  |  |                 game_options[option_name] = { | 
					
						
							|  |  |  |                     "type": "items-list", | 
					
						
							| 
									
										
										
										
											2022-02-02 16:29:29 +01:00
										 |  |  |                     "displayName": option.display_name if hasattr(option, "display_name") else option_name, | 
					
						
							| 
									
										
										
										
											2022-08-21 00:58:46 +02:00
										 |  |  |                     "description": get_html_doc(option), | 
					
						
							| 
									
										
										
										
											2023-04-10 17:01:54 -05:00
										 |  |  |                     "defaultValue": list(option.default) | 
					
						
							| 
									
										
										
										
											2022-01-11 17:36:33 -05:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-29 17:37:39 -04:00
										 |  |  |             elif issubclass(option, Options.LocationSet): | 
					
						
							| 
									
										
										
										
											2022-01-11 17:36:33 -05:00
										 |  |  |                 game_options[option_name] = { | 
					
						
							|  |  |  |                     "type": "locations-list", | 
					
						
							| 
									
										
										
										
											2022-02-02 16:29:29 +01:00
										 |  |  |                     "displayName": option.display_name if hasattr(option, "display_name") else option_name, | 
					
						
							| 
									
										
										
										
											2022-08-21 00:58:46 +02:00
										 |  |  |                     "description": get_html_doc(option), | 
					
						
							| 
									
										
										
										
											2023-04-10 17:01:54 -05:00
										 |  |  |                     "defaultValue": list(option.default) | 
					
						
							| 
									
										
										
										
											2022-01-11 17:36:33 -05:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-06 19:07:57 -05:00
										 |  |  |             elif issubclass(option, Options.VerifyKeys) and not issubclass(option, Options.OptionDict): | 
					
						
							| 
									
										
										
										
											2022-01-11 17:36:33 -05:00
										 |  |  |                 if option.valid_keys: | 
					
						
							|  |  |  |                     game_options[option_name] = { | 
					
						
							|  |  |  |                         "type": "custom-list", | 
					
						
							| 
									
										
										
										
											2022-02-02 16:29:29 +01:00
										 |  |  |                         "displayName": option.display_name if hasattr(option, "display_name") else option_name, | 
					
						
							| 
									
										
										
										
											2022-08-21 00:58:46 +02:00
										 |  |  |                         "description": get_html_doc(option), | 
					
						
							| 
									
										
										
										
											2022-01-11 17:36:33 -05:00
										 |  |  |                         "options": list(option.valid_keys), | 
					
						
							| 
									
										
										
										
											2023-04-10 17:01:54 -05:00
										 |  |  |                         "defaultValue": list(option.default) if hasattr(option, "default") else [] | 
					
						
							| 
									
										
										
										
											2022-01-11 17:36:33 -05:00
										 |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-11 22:01:54 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  |                 logging.debug(f"{option} not exported to Web Options.") | 
					
						
							| 
									
										
										
										
											2021-07-25 18:18:15 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  |         player_options["gameOptions"] = game_options | 
					
						
							| 
									
										
										
										
											2021-07-24 21:27:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  |         player_options["presetOptions"] = {} | 
					
						
							|  |  |  |         for preset_name, preset in world.web.options_presets.items(): | 
					
						
							|  |  |  |             player_options["presetOptions"][preset_name] = {} | 
					
						
							|  |  |  |             for option_name, option_value in preset.items(): | 
					
						
							|  |  |  |                 # Random range type settings are not valid. | 
					
						
							|  |  |  |                 assert (not str(option_value).startswith("random-")), \ | 
					
						
							|  |  |  |                     f"Invalid preset value '{option_value}' for '{option_name}' in '{preset_name}'. Special random " \ | 
					
						
							|  |  |  |                     f"values are not supported for presets." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 # Normal random is supported, but needs to be handled explicitly. | 
					
						
							|  |  |  |                 if option_value == "random": | 
					
						
							|  |  |  |                     player_options["presetOptions"][preset_name][option_name] = option_value | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 option = world.options_dataclass.type_hints[option_name].from_any(option_value) | 
					
						
							| 
									
										
										
										
											2023-11-25 00:10:52 +01:00
										 |  |  |                 if isinstance(option, Options.NamedRange) and isinstance(option_value, str): | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  |                     assert option_value in option.special_range_names, \ | 
					
						
							|  |  |  |                         f"Invalid preset value '{option_value}' for '{option_name}' in '{preset_name}'. " \ | 
					
						
							|  |  |  |                         f"Expected {option.special_range_names.keys()} or {option.range_start}-{option.range_end}." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     # Still use the true value for the option, not the name. | 
					
						
							|  |  |  |                     player_options["presetOptions"][preset_name][option_name] = option.value | 
					
						
							|  |  |  |                 elif isinstance(option, Options.Range): | 
					
						
							|  |  |  |                     player_options["presetOptions"][preset_name][option_name] = option.value | 
					
						
							|  |  |  |                 elif isinstance(option_value, str): | 
					
						
							|  |  |  |                     # For Choice and Toggle options, the value should be the name of the option. This is to prevent | 
					
						
							|  |  |  |                     # setting a preset for an option with an overridden from_text method that would normally be okay, | 
					
						
							|  |  |  |                     # but would not be okay for the webhost's current implementation of player options UI. | 
					
						
							|  |  |  |                     assert option.name_lookup[option.value] == option_value, \ | 
					
						
							|  |  |  |                         f"Invalid option value '{option_value}' for '{option_name}' in preset '{preset_name}'. " \ | 
					
						
							|  |  |  |                         f"Values must not be resolved to a different option via option.from_text (or an alias)." | 
					
						
							|  |  |  |                     player_options["presetOptions"][preset_name][option_name] = option.current_key | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     # int and bool values are fine, just resolve them to the current key for webhost. | 
					
						
							|  |  |  |                     player_options["presetOptions"][preset_name][option_name] = option.current_key | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  |         os.makedirs(os.path.join(target_folder, 'player-options'), exist_ok=True) | 
					
						
							| 
									
										
										
										
											2021-10-11 21:37:08 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  |         with open(os.path.join(target_folder, 'player-options', game_name + ".json"), "w") as f: | 
					
						
							|  |  |  |             json.dump(player_options, f, indent=2, separators=(',', ': ')) | 
					
						
							| 
									
										
										
										
											2021-12-31 13:22:23 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  |         if not world.hidden and world.web.options_page is True: | 
					
						
							|  |  |  |             # Add the random option to Choice, TextChoice, and Toggle options | 
					
						
							| 
									
										
										
										
											2023-03-30 19:01:31 -04:00
										 |  |  |             for option in game_options.values(): | 
					
						
							|  |  |  |                 if option["type"] == "select": | 
					
						
							|  |  |  |                     option["options"].append({"name": "Random", "value": "random"}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if not option["defaultValue"]: | 
					
						
							|  |  |  |                         option["defaultValue"] = "random" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  |             weighted_options["baseOptions"]["game"][game_name] = 0 | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  |             weighted_options["games"][game_name] = { | 
					
						
							|  |  |  |                 "gameSettings": game_options, | 
					
						
							|  |  |  |                 "gameItems": tuple(world.item_names), | 
					
						
							|  |  |  |                 "gameItemGroups": [ | 
					
						
							|  |  |  |                     group for group in world.item_name_groups.keys() if group != "Everything" | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |                 "gameItemDescriptions": world.item_descriptions, | 
					
						
							|  |  |  |                 "gameLocations": tuple(world.location_names), | 
					
						
							|  |  |  |                 "gameLocationGroups": [ | 
					
						
							|  |  |  |                     group for group in world.location_name_groups.keys() if group != "Everywhere" | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |                 "gameLocationDescriptions": world.location_descriptions, | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-12-31 13:22:23 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  |     with open(os.path.join(target_folder, 'weighted-options.json'), "w") as f: | 
					
						
							|  |  |  |         json.dump(weighted_options, f, indent=2, separators=(',', ': ')) | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | 
 |