Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
153 commits
Select commit Hold shift + click to select a range
7a13119
Docs: Update max py version to 3.13 #5410
qwint Sep 3, 2025
8f88152
MultiServer: Validate CreateHints status arg #5408
qwint Sep 3, 2025
3c28db0
LADX: Drop a marin text option that makes patching fail #5398
Sep 3, 2025
e342a20
Celeste (Open World): Post-merge Logic Fix (#5415)
PoryGone Sep 4, 2025
03992c4
Docs: update mac install instructions to reflect 3.13 support (#5411)
qwint Sep 4, 2025
42ace29
Celeste 64: Logic Fixes #5417
PoryGone Sep 4, 2025
b0b3e36
TUNIC: The Big Refactor (#5195)
ScipioWright Sep 4, 2025
ef59a5e
TUNIC: Change non_local_items Earlier (#5249)
Exempt-Medic Sep 4, 2025
5b5e2c3
SMZ3: Fix distribution of SM prizes (#5303)
KScl Sep 5, 2025
89be26a
Heretic: Update Steam URL (#5304)
KScl Sep 5, 2025
64d3c55
Stardew Valley: Add money logic to traveling merchant (#5327)
Jouramie Sep 5, 2025
7a38e44
Test: Deprecate TestBase (#5339)
qwint Sep 5, 2025
e518e41
Hollow Knight: Make the connecting header separate from the yaml one …
ScipioWright Sep 5, 2025
b9fb5c8
Super Mario Land 2: Remove erroneous Coinsanity checks #5364
Alchav Sep 5, 2025
0d26b64
Core: Remove lttp module requirement from generation #5384
qwint Sep 5, 2025
8c2d246
SC2: Restrict allow Orphan to missions that already require that (#5405)
Ziktofel Sep 5, 2025
5c6dbdd
SC2: Update docs for Linux launch script to follow the core client mi…
Ziktofel Sep 5, 2025
90058ee
CommonClient: fix /items, /locations and /missing not working if the …
Berserker66 Sep 5, 2025
e23720a
LttP: shuffle around gitignore (#5307)
Berserker66 Sep 5, 2025
31b2eed
TUNIC: Make the local_fill option show up on the website #5348
ScipioWright Sep 5, 2025
77cab13
ArchipIDLE: Remove game #5422
NewSoupVi Sep 5, 2025
c5b404b
CI: only trigger release action for bare semver (#5065)
black-sliver Sep 5, 2025
c3c517a
DS3: use yaml.safe_load (#5360)
black-sliver Sep 6, 2025
8a091c9
Kivy: Fix MessageBox popups (#5193)
duckboycool Sep 6, 2025
1b200fb
Choo-Choo Charles: implement new game and documentations (#5287)
lgbarrere Sep 8, 2025
63f3512
Core: adds a custom KeyError for invalid item names (#4223)
qwint Sep 8, 2025
17dad83
Test: Remove most dependencies on lttp (#5338)
qwint Sep 8, 2025
18ac921
SA2B: Logic Fixes and Black Market Trap Name Improvements (#5427)
PoryGone Sep 9, 2025
287bb63
Docs: Kivy Style (#5425)
Berserker66 Sep 9, 2025
9aa0bf7
FF1: New Maintainership (#5027)
Rosalie-A Sep 9, 2025
78b529f
Timespinner: Adds Lantern Check flags, Missing Traps (#5188)
TriumphantBass Sep 10, 2025
1322ce8
Kingdom Hearts: Adding a bunch of new features (#5078)
gaithern Sep 10, 2025
aaaceeb
Timespinner: Add Boss Rando Type Options (#4466)
bendxn Sep 10, 2025
27e50aa
MultiServer: Make it so hint_location doesn't set an automatic priori…
NewSoupVi Sep 10, 2025
76a8b0d
CCCharles: Bug fix for cyclic connections of Entrances with the ignor…
lgbarrere Sep 12, 2025
4e08589
SC2: Region access rule speedups (#5426)
Salzkorn Sep 12, 2025
5975835
KH1: Remove top level script & remove script_name from its component …
NewSoupVi Sep 13, 2025
9c00eb9
WebHost: fix Internal Server Error if parallel access to /room/* happ…
Berserker66 Sep 14, 2025
71de33d
CI: Fix peer review tag on undrafting a PR (#5282)
duckboycool Sep 14, 2025
174d89c
feat(workflow): Implement new Github workflow for building and pushin…
a-priestley Sep 14, 2025
9fdeecd
KH2: Remove top level client script (#5446)
JaredWeakStrike Sep 15, 2025
8f2b4a9
Raft: Add Zipline Tool requirement to Engine controls blueprint #5455
SunnyBat Sep 16, 2025
73718bb
Core: make APContainer seek archipelago.json (#5261)
Berserker66 Sep 19, 2025
3af1e92
TUNIC: Update name of a chest in the UT poptracker map integration #5462
ScipioWright Sep 21, 2025
7badc3e
SC2: Logic bugfixes (#5461)
MatthewMarinets Sep 21, 2025
1bd44e1
Stardew Valley: Fixed Traveling merchant flaky test (#5434)
agilbert1412 Sep 21, 2025
9e96cec
LADX: Fix quickswap #5399
Sep 21, 2025
6c45c8d
Core: make countdown a "admin" only command (#5463)
Berserker66 Sep 21, 2025
68187ba
WebHost: remove team argument from tracker arguments where it's irrel…
Berserker66 Sep 21, 2025
fb9011d
WebHost: revamp /api/*tracker/ (#5388)
Berserker66 Sep 21, 2025
e256abf
Factorio: Allow to reconnect a timed out RCON client connection. (#5421)
CaitSith2 Sep 22, 2025
a99da85
Core: APWorld manifest (#4516)
Berserker66 Sep 24, 2025
dc27030
Core: improve formatting on /help command (#5381)
Berserker66 Sep 24, 2025
4525bae
Webhost: add total player location counts to tracker API (#5441)
Etsuna Sep 24, 2025
4ae87ed
Core: apworld manifest launcher component (#5340)
Berserker66 Sep 24, 2025
2439456
Core: Bump Container Version to 7, and make APWorldContainer use 7 as…
NewSoupVi Sep 25, 2025
12998bf
Pokemon Emerald: Fix missing fanfare address (#5490)
Zunawe Sep 27, 2025
d162467
Pokemon Emerald: Set all abilities to Cacophony if all are blackliste…
Zunawe Sep 28, 2025
1d861d1
Pokémon RB: Update Slot Data (#5494)
palex00 Sep 28, 2025
6099869
Core: new cx_freeze (#5316)
Berserker66 Sep 29, 2025
47b2242
TUNIC: Add archipelago.json (#5482)
ScipioWright Sep 30, 2025
25baa57
meritous: Create manifest (#5497)
FelicitusNeko Sep 30, 2025
f9083d9
bumpstik: Create manifest (#5496)
FelicitusNeko Sep 30, 2025
2f23dc7
Muse Dash: Update song list to Legendary Voyage, Mystic Treasure. Add…
DeamonHunter Sep 30, 2025
ab20979
Jak and Daxter: Add manifest #5492
massimilianodelliubaldini Sep 30, 2025
053f876
Pokemon Emerald: Add manifest (#5487)
Zunawe Sep 30, 2025
c30a5b2
Noita: Add archipelago.json (#5483)
ScipioWright Sep 30, 2025
580370c
Jak and Daxter: close Power Cell loophole in trades test #5493
massimilianodelliubaldini Sep 30, 2025
5345937
The Witness: Remove two things from slot_data that nothing uses anymo…
NewSoupVi Sep 30, 2025
d9955d6
KH1: Fix Slot 2 Level Checks description #5451
gaithern Sep 30, 2025
a30b438
KDL3, MM2: set goal condition before generate basic (#5382)
Silvris Sep 30, 2025
516ebc5
LADX: fix local lvl 2 sword on the beach turning into a lvl 0 shield …
Sep 30, 2025
1d2ad1f
Docs: More type annotation changes (#5301)
duckboycool Sep 30, 2025
92ff0dd
SC2: Launcher bugfixes after content merge (#5409)
MatthewMarinets Sep 30, 2025
897d5ab
SC2: Fix Conviction logic for Grant Story Tech (#5419)
Ziktofel Sep 30, 2025
49f2d30
Sc2: [performance] change default options (#5424)
MatthewMarinets Sep 30, 2025
448f214
core: Option to skip "unused" item links (#4608)
silasary Sep 30, 2025
5cec3f4
LADX: reorganize options page (#4851)
Sep 30, 2025
50c9d05
KH1: Fix a small error in option descriptions #5445
esutley Sep 30, 2025
f26fcc0
LADX: use generic slot name for slots 101+ (#5208)
Sep 30, 2025
0882c0f
Core: only store persistent changes if there are changes (#5311)
Berserker66 Sep 30, 2025
e6fb7d9
Core: Add an "options" arg to setup_multiworld so that non-default op…
NewSoupVi Sep 30, 2025
6a63de2
TUNIC: Fuse and Bell Shuffle (#5420)
ScipioWright Sep 30, 2025
76b0197
SC2: any_unit and item parent bugfixes (#5480)
MatthewMarinets Sep 30, 2025
4893ac3
SC2: Fix Terran global upgrades present even if no Terran build missi…
Ziktofel Oct 1, 2025
33b485c
Core: expose world version to world classes and yaml (#5484)
Silvris Oct 1, 2025
b162095
Launcher: Rework apworld install popup #5508
qwint Oct 1, 2025
50f6cf0
Core: "Build APWorlds" cleanup (#5507)
Silvris Oct 2, 2025
6d7abb3
Webhost: Ignore Invalid Worlds in Webhost (#5433)
qwint Oct 2, 2025
83cfb80
SMZ3: Fix forced fill behaviors (GT junk fill, initial Super/PB front…
KScl Oct 3, 2025
6a08064
Core: Assert that if an apworld manifest file exists, it has a game f…
NewSoupVi Oct 4, 2025
91e97b6
Webhost: eagerly free resources in customserver (#5512)
black-sliver Oct 5, 2025
ae4426a
Core: Pad version string in world printout #5511
duckboycool Oct 5, 2025
7a65251
[Website docs] Update wording of "adding a game to archipelago" section
ScipioWright Oct 5, 2025
7996fd8
Core: Update start inventory description to mention item quantities (…
PinkSwitch Oct 5, 2025
a547c8d
Core: Add location count field for world to spoiler log (#5440)
duckboycool Oct 5, 2025
ec9145e
Region: Use Mapping type for adding locations/exits #5354
duckboycool Oct 5, 2025
bdef410
Civilization VI: Update for the setup instructions #5286
DJ-lennart Oct 5, 2025
1cbc5d6
Short Hike: improve setup guide docs #5470
BrandenEK Oct 5, 2025
3eb25a5
Aquaria: Updating documentation to add latest clients informations (#…
tioui Oct 5, 2025
60070c2
PyCharm: add a run config for the new apworld builder workflow (#5489)
benny-dreamly Oct 5, 2025
f8f30f4
Launcher: Newly installed custom worlds are not relative #4989
silasary Oct 5, 2025
a2460b7
Pokemon RB: Add client tracking for tracker relevant events (#5495)
gerbiljames Oct 5, 2025
f07fea2
CommonClient: Move command marker to last_autofillable_command (#4907)
Jouramie Oct 5, 2025
adb5a7d
SA2B, DKC3, SMW, Celeste 64, Celeste (Open World): Manifest manifests
PoryGone Oct 5, 2025
8decde0
Core: Don't waste swaps by swapping two copies of the same item (#5516)
Mysteryem Oct 5, 2025
fd87940
WebHost: Improve user friendliness of generation failure webpage (#4964)
massimilianodelliubaldini Oct 5, 2025
60617c6
WebHost: fix log fetching extra characters when there is non-ascii (#…
black-sliver Oct 5, 2025
8dc9719
Core: Cleanup unneeded use of `Version`/`tuplize_version` (#5519)
duckboycool Oct 5, 2025
5c7a847
WebHost: Handle blank values for OptionCounters #5517
duckboycool Oct 6, 2025
c7978bc
Docs: Add info about custom worlds (#5510)
duckboycool Oct 6, 2025
5a933a1
SC2: Add option presets (#5436)
Snarkie Oct 7, 2025
e907980
MultiServer: slight optimizations (#5527)
black-sliver Oct 8, 2025
28ea244
kvui: re-enable settings menu (#4823)
Berserker66 Oct 8, 2025
e355d20
WebHost: Don't show e.__cause__ on the generation error page #5521
NewSoupVi Oct 8, 2025
ff59b86
Docs: More apworld manifest documentation (#5477)
NewSoupVi Oct 9, 2025
30ce74d
core: Add host.yaml setting to make !countdown configurable (#5465)
silasary Oct 10, 2025
d3588a0
Tests: gc.freeze() by default in the test\benchmark\locations.py (#5055)
Mysteryem Oct 10, 2025
6276ccf
LADX: move client out of root (#4226)
Oct 10, 2025
bb457b0
SNI Client: fix that it isnt using host.yaml settings (#5533)
Oct 11, 2025
5390561
MultiServer: Fix breaking weakrefs for SetNotify (#5539)
black-sliver Oct 12, 2025
0c1ecf7
Terraria: Remove `/apstart` from docs (#5537)
Seldom-SE Oct 13, 2025
30cedb1
Core: Limit ItemLink Name to 16 Characters (#4318)
Exempt-Medic Oct 13, 2025
aff98a5
CommonClient: Fix manually connecting to a url when the username or p…
NewSoupVi Oct 13, 2025
5ce71db
LADX: use start_inventory_from_pool (#4641)
Oct 13, 2025
fc404d0
MM2: fix Heat Man always being invulnerable to Atomic Fire #5546
Silvris Oct 14, 2025
bdae7cd
MultiServer: Fix hinting multi-copy items bleeding found status (#5547)
NewSoupVi Oct 14, 2025
28c7a21
Core: Use Better Practices Accessing Manifests (#5543)
nicholassaylor Oct 14, 2025
123acde
Docs: warn HK users not to use BepInEx #5550
BadMagic100 Oct 15, 2025
f6d696e
KH2: Manifest File (#5553)
JaredWeakStrike Oct 15, 2025
cf02e1a
shapez: Fix floating layers logic error #5263
BlastSlimey Oct 15, 2025
03bd59b
Ocarina of Time: Create manifest (#5536)
Rooby-Roo Oct 16, 2025
91439e0
KH2: Manifest eletric boogaloo (#5556)
JaredWeakStrike Oct 16, 2025
406b905
Stardew Valley: Add archipelago.json (#5535)
Jouramie Oct 16, 2025
f756919
CI: Add worlds manifests to build action trigger (#5555)
duckboycool Oct 16, 2025
0718ada
Core: Allow PlandoItems to be pickled (#5335)
duckboycool Oct 17, 2025
da519e7
SC2: fix incorrect preset option (#5551)
Snarkie Oct 17, 2025
3f2942c
Super Mario Land 2: Logic fixes #5258
Alchav Oct 17, 2025
f5f554c
[FF1] Client fix and improvement (#5390)
Rosalie-A Oct 17, 2025
7ead8fd
Civ 6: Add era requirements for boosts and update boost prereqs (#5296)
hesto2 Oct 17, 2025
946f227
[FF1] Added Deep Dungeon locations to locations.json so they exist in…
Rosalie-A Oct 17, 2025
2569c9e
DLC Quest: Enable multi-classification items (#5552)
benny-dreamly Oct 19, 2025
2ac9ab5
Docs: add warning about BepInEx to HK translated setup guides (#5554)
Fafale Oct 19, 2025
00acfe6
WebHost: Update publish_parts parameters (#5544)
nicholassaylor Oct 19, 2025
11d18db
Docs: APWorld documentation, make a distinction between APWorld and .…
NewSoupVi Oct 19, 2025
914a534
WebHost: fix gen timeout/exception resource handling (#5540)
black-sliver Oct 20, 2025
708df4d
WebHost: Fix flask-compress to 1.18 for Python 3.11 (to get CI to pas…
NewSoupVi Oct 20, 2025
7cd73e2
WebHost: Fix generate argparse with --config-override + add autogen u…
NewSoupVi Oct 20, 2025
621ec27
Yugioh: Fix likely unintended concatenations (#5567)
duckboycool Oct 20, 2025
d2bf7fd
AHiT: Fix likely unintended concatenation #5565
duckboycool Oct 20, 2025
c199775
Pokemon RB: Fix likely unintended concatenation #5566
duckboycool Oct 20, 2025
e8c8b0d
MM2: fix Proteus reading #5575
Silvris Oct 21, 2025
3105320
Test: check fields in world source manifest (#5558)
black-sliver Oct 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ on:
- 'setup.py'
- 'requirements.txt'
- '*.iss'
- 'worlds/*/archipelago.json'
pull_request:
paths:
- '.github/workflows/build.yml'
- 'setup.py'
- 'requirements.txt'
- '*.iss'
- 'worlds/*/archipelago.json'
workflow_dispatch:

env:
Expand Down
154 changes: 154 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
name: Build and Publish Docker Images

on:
push:
paths:
- "**"
- "!docs/**"
- "!deploy/**"
- "!setup.py"
- "!.gitignore"
- "!.github/workflows/**"
- ".github/workflows/docker.yml"
branches:
- "*"
tags:
- "v?[0-9]+.[0-9]+.[0-9]*"
workflow_dispatch:

env:
REGISTRY: ghcr.io

jobs:
prepare:
runs-on: ubuntu-latest
outputs:
image-name: ${{ steps.image.outputs.name }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
package-name: ${{ steps.package.outputs.name }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set lowercase image name
id: image
run: |
echo "name=${GITHUB_REPOSITORY,,}" >> $GITHUB_OUTPUT

- name: Set package name
id: package
run: |
echo "name=$(basename ${GITHUB_REPOSITORY,,})" >> $GITHUB_OUTPUT

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ steps.image.outputs.name }}
tags: |
type=ref,event=branch,enable={{is_not_default_branch}}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=nightly,enable={{is_default_branch}}

- name: Compute final tags
id: final-tags
run: |
readarray -t tags <<< "${{ steps.meta.outputs.tags }}"

if [[ "${{ github.ref_type }}" == "tag" ]]; then
tag="${{ github.ref_name }}"
if [[ "$tag" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
full_latest="${{ env.REGISTRY }}/${{ steps.image.outputs.name }}:latest"
# Check if latest is already in tags to avoid duplicates
if ! printf '%s\n' "${tags[@]}" | grep -q "^$full_latest$"; then
tags+=("$full_latest")
fi
fi
fi

# Set multiline output
echo "tags<<EOF" >> $GITHUB_OUTPUT
printf '%s\n' "${tags[@]}" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

build:
needs: prepare
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
strategy:
matrix:
include:
- platform: amd64
runner: ubuntu-latest
suffix: amd64
cache-scope: amd64
- platform: arm64
runner: ubuntu-24.04-arm
suffix: arm64
cache-scope: arm64
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Compute suffixed tags
id: tags
run: |
readarray -t tags <<< "${{ needs.prepare.outputs.tags }}"
suffixed=()
for t in "${tags[@]}"; do
suffixed+=("$t-${{ matrix.suffix }}")
done
echo "tags=$(IFS=','; echo "${suffixed[*]}")" >> $GITHUB_OUTPUT

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/${{ matrix.platform }}
push: true
tags: ${{ steps.tags.outputs.tags }}
labels: ${{ needs.prepare.outputs.labels }}
cache-from: type=gha,scope=${{ matrix.cache-scope }}
cache-to: type=gha,mode=max,scope=${{ matrix.cache-scope }}
provenance: false

manifest:
needs: [prepare, build]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create and push multi-arch manifest
run: |
readarray -t tag_array <<< "${{ needs.prepare.outputs.tags }}"

for tag in "${tag_array[@]}"; do
docker manifest create "$tag" \
"$tag-amd64" \
"$tag-arm64"

docker manifest push "$tag"
done
1 change: 0 additions & 1 deletion .github/workflows/label-pull-requests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ env:
jobs:
labeler:
name: 'Apply content-based labels'
if: github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize'
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: Release
on:
push:
tags:
- '*.*.*'
- 'v?[0-9]+.[0-9]+.[0-9]*'

env:
ENEMIZER_VERSION: 7.1
Expand Down
24 changes: 24 additions & 0 deletions .run/Build APWorld.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Build APWorld" type="PythonConfigurationType" factoryName="Python">
<module name="Archipelago" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$ContentRoot$/Launcher.py" />
<option name="PARAMETERS" value="\&quot;Build APWorlds\&quot;" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>
18 changes: 13 additions & 5 deletions BaseClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ def set_item_links(self):
"local_items": set(item_link.get("local_items", [])),
"non_local_items": set(item_link.get("non_local_items", [])),
"link_replacement": replacement_prio.index(item_link["link_replacement"]),
"skip_if_solo": item_link.get("skip_if_solo", False),
}

for _name, item_link in item_links.items():
Expand All @@ -284,6 +285,8 @@ def set_item_links(self):

for group_name, item_link in item_links.items():
game = item_link["game"]
if item_link["skip_if_solo"] and len(item_link["players"]) == 1:
continue
group_id, group = self.add_group(group_name, game, set(item_link["players"]))

group["item_pool"] = item_link["item_pool"]
Expand Down Expand Up @@ -1343,8 +1346,7 @@ def get_connecting_entrance(self, is_main_entrance: Callable[[Entrance], bool])
for entrance in self.entrances: # BFS might be better here, trying DFS for now.
return entrance.parent_region.get_connecting_entrance(is_main_entrance)

def add_locations(self, locations: Dict[str, Optional[int]],
location_type: Optional[type[Location]] = None) -> None:
def add_locations(self, locations: Mapping[str, int | None], location_type: type[Location] | None = None) -> None:
"""
Adds locations to the Region object, where location_type is your Location class and locations is a dict of
location names to address.
Expand Down Expand Up @@ -1432,16 +1434,16 @@ def create_er_target(self, name: str) -> Entrance:
entrance.connect(self)
return entrance

def add_exits(self, exits: Union[Iterable[str], Dict[str, Optional[str]]],
rules: Dict[str, Callable[[CollectionState], bool]] = None) -> List[Entrance]:
def add_exits(self, exits: Iterable[str] | Mapping[str, str | None],
rules: Mapping[str, Callable[[CollectionState], bool]] | None = None) -> List[Entrance]:
"""
Connects current region to regions in exit dictionary. Passed region names must exist first.

:param exits: exits from the region. format is {"connecting_region": "exit_name"}. if a non dict is provided,
created entrances will be named "self.name -> connecting_region"
:param rules: rules for the exits from this region. format is {"connecting_region": rule}
"""
if not isinstance(exits, Dict):
if not isinstance(exits, Mapping):
exits = dict.fromkeys(exits)
return [
self.connect(
Expand Down Expand Up @@ -1855,6 +1857,9 @@ def write_option(option_key: str, option_obj: Options.AssembleOptions) -> None:
Utils.__version__, self.multiworld.seed))
outfile.write('Filling Algorithm: %s\n' % self.multiworld.algorithm)
outfile.write('Players: %d\n' % self.multiworld.players)
if self.multiworld.players > 1:
loc_count = len([loc for loc in self.multiworld.get_locations() if not loc.is_event])
outfile.write('Total Location Count: %d\n' % loc_count)
outfile.write(f'Plando Options: {self.multiworld.plando_options}\n')
AutoWorld.call_stage(self.multiworld, "write_spoiler_header", outfile)

Expand All @@ -1863,6 +1868,9 @@ def write_option(option_key: str, option_obj: Options.AssembleOptions) -> None:
outfile.write('\nPlayer %d: %s\n' % (player, self.multiworld.get_player_name(player)))
outfile.write('Game: %s\n' % self.multiworld.game[player])

loc_count = len([loc for loc in self.multiworld.get_locations(player) if not loc.is_event])
outfile.write('Location Count: %d\n' % loc_count)

for f_option, option in self.multiworld.worlds[player].options_dataclass.type_hints.items():
write_option(f_option, option)

Expand Down
37 changes: 11 additions & 26 deletions CommonClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,6 @@ def _cmd_received(self) -> bool:
self.ctx.on_print_json({"data": parts, "cmd": "PrintJSON"})
return True

def get_current_datapackage(self) -> dict[str, typing.Any]:
"""
Return datapackage for current game if known.

:return: The datapackage for the currently registered game. If not found, an empty dictionary will be returned.
"""
if not self.ctx.game:
return {}
checksum = self.ctx.checksums[self.ctx.game]
return Utils.load_data_package_for_checksum(self.ctx.game, checksum)

def _cmd_missing(self, filter_text = "") -> bool:
"""List all missing location checks, from your local game state.
Can be given text, which will be used as filter."""
Expand All @@ -119,8 +108,8 @@ def _cmd_missing(self, filter_text = "") -> bool:
count = 0
checked_count = 0

lookup = self.get_current_datapackage().get("location_name_to_id", {})
for location, location_id in lookup.items():
lookup = self.ctx.location_names[self.ctx.game]
for location_id, location in lookup.items():
if filter_text and filter_text not in location:
continue
if location_id < 0:
Expand All @@ -141,11 +130,10 @@ def _cmd_missing(self, filter_text = "") -> bool:
self.output("No missing location checks found.")
return True

def output_datapackage_part(self, key: str, name: str) -> bool:
def output_datapackage_part(self, name: typing.Literal["Item Names", "Location Names"]) -> bool:
"""
Helper to digest a specific section of this game's datapackage.

:param key: The dictionary key in the datapackage.
:param name: Printed to the user as context for the part.

:return: Whether the process was successful.
Expand All @@ -154,23 +142,20 @@ def output_datapackage_part(self, key: str, name: str) -> bool:
self.output(f"No game set, cannot determine {name}.")
return False

lookup = self.get_current_datapackage().get(key)
if lookup is None:
self.output("datapackage not yet loaded, try again")
return False

lookup = self.ctx.item_names if name == "Item Names" else self.ctx.location_names
lookup = lookup[self.ctx.game]
self.output(f"{name} for {self.ctx.game}")
for key in lookup:
self.output(key)
for name in lookup.values():
self.output(name)
return True

def _cmd_items(self) -> bool:
"""List all item names for the currently running game."""
return self.output_datapackage_part("item_name_to_id", "Item Names")
return self.output_datapackage_part("Item Names")

def _cmd_locations(self) -> bool:
"""List all location names for the currently running game."""
return self.output_datapackage_part("location_name_to_id", "Location Names")
return self.output_datapackage_part("Location Names")

def output_group_part(self, group_key: typing.Literal["item_name_groups", "location_name_groups"],
filter_key: str,
Expand Down Expand Up @@ -871,9 +856,9 @@ async def server_loop(ctx: CommonContext, address: typing.Optional[str] = None)

server_url = urllib.parse.urlparse(address)
if server_url.username:
ctx.username = server_url.username
ctx.username = urllib.parse.unquote(server_url.username)
if server_url.password:
ctx.password = server_url.password
ctx.password = urllib.parse.unquote(server_url.password)

def reconnect_hint() -> str:
return ", type /connect to reconnect" if ctx.server_address else ""
Expand Down
4 changes: 4 additions & 0 deletions Fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ def fill_restrictive(multiworld: MultiWorld, base_state: CollectionState, locati
for i, location in enumerate(placements))
for (i, location, unsafe) in swap_attempts:
placed_item = location.item
if item_to_place == placed_item:
# The number of allowed swaps is limited, so do not allow a swap of an item with a copy of
# itself.
continue
# Unplaceable items can sometimes be swapped infinitely. Limit the
# number of times we will swap an individual item to prevent this
swap_count = swapped_items[placed_item.player, placed_item.name, unsafe]
Expand Down
Loading
Loading