mirror of
				https://github.com/MarioSpore/Grinch-AP.git
				synced 2025-10-21 20:21:32 -06:00 
			
		
		
		
	 827444f5a4
			
		
	
	827444f5a4
	
	
	
		
			
			* 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 <109771707+el-u@users.noreply.github.com>
* 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 <zach@alliware.com>
* Settings: correctly resolve .exe in UserPath and LocalPath
---------
Co-authored-by: el-u <109771707+el-u@users.noreply.github.com>
Co-authored-by: Zach Parks <zach@alliware.com>
		
	
		
			
				
	
	
		
			188 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Archipelago Settings API
 | |
| 
 | |
| The settings API describes how to use installation-wide config and let the user configure them, like paths, etc. using
 | |
| host.yaml. For the player settings / player yamls see [options api.md](options api.md).
 | |
| 
 | |
| The settings API replaces `Utils.get_options()` and `Utils.get_default_options()`
 | |
| as well as the predefined `host.yaml` in the repository.
 | |
| 
 | |
| For backwards compatibility with APWorlds, some interfaces are kept for now and will produce a warning when being used.
 | |
| 
 | |
| 
 | |
| ## Config File
 | |
| 
 | |
| Settings use options.yaml (manual override), if that exists, or host.yaml (the default) otherwise.
 | |
| The files are searched for in the current working directory, if different from install directory, and in `user_path`,
 | |
| which either points to the installation directory, if writable, or to %home%/Archipelago otherwise.
 | |
| 
 | |
| **Examples:**
 | |
| * C:\Program Data\Archipelago\options.yaml
 | |
| * C:\Program Data\Archipelago\host.yaml
 | |
| * path\to\code\repository\host.yaml
 | |
| * ~/Archipelago/host.yaml
 | |
| 
 | |
| Using the settings API, AP can update the config file or create a new one with default values and comments, 
 | |
| if it does not exist.
 | |
| 
 | |
| 
 | |
| ## Global Settings
 | |
| 
 | |
| All non-world-specific settings are defined directly in settings.py.
 | |
| Each value needs to have a default. If the default should be `None`, define it as `typing.Optional` and assign `None`.
 | |
| 
 | |
| To access a "global" config value, with correct typing, use one of
 | |
| ```python
 | |
| from settings import get_settings, GeneralOptions, FolderPath
 | |
| from typing import cast
 | |
| 
 | |
| x = get_settings().general_options.output_path
 | |
| y = cast(GeneralOptions, get_settings()["general_options"]).output_path
 | |
| z = cast(FolderPath, get_settings()["general_options"]["output_path"])
 | |
| ```
 | |
| 
 | |
| 
 | |
| ## World Settings
 | |
| 
 | |
| Worlds can define the top level key to use by defining `settings_key: ClassVar[str]` in their World class.
 | |
| It defaults to `{folder_name}_options` if undefined, i.e. `worlds/factorio/...` defaults to `factorio_options`.
 | |
| 
 | |
| Worlds define the layout of their config section using type annotation of the variable `settings` in the class.
 | |
| The type has to inherit from `settings.Group`. Each value in the config can have a comment by subclassing a built-in
 | |
