Compare commits

...

9 Commits

Author SHA1 Message Date
Fabian Dill
404a0e461e Update worlds/generic/docs/other_en.md
Co-authored-by: Emily <35015090+EmilyV99@users.noreply.github.com>
2026-03-16 07:41:33 +01:00
Berserker
a446c8bbbc Move APSudoku to https://archipelago.miraheze.org/wiki/Category:Hint_games 2026-03-15 18:57:55 +01:00
Fabian Dill
75fd44bb02 Update worlds/generic/docs/other_en.md
Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
2026-03-14 23:07:32 +01:00
Berserker
d14e8b98cd rewording 2026-03-14 22:38:44 +01:00
Berserker
826ac2ed19 Core: Other Games and Tools page 2026-03-14 22:22:04 +01:00
Fabian Dill
70fc3e05fb Webhost: port reuse fix & configurable max room timeout (#6033)
* WebHost: make autolauncher max room timeout configurable

* WebHost: launch rooms with assigned port first
2026-03-12 02:48:45 +01:00
Duck
d01c9577ab CommonClient: Add explicit message for connection timeout (#5842)
* Change timeout and add timeout-specific message

* Revert open_timeout
2026-03-11 23:46:59 +01:00
qwint
260bae359d Core: Update .gitignore to include an exe setup.py downloads (#6031) 2026-03-11 21:37:00 +01:00
Mysteryem
3016379b85 KH2: Fix nondeterministic generation when CasualBounties is enabled (#5967)
When CasualBounties was enabled, the location names in
`exclusion_table["HitlistCasual"]` would be iterated into
`self.random_super_boss_list` in `generate_early`, but
`exclusion_table["HitlistCasual"]` was a `set`, so its iteration order
would vary on each generation, even with same seed.

Random location names would be picked from `self.random_super_boss_list`
to place Bounty items at, so different locations could be picked on each
generation with the same seed.

`exclusion_table["Hitlist"]` is similar and was already a `list`,
avoiding the issue of nondeterministic iteration order, so
`exclusion_table["HitlistCasual"]` has been changed to a `list` to
match.
2026-03-10 23:06:44 +01:00
14 changed files with 51 additions and 116 deletions

1
.gitignore vendored
View File

@@ -45,6 +45,7 @@ EnemizerCLI/
/SNI/
/sni-*/
/appimagetool*
/VC_redist.x64.exe
/host.yaml
/options.yaml
/config.yaml

View File

@@ -773,7 +773,7 @@ class CommonContext:
if len(parts) == 1:
parts = title.split(', ', 1)
if len(parts) > 1:
text = parts[1] + '\n\n' + text
text = f"{parts[1]}\n\n{text}" if text else parts[1]
title = parts[0]
# display error
self._messagebox = MessageBox(title, text, error=True)
@@ -896,6 +896,8 @@ async def server_loop(ctx: CommonContext, address: typing.Optional[str] = None)
"May not be running Archipelago on that address or port.")
except websockets.InvalidURI:
ctx.handle_connection_loss("Failed to connect to the multiworld server (invalid URI)")
except asyncio.TimeoutError:
ctx.handle_connection_loss("Failed to connect to the multiworld server. Connection timed out.")
except OSError:
ctx.handle_connection_loss("Failed to connect to the multiworld server")
except Exception:

View File

@@ -46,6 +46,8 @@ app.config["SELFGEN"] = True # application process is in charge of scheduling G
app.config["JOB_THRESHOLD"] = 1
# after what time in seconds should generation be aborted, freeing the queue slot. Can be set to None to disable.
app.config["JOB_TIME"] = 600
# maximum time in seconds since last activity for a room to be hosted
app.config["MAX_ROOM_TIMEOUT"] = 259200
# memory limit for generator processes in bytes
app.config["GENERATOR_MEMORY_LIMIT"] = 4294967296

View File

@@ -9,7 +9,7 @@ from threading import Event, Thread
from typing import Any
from uuid import UUID
from pony.orm import db_session, select, commit, PrimaryKey
from pony.orm import db_session, select, commit, PrimaryKey, desc
from Utils import restricted_loads, utcnow
from .locker import Locker, AlreadyRunningException
@@ -129,7 +129,8 @@ def autohost(config: dict):
with db_session:
rooms = select(
room for room in Room if
room.last_activity >= utcnow() - timedelta(days=3))
room.last_activity >= utcnow() - timedelta(
seconds=config["MAX_ROOM_TIMEOUT"])).order_by(desc(Room.last_port))
for room in rooms:
# we have to filter twice, as the per-room timeout can't currently be PonyORM transpiled.
if room.last_activity >= utcnow() - timedelta(seconds=room.timeout + 5):

View File

@@ -19,8 +19,6 @@
# NewSoupVi is acting maintainer, but world belongs to core with the exception of the music
/worlds/apquest/ @NewSoupVi
# Sudoku (APSudoku)
/worlds/apsudoku/ @EmilyV99
# Aquaria
/worlds/aquaria/ @tioui

View File

@@ -71,7 +71,6 @@ non_apworlds: set[str] = {
"Ocarina of Time",
"Overcooked! 2",
"Raft",
"Sudoku",
"Super Mario 64",
"VVVVVV",
"Wargroove",

View File

@@ -11,7 +11,7 @@ class TestImplemented(unittest.TestCase):
def test_completion_condition(self):
"""Ensure a completion condition is set that has requirements."""
for game_name, world_type in AutoWorldRegister.world_types.items():
if not world_type.hidden and game_name not in {"Sudoku"}:
if not world_type.hidden:
with self.subTest(game_name):
multiworld = setup_solo_multiworld(world_type)
self.assertFalse(multiworld.completion_condition[1](multiworld.state))
@@ -59,7 +59,7 @@ class TestImplemented(unittest.TestCase):
def test_prefill_items(self):
"""Test that every world can reach every location from allstate before pre_fill."""
for gamename, world_type in AutoWorldRegister.world_types.items():
if gamename not in ("Archipelago", "Sudoku", "Final Fantasy", "Test Game"):
if gamename not in ("Archipelago", "Final Fantasy", "Test Game"):
with self.subTest(gamename):
multiworld = setup_solo_multiworld(world_type, ("generate_early", "create_regions", "create_items",
"set_rules", "connect_entrances", "generate_basic"))

View File

@@ -109,7 +109,7 @@ class TestOptions(unittest.TestCase):
def test_option_set_keys_random(self):
"""Tests that option sets do not contain 'random' and its variants as valid keys"""
for game_name, world_type in AutoWorldRegister.world_types.items():
if game_name not in ("Archipelago", "Sudoku", "Super Metroid"):
if game_name not in ("Archipelago", "Super Metroid"):
for option_key, option in world_type.options_dataclass.type_hints.items():
if issubclass(option, OptionSet):
with self.subTest(game=game_name, option=option_key):

View File

@@ -1,34 +0,0 @@
from typing import Dict
from BaseClasses import Tutorial
from ..AutoWorld import WebWorld, World
class AP_SudokuWebWorld(WebWorld):
options_page = False
theme = 'partyTime'
setup_en = Tutorial(
tutorial_name='Setup Guide',
description='A guide to playing APSudoku',
language='English',
file_name='setup_en.md',
link='setup/en',
authors=['EmilyV']
)
tutorials = [setup_en]
class AP_SudokuWorld(World):
"""
Play a little Sudoku while you're in BK mode to maybe get some useful hints
"""
game = "Sudoku"
web = AP_SudokuWebWorld()
item_name_to_id: Dict[str, int] = {}
location_name_to_id: Dict[str, int] = {}
@classmethod
def stage_assert_generate(cls, multiworld):
raise Exception("APSudoku cannot be used for generating worlds, the client can instead connect to any slot from any world")

View File

@@ -1,15 +0,0 @@
# APSudoku
## Hint Games
HintGames do not need to be added at the start of a seed, and do not create a 'slot'- instead, you connect the HintGame client to a different game's slot. By playing a HintGame, you can earn hints for the connected slot.
## What is this game?
Play Sudoku puzzles of varying difficulties, earning a hint for each puzzle correctly solved. Harder puzzles are more likely to grant a hint towards a Progression item, though otherwise what hint is granted is random.
## Where is the options page?
There is no options page; this game cannot be used in your .yamls. Instead, the client can connect to any slot in a multiworld.
By using the connected room's Admin Password on the Admin Panel tab, you can configure some settings at any time to affect the entire room. This allows disabling hints entirely, as well as altering the hint odds for each difficulty.

View File

@@ -1,55 +0,0 @@
# APSudoku Setup Guide
## Required Software
- [APSudoku](https://github.com/APSudoku/APSudoku)
## General Concept
This is a HintGame client, which can connect to any multiworld slot, allowing you to play Sudoku to unlock random hints for that slot's locations.
Does not need to be added at the start of a seed, as it does not create any slots of its own, nor does it have any YAML files.
## Installation Procedures
### Windows / Linux
Go to the latest release from the [github APSudoku Releases page](https://github.com/APSudoku/APSudoku/releases/latest). Download and extract the appropriate file for your platform.
### Web
Go to the [github pages](apsudoku.github.io) or [itch.io](https://emilyv99.itch.io/apsudoku) site, and play in the browser.
## Joining a MultiWorld Game
1. Run the APSudoku executable.
2. Under `Settings` &rarr; `Connection` at the top-right:
- Enter the server address and port number
- Enter the name of the slot you wish to connect to
- Enter the room password (optional)
- Select DeathLink related settings (optional)
- Press `Connect`
4. Under the `Sudoku` tab
- Choose puzzle difficulty
- Click `Start` to generate a puzzle
5. Try to solve the Sudoku. Click `Check` when done
- A correct solution rewards you with 1 hint for a location in the world you are connected to
- An incorrect solution has no penalty, unless DeathLink is enabled (see below)
Info:
- You can set various settings under `Settings` &rarr; `Sudoku`, and can change the colors used under `Settings` &rarr; `Theme`.
- While connected, you can view the `Console` and `Hints` tabs for standard TextClient-like features
- You can also use the `Tracking` tab to view either a basic tracker or a valid [GodotAP tracker pack](https://github.com/EmilyV99/GodotAP/blob/main/tracker_packs/GET_PACKS.md)
- While connected, the number of "unhinted" locations for your slot is shown in the upper-left of the the `Sudoku` tab. (If this reads 0, no further hints can be earned for this slot, as every locations is already hinted)
- Click the various `?` buttons for information on controls/how to play
## Admin Settings
By using the connected room's Admin Password on the Admin Panel tab, you can configure some settings at any time to affect the entire room.
- You can disable APSudoku for the entire room, preventing any hints from being granted.
- You can customize the reward weights for each difficulty, making progression hints more or less likely, and/or adding a chance to get "no hint" after a solve.
## DeathLink Support
If `DeathLink` is enabled when you click `Connect`:
- Lose a life if you check an incorrect puzzle (not an _incomplete_ puzzle- if any cells are empty, you get off with a warning), or if you quit a puzzle without solving it (including disconnecting).
- Your life count is customizable (default 0). Dying with 0 lives left kills linked players AND resets your puzzle.
- On receiving a DeathLink from another player, your puzzle resets.

View File

@@ -26,7 +26,10 @@ class GenericWeb(WebWorld):
'English', 'setup_en.md', 'setup/en', ['alwaysintreble'])
triggers = Tutorial('Archipelago Triggers Guide', 'A guide to setting up and using triggers in your game settings.',
'English', 'triggers_en.md', 'triggers/en', ['alwaysintreble'])
tutorials = [setup, mac, commands, advanced_settings, triggers, plando]
other_games = Tutorial('Other Games and Tools',
'A guide to additional games and tools that can be used with Archipelago.',
'English', 'other_en.md', 'other/en', ['Berserker'])
tutorials = [setup, mac, commands, advanced_settings, triggers, plando, other_games]
class GenericWorld(World):

View File

@@ -0,0 +1,33 @@
# Other Games And Tools
This page provides information and links regarding various tools that may be of use with Archipelago, including additional playable games not supported by this website.
You should only download and use files from sources you trust; sources listed here are not officially vetted for safety, so use your own judgement and caution.
## Discord
Currently, Discord is the primary hub for Archipelago; whether it be finding people to play with, developing new game implementations, or finding new playable games.
The [Archipelago Official Discord](https://discord.gg/8Z65BR2) is the main hub, while the [Archipelago After Dark Discord](https://discord.gg/fqvNCCRsu4) houses additional games that may be unrated or 18+ in some territories.
The `#apworld-index` channels in each of these servers contain lists of playable games which should be easily downloadable and playable with an Archipelago installation.
## Wiki
The community-maintained [Archipelago Wiki](https://archipelago.miraheze.org/) has information on many games as well, and acts as a great discord-free source of information.
## Hint Games
Hint Games are a special type of game which are not included as part of the multiworld generation process. Instead, they can log in to an ongoing multiworld, connecting to a slot designated for any game. Rather than earning items for other games in the multiworld, a Hint Game will allow you to earn hints for the slot you are connected to.
Hint Games can be found from sources such as the Discord and the [Hint Game Category](https://archipelago.miraheze.org/wiki/Category:Hint_games) of the wiki, as detailed above.
## Notable Tools
### Options Creator
The Options Creator is included in the Archipelago installation, and is accessible from the Archipelago Launcher. Using this simple GUI tool, you can easily create randomization options for any installed '.apworld'- perfect when using custom worlds you've installed that don't have options pages on the website.
### PopTracker
[PopTracker](https://github.com/black-sliver/PopTracker) is a popular tool in Randomizer communities, which many games support via custom PopTracker Packs. Many Archipelago packs include the ability to directly connect to your slot for auto-tracking capabilities. (Check each game's setup guide to see if it has PopTracker compatibility!)

View File

@@ -1281,7 +1281,7 @@ exclusion_table = {
LocationName.HadesCupTrophyParadoxCups,
LocationName.MusicalOrichalcumPlus,
],
"HitlistCasual": {
"HitlistCasual": [
LocationName.FuturePete,
LocationName.BetwixtandBetweenBondofFlame,
LocationName.GrimReaper2,
@@ -1299,7 +1299,7 @@ exclusion_table = {
LocationName.MCP,
LocationName.Lvl50,
LocationName.Lvl99
},
],
"Cups": {
LocationName.ProtectBeltPainandPanicCup,
LocationName.SerenityGemPainandPanicCup,