mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-27 03:23:19 -07:00
Merge remote-tracking branch 'remotes/origin/main' into Satisfactory_ToBeVerified
# Conflicts: # README.md
This commit is contained in:
@@ -136,6 +136,9 @@
|
||||
# Overcooked! 2
|
||||
/worlds/overcooked2/ @toasterparty
|
||||
|
||||
# Paint
|
||||
/worlds/paint/ @MarioManTAW
|
||||
|
||||
# Pokemon Emerald
|
||||
/worlds/pokemon_emerald/ @Zunawe
|
||||
|
||||
|
||||
91
docs/deploy using containers.md
Normal file
91
docs/deploy using containers.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Deploy Using Containers
|
||||
|
||||
If you just want to play and there is a compiled version available on the [Archipelago releases page](https://github.com/ArchipelagoMW/Archipelago/releases), use that version.
|
||||
To build the full Archipelago software stack, refer to [Running From Source](running%20from%20source.md).
|
||||
Follow these steps to build and deploy a containerized instance of the web host software, optionally integrating [Gunicorn](https://gunicorn.org/) WSGI HTTP Server running behind the [nginx](https://nginx.org/) reverse proxy.
|
||||
|
||||
|
||||
## Building the Container Image
|
||||
|
||||
What you'll need:
|
||||
* A container runtime engine such as:
|
||||
* [Docker](https://www.docker.com/)
|
||||
* [Podman](https://podman.io/)
|
||||
* For running with rootless podman, you need to ensure all ports used are usable rootless, by default ports less than 1024 are root only. See [the official tutorial](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md) for details.
|
||||
|
||||
Starting from the root repository directory, the standalone Archipelago image can be built and run with the command:
|
||||
`docker build -t archipelago .`
|
||||
Or:
|
||||
`podman build -t archipelago .`
|
||||
|
||||
It is recommended to tag the image using `-t` to more easily identify the image and run it.
|
||||
|
||||
|
||||
## Running the Container
|
||||
|
||||
Running the container can be performed using:
|
||||
`docker run --network host archipelago`
|
||||
Or:
|
||||
`podman run --network host archipelago`
|
||||
|
||||
The Archipelago web host requires access to multiple ports in order to host game servers simultaneously. To simplify configuration for this purpose, specify `--network host`.
|
||||
|
||||
Given the default configuration, the website will be accessible at the hostname/IP address (localhost if run locally) of the machine being deployed to, at port 80. It can be configured by creating a YAML file and mapping a volume to the container when running initially:
|
||||
`docker run archipelago --network host -v /path/to/config.yaml:/app/config.yaml`
|
||||
See `docs/webhost configuration sample.yaml` for example.
|
||||
|
||||
|
||||
## Using Docker Compose
|
||||
|
||||
An example [docker compose](../deploy/docker-compose.yml) file can be found in [deploy](../deploy), along with example configuration files used by the services it orchestrates. Using these files as-is will spin up two separate archipelago containers with special modifications to their runtime arguments, in addition to deploying an `nginx` reverse proxy container.
|
||||
|
||||
To deploy in this manner, from the ["deploy"](../deploy) directory, run:
|
||||
`docker compose up -d`
|
||||
|
||||
### Services
|
||||
|
||||
The `docker-compose.yaml` file defines three services:
|
||||
* multiworld:
|
||||
* Executes the main `WebHost` process, using the [example config](../deploy/example_config.yaml), and overriding with a secondary [selflaunch example config](../deploy/example_selflaunch.yaml). This is because we do not want to launch the website through this service.
|
||||
* web:
|
||||
* Executes `gunicorn` using its [example config](../deploy/example_gunicorn.conf.py), which will bind it to the `WebHost` application, in effect launching it.
|
||||
* We mount the main [config](../deploy/example_config.yaml) without an override to specify that we are launching the website through this service.
|
||||
* No ports are exposed through to the host.
|
||||
* nginx:
|
||||
* Serves as a reverse proxy with `web` as its upstream.
|
||||
* Directs all HTTP traffic from port 80 to the upstream service.
|
||||
* Exposed to the host on port 8080. This is where we can reach the website.
|
||||
|
||||
### Configuration
|
||||
|
||||
As these are examples, they can be copied and modified. For instance setting the value of `HOST_ADDRESS` in [example config](../deploy/example_config.yaml) to host machines local IP address, will expose the service to its local area network.
|
||||
|
||||
The configuration files may be modified to handle for machine-specific optimizations, such as:
|
||||
* Web pages responding too slowly
|
||||
* Edit [the gunicorn config](../deploy/example_gunicorn.conf.py) to increase thread and/or worker count.
|
||||
* Game generation stalls
|
||||
* Increase the generator count in [selflaunch config](../deploy/example_selflaunch.yaml)
|
||||
* Gameplay lags
|
||||
* Increase the hoster count in [selflaunch config](../deploy/example_selflaunch.yaml)
|
||||
|
||||
Changes made to `docker-compose.yaml` can be applied by running `docker compose up -d`, while those made to other files are applied by running `docker compose restart`.
|
||||
|
||||
|
||||
## Windows
|
||||
|
||||
It is possible to carry out these deployment steps on Windows under [Windows Subsystem for Linux](https://learn.microsoft.com/en-us/windows/wsl/install).
|
||||
|
||||
|
||||
## Optional: A Link to the Past Enemizer
|
||||
|
||||
Only required to generate seeds that include A Link to the Past with certain options enabled. You will receive an
|
||||
error if it is required.
|
||||
Enemizer can be enabled on `x86_64` platform architecture, and is included in the image build process. Enemizer requires a version 1.0 Japanese "Zelda no Densetsu" `.sfc` rom file to be placed in the application directory:
|
||||
`docker run archipelago -v "/path/to/zelda.sfc:/app/Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"`.
|
||||
Enemizer is not currently available for `aarch64`.
|
||||
|
||||
|
||||
## Optional: Git
|
||||
|
||||
Building the image requires a local copy of the ArchipelagoMW source code.
|
||||
Refer to [Running From Source](running%20from%20source.md#optional-git).
|
||||
@@ -276,6 +276,7 @@ These packets are sent purely from client to server. They are not accepted by cl
|
||||
* [Sync](#Sync)
|
||||
* [LocationChecks](#LocationChecks)
|
||||
* [LocationScouts](#LocationScouts)
|
||||
* [CreateHints](#CreateHints)
|
||||
* [UpdateHint](#UpdateHint)
|
||||
* [StatusUpdate](#StatusUpdate)
|
||||
* [Say](#Say)
|
||||
@@ -294,7 +295,7 @@ Sent by the client to initiate a connection to an Archipelago game session.
|
||||
| password | str | If the game session requires a password, it should be passed here. |
|
||||
| game | str | The name of the game the client is playing. Example: `A Link to the Past` |
|
||||
| name | str | The player name for this client. |
|
||||
| uuid | str | Unique identifier for player client. |
|
||||
| uuid | str | Unique identifier for player. Cached in the user cache \Archipelago\Cache\common.json |
|
||||
| version | [NetworkVersion](#NetworkVersion) | An object representing the Archipelago version this client supports. |
|
||||
| items_handling | int | Flags configuring which items should be sent by the server. Read below for individual flags. |
|
||||
| tags | list\[str\] | Denotes special features or capabilities that the sender is capable of. [Tags](#Tags) |
|
||||
@@ -347,6 +348,21 @@ This is useful in cases where an item appears in the game world, such as 'ledge
|
||||
| locations | list\[int\] | The ids of the locations seen by the client. May contain any number of locations, even ones sent before; duplicates do not cause issues with the Archipelago server. |
|
||||
| create_as_hint | int | If non-zero, the scouted locations get created and broadcasted as a player-visible hint. <br/>If 2 only new hints are broadcast, however this does not remove them from the LocationInfo reply. |
|
||||
|
||||
### CreateHints
|
||||
|
||||
Sent to the server to create hints for a specified list of locations.
|
||||
Hints that already exist will be silently skipped and their status will not be updated.
|
||||
|
||||
When creating hints for another slot's locations, the packet will fail if any of those locations don't contain items for the requesting slot.
|
||||
When creating hints for your own slot's locations, non-existing locations will silently be skipped.
|
||||
|
||||
#### Arguments
|
||||
| Name | Type | Notes |
|
||||
| ---- | ---- | ----- |
|
||||
| locations | list\[int\] | The ids of the locations to create hints for. |
|
||||
| player | int | The ID of the player whose locations are being hinted for. Defaults to the requesting slot. |
|
||||
| status | [HintStatus](#HintStatus) | If included, sets the status of the hint to this status. Defaults to `HINT_UNSPECIFIED`. Cannot set `HINT_FOUND`. |
|
||||
|
||||
### UpdateHint
|
||||
Sent to the server to update the status of a Hint. The client must be the 'receiving_player' of the Hint, or the update fails.
|
||||
|
||||
|
||||
347
docs/webhost api.md
Normal file
347
docs/webhost api.md
Normal file
@@ -0,0 +1,347 @@
|
||||
# API Guide
|
||||
|
||||
Archipelago has a rudimentary API that can be queried by endpoints. The API is a work-in-progress and should be improved over time.
|
||||
|
||||
The following API requests are formatted as: `https://<Archipelago URL>/api/<endpoint>`
|
||||
|
||||
The returned data will be formated in a combination of JSON lists or dicts, with their keys or values being notated in `blocks` (if applicable)
|
||||
|
||||
Current endpoints:
|
||||
- Datapackage API
|
||||
- [`/datapackage`](#datapackage)
|
||||
- [`/datapackage/<string:checksum>`](#datapackagestringchecksum)
|
||||
- [`/datapackage_checksum`](#datapackagechecksum)
|
||||
- Generation API
|
||||
- [`/generate`](#generate)
|
||||
- [`/status/<suuid:seed>`](#status)
|
||||
- Room API
|
||||
- [`/room_status/<suuid:room_id>`](#roomstatus)
|
||||
- User API
|
||||
- [`/get_rooms`](#getrooms)
|
||||
- [`/get_seeds`](#getseeds)
|
||||
|
||||
|
||||
## Datapackage Endpoints
|
||||
These endpoints are used by applications to acquire a room's datapackage, and validate that they have the correct datapackage for use. Datapackages normally include, item IDs, location IDs, and name groupings, for a given room, and are essential for mapping IDs received from Archipelago to their correct items or locations.
|
||||
|
||||
### `/datapackage`
|
||||
<a name="datapackage"></a>
|
||||
Fetches the current datapackage from the WebHost.
|
||||
You'll receive a dict named `games` that contains a named dict of every game and its data currently supported by Archipelago.
|
||||
Each game will have:
|
||||
- A checksum `checksum`
|
||||
- A dict of item groups `item_name_groups`
|
||||
- Item name to AP ID dict `item_name_to_id`
|
||||
- A dict of location groups `location_name_groups`
|
||||
- Location name to AP ID dict `location_name_to_id`
|
||||
|
||||
Example:
|
||||
```
|
||||
{
|
||||
"games": {
|
||||
...
|
||||
"Clique": {
|
||||
"checksum": "0271f7a80b44ba72187f92815c2bc8669cb464c7",
|
||||
"item_name_groups": {
|
||||
"Everything": [
|
||||
"A Cool Filler Item (No Satisfaction Guaranteed)",
|
||||
"Button Activation",
|
||||
"Feeling of Satisfaction"
|
||||
]
|
||||
},
|
||||
"item_name_to_id": {
|
||||
"A Cool Filler Item (No Satisfaction Guaranteed)": 69696967,
|
||||
"Button Activation": 69696968,
|
||||
"Feeling of Satisfaction": 69696969
|
||||
},
|
||||
"location_name_groups": {
|
||||
"Everywhere": [
|
||||
"The Big Red Button",
|
||||
"The Item on the Desk"
|
||||
]
|
||||
},
|
||||
"location_name_to_id": {
|
||||
"The Big Red Button": 69696969,
|
||||
"The Item on the Desk": 69696968
|
||||
}
|
||||
},
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `/datapackage/<string:checksum>`
|
||||
<a name="datapackagestringchecksum"></a>
|
||||
Fetches a single datapackage by checksum.
|
||||
Returns a dict of the game's data with:
|
||||
- A checksum `checksum`
|
||||
- A dict of item groups `item_name_groups`
|
||||
- Item name to AP ID dict `item_name_to_id`
|
||||
- A dict of location groups `location_name_groups`
|
||||
- Location name to AP ID dict `location_name_to_id`
|
||||
|
||||
Its format will be identical to the whole-datapackage endpoint (`/datapackage`), except you'll only be returned the single game's data in a dict.
|
||||
|
||||
### `/datapackage_checksum`
|
||||
<a name="datapackagechecksum"></a>
|
||||
Fetches the checksums of the current static datapackages on the WebHost.
|
||||
You'll receive a dict with `game:checksum` key-value pairs for all the current officially supported games.
|
||||
Example:
|
||||
```
|
||||
{
|
||||
...
|
||||
"Donkey Kong Country 3":"f90acedcd958213f483a6a4c238e2a3faf92165e",
|
||||
"Factorio":"a699194a9589db3ebc0d821915864b422c782f44",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Generation Endpoint
|
||||
These endpoints are used internally for the WebHost to generate games and validate their generation. They are also used by external applications to generate games automatically.
|
||||
|
||||
### `/generate`
|
||||
<a name="generate"></a>
|
||||
Submits a game to the WebHost for generation.
|
||||
**This endpoint only accepts a POST HTTP request.**
|
||||
|
||||
There are two ways to submit data for generation: With a file and with JSON.
|
||||
|
||||
#### With a file:
|
||||
Have your ZIP of yaml(s) or a single yaml, and submit a POST request to the `/generate` endpoint.
|
||||
If the options are valid, you'll be returned a successful generation response. (see [Generation Response](#generation-response))
|
||||
|
||||
Example using the python requests library:
|
||||
```
|
||||
file = {'file': open('Games.zip', 'rb')}
|
||||
req = requests.post("https://archipelago.gg/api/generate", files=file)
|
||||
```
|
||||
|
||||
#### With JSON:
|
||||
Compile your weights/yaml data into a dict. Then insert that into a dict with the key `"weights"`.
|
||||
Finally, submit a POST request to the `/generate` endpoint.
|
||||
If the weighted options are valid, you'll be returned a successful generation response (see [Generation Response](#generation-response))
|
||||
|
||||
Example using the python requests library:
|
||||
```
|
||||
data = {"Test":{"game": "Factorio","name": "Test","Factorio": {}},}
|
||||
weights={"weights": data}
|
||||
req = requests.post("https://archipelago.gg/api/generate", json=weights)
|
||||
```
|
||||
|
||||
#### Generation Response:
|
||||
##### Successful Generation:
|
||||
Upon successful generation, you'll be sent a JSON dict response detailing the generation:
|
||||
- The UUID of the generation `detail`
|
||||
- The SUUID of the generation `encoded`
|
||||
- The response text `text`
|
||||
- The page that will resolve to the seed/room generation page once generation has completed `url`
|
||||
- The API status page of the generation `wait_api_url` (see [Status Endpoint](#status))
|
||||
|
||||
Example:
|
||||
```
|
||||
{
|
||||
"detail": "19878f16-5a58-4b76-aab7-d6bf38be9463",
|
||||
"encoded": "GYePFlpYS3aqt9a_OL6UYw",
|
||||
"text": "Generation of seed 19878f16-5a58-4b76-aab7-d6bf38be9463 started successfully.",
|
||||
"url": "http://archipelago.gg/wait/GYePFlpYS3aqt9a_OL6UYw",
|
||||
"wait_api_url": "http://archipelago.gg/api/status/GYePFlpYS3aqt9a_OL6UYw"
|
||||
}
|
||||
```
|
||||
|
||||
##### Failed Generation:
|
||||
|
||||
Upon failed generation, you'll be returned a single key-value pair. The key will always be `text`
|
||||
The value will give you a hint as to what may have gone wrong.
|
||||
- Options without tags, and a 400 status code
|
||||
- Options in a string, and a 400 status code
|
||||
- Invalid file/weight string, `No options found. Expected file attachment or json weights.` with a 400 status code
|
||||
- Too many slots for the server to process, `Max size of multiworld exceeded` with a 409 status code
|
||||
|
||||
If the generation detects a issue in generation, you'll be sent a dict with two key-value pairs (`text` and `detail`) and a 400 status code. The values will be:
|
||||
- Summary of issue in `text`
|
||||
- Detailed issue in `detail`
|
||||
|
||||
In the event of an unhandled server exception, you'll be provided a dict with a single key `text`:
|
||||
- Exception, `Uncought Exception: <error>` with a 500 status code
|
||||
|
||||
### `/status/<suuid:seed>`
|
||||
<a name="status"></a>
|
||||
Retrieves the status of the seed's generation.
|
||||
This endpoint will return a dict with a single key-vlaue pair. The key will always be `text`
|
||||
The value will tell you the status of the generation:
|
||||
- Generation was completed: `Generation done` with a 201 status code
|
||||
- Generation request was not found: `Generation not found` with a 404 status code
|
||||
- Generation of the seed failed: `Generation failed` with a 500 status code
|
||||
- Generation is in progress still: `Generation running` with a 202 status code
|
||||
|
||||
## Room Endpoints
|
||||
Endpoints to fetch information of the active WebHost room with the supplied room_ID.
|
||||
|
||||
### `/room_status/<suuid:room_id>`
|
||||
<a name="roomstatus"></a>
|
||||
Will provide a dict of room data with the following keys:
|
||||
- Tracker SUUID (`tracker`)
|
||||
- A list of players (`players`)
|
||||
- Each item containing a list with the Slot name and Game
|
||||
- Last known hosted port (`last_port`)
|
||||
- Last activity timestamp (`last_activity`)
|
||||
- The room timeout counter (`timeout`)
|
||||
- A list of downloads for files required for gameplay (`downloads`)
|
||||
- Each item is a dict containings the download URL and slot (`slot`, `download`)
|
||||
|
||||
Example:
|
||||
```
|
||||
{
|
||||
"downloads": [
|
||||
{
|
||||
"download": "/slot_file/kK5fmxd8TfisU5Yp_eg/1",
|
||||
"slot": 1
|
||||
},
|
||||
{
|
||||
"download": "/slot_file/kK5fmxd8TfisU5Yp_eg/2",
|
||||
"slot": 2
|
||||
},
|
||||
{
|
||||
"download": "/slot_file/kK5fmxd8TfisU5Yp_eg/3",
|
||||
"slot": 3
|
||||
},
|
||||
{
|
||||
"download": "/slot_file/kK5fmxd8TfisU5Yp_eg/4",
|
||||
"slot": 4
|
||||
},
|
||||
{
|
||||
"download": "/slot_file/kK5fmxd8TfisU5Yp_eg/5",
|
||||
"slot": 5
|
||||
}
|
||||
],
|
||||
"last_activity": "Fri, 18 Apr 2025 20:35:45 GMT",
|
||||
"last_port": 52122,
|
||||
"players": [
|
||||
[
|
||||
"Slot_Name_1",
|
||||
"Ocarina of Time"
|
||||
],
|
||||
[
|
||||
"Slot_Name_2",
|
||||
"Ocarina of Time"
|
||||
],
|
||||
[
|
||||
"Slot_Name_3",
|
||||
"Ocarina of Time"
|
||||
],
|
||||
[
|
||||
"Slot_Name_4",
|
||||
"Ocarina of Time"
|
||||
],
|
||||
[
|
||||
"Slot_Name_5",
|
||||
"Ocarina of Time"
|
||||
]
|
||||
],
|
||||
"timeout": 7200,
|
||||
"tracker": "cf6989c0-4703-45d7-a317-2e5158431171"
|
||||
}
|
||||
```
|
||||
|
||||
## User Endpoints
|
||||
User endpoints can get room and seed details from the current session tokens (cookies)
|
||||
|
||||
### `/get_rooms`
|
||||
<a name="getrooms"></a>
|
||||
Retreives a list of all rooms currently owned by the session token.
|
||||
Each list item will contain a dict with the room's details:
|
||||
- Room SUUID (`room_id`)
|
||||
- Seed SUUID (`seed_id`)
|
||||
- Creation timestamp (`creation_time`)
|
||||
- Last activity timestamp (`last_activity`)
|
||||
- Last known AP port (`last_port`)
|
||||
- Room timeout counter in seconds (`timeout`)
|
||||
- Room tracker SUUID (`tracker`)
|
||||
|
||||
Example:
|
||||
```
|
||||
[
|
||||
{
|
||||
"creation_time": "Fri, 18 Apr 2025 19:46:53 GMT",
|
||||
"last_activity": "Fri, 18 Apr 2025 21:16:02 GMT",
|
||||
"last_port": 52122,
|
||||
"room_id": "90ae5f9b-177c-4df8-ac53-9629fc3bff7a",
|
||||
"seed_id": "efbd62c2-aaeb-4dda-88c3-f461c029cef6",
|
||||
"timeout": 7200,
|
||||
"tracker": "cf6989c0-4703-45d7-a317-2e5158431171"
|
||||
},
|
||||
{
|
||||
"creation_time": "Fri, 18 Apr 2025 20:36:42 GMT",
|
||||
"last_activity": "Fri, 18 Apr 2025 20:36:46 GMT",
|
||||
"last_port": 56884,
|
||||
"room_id": "14465c05-d08e-4d28-96bd-916f994609d8",
|
||||
"seed_id": "a528e34c-3b4f-42a9-9f8f-00a4fd40bacb",
|
||||
"timeout": 7200,
|
||||
"tracker": "4e624bd8-32b6-42e4-9178-aa407f72751c"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### `/get_seeds`
|
||||
<a name="getseeds"></a>
|
||||
Retreives a list of all seeds currently owned by the session token.
|
||||
Each item in the list will contain a dict with the seed's details:
|
||||
- Seed SUUID (`seed_id`)
|
||||
- Creation timestamp (`creation_time`)
|
||||
- A list of player slots (`players`)
|
||||
- Each item in the list will contain a list of the slot name and game
|
||||
|
||||
Example:
|
||||
```
|
||||
[
|
||||
{
|
||||
"creation_time": "Fri, 18 Apr 2025 19:46:52 GMT",
|
||||
"players": [
|
||||
[
|
||||
"Slot_Name_1",
|
||||
"Ocarina of Time"
|
||||
],
|
||||
[
|
||||
"Slot_Name_2",
|
||||
"Ocarina of Time"
|
||||
],
|
||||
[
|
||||
"Slot_Name_3",
|
||||
"Ocarina of Time"
|
||||
],
|
||||
[
|
||||
"Slot_Name_4",
|
||||
"Ocarina of Time"
|
||||
],
|
||||
[
|
||||
"Slot_Name_5",
|
||||
"Ocarina of Time"
|
||||
]
|
||||
],
|
||||
"seed_id": "efbd62c2-aaeb-4dda-88c3-f461c029cef6"
|
||||
},
|
||||
{
|
||||
"creation_time": "Fri, 18 Apr 2025 20:36:39 GMT",
|
||||
"players": [
|
||||
[
|
||||
"Slot_Name_1",
|
||||
"Clique"
|
||||
],
|
||||
[
|
||||
"Slot_Name_2",
|
||||
"Clique"
|
||||
],
|
||||
[
|
||||
"Slot_Name_3",
|
||||
"Clique"
|
||||
],
|
||||
[
|
||||
"Slot_Name_4",
|
||||
"Archipelago"
|
||||
]
|
||||
],
|
||||
"seed_id": "a528e34c-3b4f-42a9-9f8f-00a4fd40bacb"
|
||||
}
|
||||
]
|
||||
```
|
||||
Reference in New Issue
Block a user