| type. Some helper types are defined in `settings.py`, see [Types](#Types) for a list.```
 | |
| 
 | |
| Inside the class code, you can then simply use `self.settings.rom_file` to get the value.
 | |
| In case of paths they will automatically be read as absolute file paths. No need to use user_path or local_path.
 | |
| 
 | |
| ```python
 | |
| import settings
 | |
| from worlds.AutoWorld import World
 | |
| 
 | |
| 
 | |
| class MyGameSettings(settings.Group):
 | |
|     class RomFile(settings.SNESRomPath):
 | |
|         """Description that is put into host.yaml"""
 | |
|         description = "My Game US v1.0 ROM File"  # displayed in the file browser
 | |
|         copy_to = "MyGame.sfc"  # instead of storing the path, copy to AP dir
 | |
|         md5s = ["..."]
 | |
| 
 | |
|     rom_file: RomFile = RomFile("MyGame.sfc")  # definition and default value
 | |
| 
 | |
| 
 | |
| class MyGameWorld(World):
 | |
|     ...
 | |
|     settings: MyGameSettings
 | |
|     ...
 | |
| 
 | |
|     def something(self):
 | |
|         pass  # use self.settings.rom_file here
 | |
| ```
 | |
| 
 | |
| 
 | |
| ## Types
 | |
| 
 | |
| When writing the host.yaml, the code will down cast the values to builtins.
 | |
| When reading the host.yaml, the code will upcast the values to what is defined in the type annotations.
 | |
| E.g. an IntEnum becomes int when saving and will construct the IntEnum when loading.
 | |
| 
 | |
| Types that can not be down cast to / up cast from a builtin can not be used except for Group, which will be converted
 | |
| to/from a dict.
 | |
| `bool` is a special case, see settings.py: ServerOptions.disable_item_cheat for an example.
 | |
| 
 | |
| Below are some predefined types that can be used if they match your requirements:
 | |
| 
 | |
| 
 | |
| ### Group
 | |
| 
 | |
| A section / dict in the config file. Behaves similar to a dataclass.
 | |
| Type annotation and default assignment define how loading, saving and default values behave.
 | |
| It can be accessed using attributes or as a dict: `group["a"]` is equivalent to `group.a`.
 | |
| 
 | |
| In worlds, this should only be used for the top level to avoid issues when upgrading/migrating.
 | |
| 
 | |
| 
 | |
| ### Bool
 | |
| 
 | |
| Since `bool` can not be subclassed, use the `settings.Bool` helper in a `typing.Union` to get a comment in host.yaml.
 | |
| 
 | |
| ```python
 | |
| import settings
 | |
| import typing
 | |
| 
 | |
| class MySettings(settings.Group):
 | |
|     class MyBool(settings.Bool):
 | |
|         """Doc string"""
 | |
| 
 | |
|     my_value: typing.Union[MyBool, bool] = True
 | |
| ```
 | |
| 
 | |
| ### UserFilePath
 | |
| 
 | |
| Path to a single file. Automatically resolves as user_path:
 | |
| Source folder or AP install path on Windows. ~/Archipelago for the AppImage.
 | |
| Will open a file browser if the file is missing when in GUI mode.
 | |
| 
 | |
| #### class method validate(cls, path: str)
 | |
| 
 | |
| Override this and raise ValueError if validation fails.
 | |
| Checks the file against [md5s](#md5s) by default.
 | |
| 
 | |
| #### is_exe: bool
 | |
| 
 | |
| Resolves to an executable (varying file extension based on platform)
 | |
| 
 | |
| #### description: Optional\[str\]
 | |
| 
 | |
| Human-readable name to use in file browser
 | |
| 
 | |
| #### copy_to: Optional\[str\]
 | |
| 
 | |
| Instead of storing the path, copy the file.
 | |
| 
 | |
| #### md5s: List[Union[str, bytes]]
 | |
| 
 | |
| Provide md5 hashes as hex digests or raw bytes for automatic validation.
 | |
| 
 | |
| 
 | |
| ### UserFolderPath
 | |
| 
 | |
| Same as [UserFilePath](#UserFilePath), but for a folder instead of a file.
 | |
| 
 | |
| 
 | |
| ### LocalFilePath
 | |
| 
 | |
| Same as [UserFilePath](#UserFilePath), but resolves as local_path:
 | |
| path inside the AP dir or Appimage even if read-only.
 | |
| 
 | |
| 
 | |
| ### LocalFolderPath
 | |
| 
 | |
| Same as [LocalFilePath](#LocalFilePath), but for a folder instead of a file.
 | |
| 
 | |
| 
 | |
| ### OptionalUserFilePath, OptionalUserFolderPath, OptionalLocalFilePath, OptionalLocalFolderPath
 | |
| 
 | |
| Same as UserFilePath, UserFolderPath, LocalFilePath, LocalFolderPath but does not open a file browser if missing.
 | |
| 
 | |
| 
 | |
| ### SNESRomPath
 | |
| 
 | |
| Specialized [UserFilePath](#UserFilePath) that ignores an optional 512 byte header when validating.
 | |
| 
 | |
| 
 | |
| ## Caveats
 | |
| 
 | |
| ### Circular Imports
 | |
| 
 | |
| Because the settings are defined on import, code that runs on import can not use settings since that would result in
 | |
| circular / partial imports. Instead, the code should fetch from settings on demand during generation.
 | |
| 
 | |
| "Global" settings are populated immediately, while worlds settings are lazy loaded, so if really necessary,
 | |
| "global" settings could be used in global scope of worlds.
 | |
| 
 | |
| 
 | |
| ### APWorld Backwards Compatibility
 | |
| 
 | |
| APWorlds that want to be compatible with both stable and dev versions, have two options:
 | |
| 1. use the old Utils.get_options() API until Archipelago 0.4.2 is out
 | |
| 2. add some sort of compatibility code to your world that mimics the new API
 |