Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
5b6714d
chore(documentation): Update deployment example config (#5476)
a-priestley Nov 8, 2025
37b87e3
[Docs] Update docs/network protocol.md/NetworkVersion to include clas…
Quasky Nov 8, 2025
96ae223
CCCharles: Fix editorial issues in documentations (#5611)
lgbarrere Nov 8, 2025
360ad71
CI: downgrade pytest to 8.4.2 (#5613)
black-sliver Nov 8, 2025
ecadb30
Core: Allows Meta.yaml to add triggers to individual yaml's categorie…
Vertraic Nov 8, 2025
b2b0d15
Core: add export_datapackage tool (#5609)
Berserker66 Nov 9, 2025
77808d3
Core: Bump version from 0.6.4 to 0.6.5 (#5607)
Berserker66 Nov 9, 2025
19db589
Game Docs: Fix main setup guide links (#5603)
duckboycool Nov 9, 2025
bec6256
SC2 Tracker: Fix bundled Protoss W/A upgrade display (#5612)
Ziktofel Nov 9, 2025
14f261b
Launcher: add skip_open_folder arg to Generate Template Options (#5302)
gurglemurgle5 Nov 9, 2025
0bf48d7
fix(workflows): Update branch filter in Docker workflow (#5616)
a-priestley Nov 9, 2025
ea40156
Jak 1: Remove PAL-only instructions, no longer needed. (#5598)
massimilianodelliubaldini Nov 11, 2025
088f2cc
SC2: Remove dependency on s2clientprotocol and update protobuf versio…
GreenestBeen Nov 11, 2025
283badf
SoE: add apworld manifest (#5557)
black-sliver Nov 11, 2025
4b2298e
SC2: make worlds._sc2common.bot.proto a regular package (#5626)
black-sliver Nov 11, 2025
dba03e3
Choo Choo Charles: Raise InvalidItemError instead of bare Exception (…
NewSoupVi Nov 12, 2025
e4fd064
Core: don't use union type just to reuse a name (#5246)
beauxq Nov 12, 2025
4a41550
CI: update pytest to 9.0.1 (#5637)
black-sliver Nov 14, 2025
d597bc4
Docs: Add troubleshooting section to kh1_en.md, typo fix in kh1/Optio…
Omnises Nov 14, 2025
5779dda
Core: Deprecate Utils.get_options by July 31st, 2025 (#4811)
NewSoupVi Nov 14, 2025
a2f8877
Core: Fix #5605 - Trigger values being shared by weights.yaml slots (…
Mysteryem Nov 14, 2025
d7eb95a
SC2: Allowing unexcluded_items to affect items excluded by vanilla_it…
MatthewMarinets Nov 15, 2025
cde73c5
SC2: Move race_swap pick_one functionality to mission picking (#5538)
Salzkorn Nov 15, 2025
c408c53
LADX: create manifest (#5563)
Nov 15, 2025
98273dd
SC2: Add Manifest (#5559)
Snarkie Nov 15, 2025
93d3d8b
SC2: Fixing a gap in the ascendant upgrades in the tracker (#5570)
MatthewMarinets Nov 15, 2025
7e8746c
Pokémon R/B: Specify encounter types for Dexsanity hint data (#5574)
snowflav-goob Nov 15, 2025
d098372
Wargroove 1: added archipelago.json (#5591)
FlySniper Nov 15, 2025
34e13c5
SC2: Adjusting and slightly simplifying mission difficulty pool adjus…
MatthewMarinets Nov 15, 2025
75eb266
CV64: Fix not having Clocktower Key3 when placed in a start_inventory…
LiquidCat64 Nov 15, 2025
557a284
SC2: Fix custom mission order if used in weights.yaml (#5604)
Ziktofel Nov 15, 2025
e4b5591
CV64: Fix not having Clocktower Key3 when placed in a start_inventory…
Nov 15, 2025
7422b10
SC2: Fix the goal mission tooltip depending on goal missions' status …
Ziktofel Nov 15, 2025
494381b
Factorio: Add no-enemies mode to worldgen schema (#5542)
silasary Nov 15, 2025
8fbd356
Core: add a local yaml creator GUI (#4900)
Silvris Nov 15, 2025
b828781
Core: add random range and additional random descriptions to template…
Silvris Nov 15, 2025
c2094a9
Muse Dash: Update Song list to Medium5 Echoes (#5597)
DeamonHunter Nov 15, 2025
3bb43b2
Landstalker: Add manifest file (#5629)
Dinopony Nov 15, 2025
5055f87
The Witness: Add archipelago.json (#5481)
NewSoupVi Nov 15, 2025
3ec1e91
Core: Only error in playthrough generation if game is not beatable (#…
NewSoupVi Nov 15, 2025
b3c323e
The Witness: Fix CreateHints spoiling vague hints (#5359)
NewSoupVi Nov 15, 2025
24aa4af
WebHost: Validation for webworld themes (#5083)
josephwhite Nov 15, 2025
5e08c8b
Celeste (Open World): fix tutorial link on game page (#5627)
black-sliver Nov 15, 2025
01e64a2
Doc: Update Mac instructions to instruct the user to make a frozen bu…
silasary Nov 16, 2025
3c819ec
LttP: logic fixes and missing bombs (#5645)
Silvris Nov 16, 2025
32a0210
Factorio: Add connection change filtering functionality (#4997)
af-chacon Nov 21, 2025
fd968d7
Yacht Dice: Add archipelago.json manifest #5658
spinerak Nov 21, 2025
8b737ca
Core: Better error message for invalid range values (#4038)
Zannick Nov 22, 2025
c2f76d8
TWW: Fix client sending duplicate magic meter (#5664)
tanjo3 Nov 23, 2025
2d15c23
SC2: Fix missing brackets in Zerg The Host logic (#5657)
Ziktofel Nov 23, 2025
e60ea17
SC2: Migrate external resources from user repos to sc2 organization (…
Ziktofel Nov 24, 2025
447f8fb
LADX: switch to asyncio.get_running_loop() (#5666)
Nov 24, 2025
e0cbf77
APQuest: Implement New Game (#5393)
NewSoupVi Nov 24, 2025
f9630fa
Core: Add a bunch of validation to AutoPatchRegister (#5431)
NewSoupVi Nov 24, 2025
aa2774a
Tests: Move world dependencies in tests to APQuest #5668
qwint Nov 25, 2025
f3000a8
LADX: Give better feedback during patching (#5401)
Nov 25, 2025
d834ece
SC2: Fix bugs and issues around excluded/unexcluded (#5644)
MatthewMarinets Nov 25, 2025
8ea49e7
Core: updates of requirements (#5672)
Berserker66 Nov 25, 2025
c2c4884
Core: Fix typo in docstring for hint_points in commonclient (#5673)
Berserker66 Nov 25, 2025
23d3192
APQuest: Fix import of Protocol from bokeh instead of typing (#5674)
NewSoupVi Nov 25, 2025
ec2c39e
Docs: Improve the documentation for priority locations to mention de-…
Emerassi Nov 26, 2025
3e16c20
PyCharm: fix the apworld builder run config (#5678)
benny-dreamly Nov 26, 2025
3b721e0
Tests: Move hosting test to APQuest #5671
NewSoupVi Nov 26, 2025
60a192b
ALttP/Factorio: Add spaces in concatenated strings (#5564)
duckboycool Nov 27, 2025
8a1a715
SC2: logic fixes minor bugs (#5660)
MatthewMarinets Nov 29, 2025
a07faca
LADX: catch exception after closing magpie #5687
Nov 29, 2025
b75cce5
TLOZ: Add space in concatenated string #5690
duckboycool Nov 29, 2025
34d362a
CV64/CVCotM: Add spaces in concatenated strings (#5691)
duckboycool Nov 29, 2025
9a755e6
Jak and Daxter: Add space in concatenated string #5692
duckboycool Nov 29, 2025
df48c3e
KH1: Add space in concatenated string #5693
duckboycool Nov 29, 2025
7631242
MLSS: Add space in concatenated string #5694
duckboycool Nov 29, 2025
c05a2ad
Wargroove: Add space in concatenated string #5696
duckboycool Nov 29, 2025
d089b00
Core: Add spaces in concatenated strings #5697
duckboycool Nov 29, 2025
360a138
Civ6: Fix issue with names including civ-breaking characters (#5204)
hesto2 Nov 29, 2025
d88fe99
DS3: Update/Fix Excluded Locations Logging (#5220)
Exempt-Medic Nov 29, 2025
f1aca0f
CVCotM: Add a client safeguard in case the player doesn't have Dash B…
LiquidCat64 Nov 29, 2025
b81be6b
Jak and Daxter: Second attempt at fixing trade tests. #5599
massimilianodelliubaldini Nov 29, 2025
08ea3fe
ALTTP: Fix setting `Beat Agahnim 1` event twice (#5617)
Mysteryem Nov 29, 2025
8ae1a7d
TUNIC: Fix fuse rule in lower zig #5621
ScipioWright Nov 29, 2025
9f9765b
shapez: Fix logic bug with vanilla shapes and floating layers #5623
BlastSlimey Nov 29, 2025
f7e3f4e
[FFMQ] Bugfix: Fix missing logic rule for Frozen Fields > Aquaria access
wildham0 Nov 29, 2025
18cf1bc
sc2: Item group fixes and new item groups (#5679)
MatthewMarinets Nov 29, 2025
146a314
SC2: Update Infested Banshee description to be more clear when the B…
Ziktofel Nov 29, 2025
886cc68
Timespinner: Exclude Removed Location from Web Tracker (#5701)
TriumphantBass Nov 29, 2025
4e608b1
Docs: fix name of "Build APWorlds" component (#5703)
beauxq Nov 30, 2025
7750657
SNIClient: new `SnesReader` interface (#5155)
beauxq Nov 30, 2025
3fa2745
OptionCreator: pre-RC1 fixes (#5680)
Silvris Nov 30, 2025
e8a63ab
weights: Fixing negatives and zeroes disappearing from option dicts u…
MatthewMarinets Nov 30, 2025
ac84b27
CI: update appimage runtime to fix problems with sleep (#5706)
black-sliver Dec 1, 2025
0905e3c
WebHost/Game Guides: Change links to stay on current instance (#5699)
duckboycool Dec 1, 2025
d25abfc
Docs: update apsudoku docs / add links to web build (#5720)
EmilyV99 Dec 5, 2025
4a0a65d
WebHost: add played game to static tracker (#5731)
Berserker66 Dec 8, 2025
5a6a0b3
sc2: Fixing typos in item descriptions (#5739)
MatthewMarinets Dec 11, 2025
d65fcf2
Launcher: Add workaround for kivy bug for linux touchpad devices (#5737)
BeeFox-sys Dec 12, 2025
ce38d8c
Docs: Add 'silasary' to Mac tutorial contributors (#5745)
benny-dreamly Dec 14, 2025
577b958
SC2: Fix Kerrigan logic for active spells (#5746)
Ziktofel Dec 14, 2025
51d5e1a
Launcher: fix shortcuts on the AppImage (#5726)
Silvris Dec 15, 2025
45994e3
Tests: test that every option in a preset is visible in either simple…
Silvris Dec 16, 2025
e272031
Added logic to allow client connections to refresh the room auto-shut…
ashiryn Dec 18, 2025
35cc8d7
Merging core 6.5 release
ashiryn Dec 19, 2025
85a25fb
Updated and added new worlds.
ashiryn Dec 19, 2025
7260626
Adding FF Tactics
ashiryn Dec 20, 2025
bf2489d
Updated satisfactory world to match community server version
ashiryn Dec 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ env:
# NOTE: since appimage/appimagetool and appimage/type2-runtime does not have tags anymore,
# we check the sha256 and require manual intervention if it was updated.
APPIMAGE_FORK: 'PopTracker'
APPIMAGETOOL_VERSION: 'r-2025-10-19'
APPIMAGETOOL_X86_64_HASH: '9493a6b253a01f84acb9c624c38810ecfa11d99daa829b952b0bff43113080f9'
APPIMAGE_RUNTIME_VERSION: 'r-2025-08-11'
APPIMAGE_RUNTIME_X86_64_HASH: 'e70ffa9b69b211574d0917adc482dd66f25a0083427b5945783965d55b0b0a8b'
APPIMAGETOOL_VERSION: 'r-2025-11-18'
APPIMAGETOOL_X86_64_HASH: '4577a452b30af2337123fbb383aea154b618e51ad5448c3b62085cbbbfbfd9a2'
APPIMAGE_RUNTIME_VERSION: 'r-2025-11-07'
APPIMAGE_RUNTIME_X86_64_HASH: '27ddd3f78e483fc5f7856e413d7c17092917f8c35bfe3318a0d378aa9435ad17'

permissions: # permissions required for attestation
id-token: 'write'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
- "!.github/workflows/**"
- ".github/workflows/docker.yml"
branches:
- "*"
- "main"
tags:
- "v?[0-9]+.[0-9]+.[0-9]*"
workflow_dispatch:
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ env:
# NOTE: since appimage/appimagetool and appimage/type2-runtime does not have tags anymore,
# we check the sha256 and require manual intervention if it was updated.
APPIMAGE_FORK: 'PopTracker'
APPIMAGETOOL_VERSION: 'r-2025-10-19'
APPIMAGETOOL_X86_64_HASH: '9493a6b253a01f84acb9c624c38810ecfa11d99daa829b952b0bff43113080f9'
APPIMAGE_RUNTIME_VERSION: 'r-2025-08-11'
APPIMAGE_RUNTIME_X86_64_HASH: 'e70ffa9b69b211574d0917adc482dd66f25a0083427b5945783965d55b0b0a8b'
APPIMAGETOOL_VERSION: 'r-2025-11-18'
APPIMAGETOOL_X86_64_HASH: '4577a452b30af2337123fbb383aea154b618e51ad5448c3b62085cbbbfbfd9a2'
APPIMAGE_RUNTIME_VERSION: 'r-2025-11-07'
APPIMAGE_RUNTIME_X86_64_HASH: '27ddd3f78e483fc5f7856e413d7c17092917f8c35bfe3318a0d378aa9435ad17'

permissions: # permissions required for attestation
id-token: 'write'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unittests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-subtests pytest-xdist
pip install -r ci-requirements.txt
python ModuleUpdate.py --yes --force --append "WebHostLib/requirements.txt"
python Launcher.py --update_settings # make sure host.yaml exists for tests
- name: Unittests
Expand Down
4 changes: 2 additions & 2 deletions .run/Build APWorld.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<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="SCRIPT_NAME" value="$PROJECT_DIR$/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" />
Expand Down
7 changes: 4 additions & 3 deletions BaseClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1726,9 +1726,10 @@ def create_playthrough(self, create_paths: bool = True) -> None:
logging.debug('The following items could not be reached: %s', ['%s (Player %d) at %s (Player %d)' % (
location.item.name, location.item.player, location.name, location.player) for location in
sphere_candidates])
if any([multiworld.worlds[location.item.player].options.accessibility != 'minimal' for location in sphere_candidates]):
raise RuntimeError(f'Not all progression items reachable ({sphere_candidates}). '
f'Something went terribly wrong here.')
if not multiworld.has_beaten_game(state):
raise RuntimeError("During playthrough generation, the game was determined to be unbeatable. "
"Something went terribly wrong here. "
f"Unreachable progression items: {sphere_candidates}")
else:
self.unreachables = sphere_candidates
break
Expand Down
6 changes: 5 additions & 1 deletion CommonClient.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ def update_game(self, game: str, name_to_id_lookup_table: typing.Dict[str, int])
hint_cost: int | None
"""Current Hint Cost per Hint from the server"""
hint_points: int | None
"""Current avaliable Hint Points from the server"""
"""Current available Hint Points from the server"""
player_names: dict[int, str]
"""Current lookup of slot number to player display name from server (includes aliases)"""

Expand Down Expand Up @@ -572,6 +572,10 @@ def is_uninteresting_item_send(self, print_json_packet: dict) -> bool:
return print_json_packet.get("type", "") == "ItemSend" \
and not self.slot_concerns_self(print_json_packet["receiving"]) \
and not self.slot_concerns_self(print_json_packet["item"].player)

def is_connection_change(self, print_json_packet: dict) -> bool:
"""Helper function for filtering out connection changes."""
return print_json_packet.get("type", "") in ["Join","Part"]

def on_print(self, args: dict):
logger.info(args["text"])
Expand Down
Binary file modified CustomWorldsInstalled.xlsx
Binary file not shown.
20 changes: 17 additions & 3 deletions Generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ def main(args=None) -> tuple[argparse.Namespace, int]:
yaml[category][key] = option
elif category_name not in yaml:
logging.warning(f"Meta: Category {category_name} is not present in {path}.")
elif key == "triggers":
if "triggers" not in yaml[category_name]:
yaml[category_name][key] = []
for trigger in option:
yaml[category_name][key].append(trigger)
else:
yaml[category_name][key] = option

Expand Down Expand Up @@ -342,7 +347,9 @@ def update_weights(weights: dict, new_weights: dict, update_type: str, name: str
elif isinstance(new_value, list):
cleaned_value.extend(new_value)
elif isinstance(new_value, dict):
cleaned_value = dict(Counter(cleaned_value) + Counter(new_value))
counter_value = Counter(cleaned_value)
counter_value.update(new_value)
cleaned_value = dict(counter_value)
else:
raise Exception(f"Cannot apply merge to non-dict, set, or list type {option_name},"
f" received {type(new_value).__name__}.")
Expand All @@ -356,13 +363,18 @@ def update_weights(weights: dict, new_weights: dict, update_type: str, name: str
for element in new_value:
cleaned_value.remove(element)
elif isinstance(new_value, dict):
cleaned_value = dict(Counter(cleaned_value) - Counter(new_value))
counter_value = Counter(cleaned_value)
counter_value.subtract(new_value)
cleaned_value = dict(counter_value)
else:
raise Exception(f"Cannot apply remove to non-dict, set, or list type {option_name},"
f" received {type(new_value).__name__}.")
cleaned_weights[option_name] = cleaned_value
else:
cleaned_weights[option_name] = new_weights[option]
# Options starting with + and - may modify values in-place, and new_weights may be shared by multiple slots
# using the same .yaml, so ensure that the new value is a copy.
cleaned_value = copy.deepcopy(new_weights[option])
cleaned_weights[option_name] = cleaned_value
new_options = set(cleaned_weights) - set(weights)
weights.update(cleaned_weights)
if new_options:
Expand All @@ -385,6 +397,8 @@ def roll_meta_option(option_key, game: str, category_dict: dict) -> Any:
if options[option_key].supports_weighting:
return get_choice(option_key, category_dict)
return category_dict[option_key]
if option_key == "triggers":
return category_dict[option_key]
raise Options.OptionError(f"Error generating meta option {option_key} for {game}.")


Expand Down
20 changes: 15 additions & 5 deletions Launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,17 @@ def open_patch():
launch([*exe, file], component.cli)


def generate_yamls():
def generate_yamls(*args):
from Options import generate_yaml_templates

parser = argparse.ArgumentParser(description="Generate Template Options", usage="[-h] [--skip_open_folder]")
parser.add_argument("--skip_open_folder", action="store_true")
args = parser.parse_args(args)

target = Utils.user_path("Players", "Templates")
generate_yaml_templates(target, False)
open_folder(target)
if not args.skip_open_folder:
open_folder(target)


def browse_files():
Expand Down Expand Up @@ -213,12 +218,17 @@ def launch(exe, in_terminal=False):

def create_shortcut(button: Any, component: Component) -> None:
from pyshortcuts import make_shortcut
script = sys.argv[0]
wkdir = Utils.local_path()
env = os.environ
if "APPIMAGE" in env:
script = env["ARGV0"]
wkdir = None # defaults to ~ on Linux
else:
script = sys.argv[0]
wkdir = Utils.local_path()

script = f"{script} \"{component.display_name}\""
make_shortcut(script, name=f"Archipelago {component.display_name}", icon=local_path("data", "icon.ico"),
startmenu=False, terminal=False, working_dir=wkdir)
startmenu=False, terminal=False, working_dir=wkdir, noexe=Utils.is_frozen())
button.menu.dismiss()


Expand Down
6 changes: 3 additions & 3 deletions Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ def precollect_hint(location: Location, auto_status: HintStatus):
if current_sphere:
spheres.append(dict(current_sphere))

multidata: NetUtils.MultiData | bytes = {
multidata: NetUtils.MultiData = {
"slot_data": slot_data,
"slot_info": slot_info,
"connect_names": {name: (0, player) for player, name in multiworld.player_name.items()},
Expand All @@ -350,11 +350,11 @@ def precollect_hint(location: Location, auto_status: HintStatus):
for key in ("slot_data", "er_hint_data"):
multidata[key] = convert_to_base_types(multidata[key])

multidata = zlib.compress(restricted_dumps(multidata), 9)
serialized_multidata = zlib.compress(restricted_dumps(multidata), 9)

with open(os.path.join(temp_dir, f'{outfilebase}.archipelago'), 'wb') as f:
f.write(bytes([3])) # version of format
f.write(multidata)
f.write(serialized_multidata)

output_file_futures.append(pool.submit(write_multidata))
if not check_accessibility_task.result():
Expand Down
14 changes: 13 additions & 1 deletion MultiServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ def _load(self, decoded_obj: MultiData, game_data_packages: typing.Dict[str, typ
self.read_data["race_mode"] = lambda: decoded_obj.get("race_mode", 0)
mdata_ver = decoded_obj["minimum_versions"]["server"]
if mdata_ver > version_tuple:
raise RuntimeError(f"Supplied Multidata (.archipelago) requires a server of at least version {mdata_ver},"
raise RuntimeError(f"Supplied Multidata (.archipelago) requires a server of at least version {mdata_ver}, "
f"however this server is of version {version_tuple}")
self.generator_version = Version(*decoded_obj["version"])
clients_ver = decoded_obj["minimum_versions"].get("clients", {})
Expand Down Expand Up @@ -906,9 +906,12 @@ def on_client_status_change(self, team: int, slot: int):
if targets:
self.broadcast(targets, [{"cmd": "SetReply", "key": key, "value": self.client_game_state[team, slot]}])

from pony.orm import db_session
class Ashipelago:

webhook_active = False
webhook_queue: queue.SimpleQueue
room_id: int
room_url: str
seed_url: str
admin_password: str
Expand All @@ -928,6 +931,7 @@ def __init__(self, ctx: Context):

# Initializes required fields needed to properly manage the newly started room
def start_room(self, room: Room, is_tracked: int, webhook_settings: dict, admin_password):
self.room_id = room.id
self.room_url = urlsafe_b64encode(room.id.bytes).rstrip(b'=').decode('ascii')
self.seed_url = urlsafe_b64encode(room.seed.id.bytes).rstrip(b'=').decode('ascii')
self.admin_password = admin_password
Expand Down Expand Up @@ -1101,6 +1105,13 @@ def push_player_connection(self):
}
self._push_to_webhook(connection)

# Manually refreshes the room timeout
def refresh_room(self):
with db_session:
room = Room.get(id=self.room_id)
room.multisave = pickle.dumps(self.ctx.get_save())
room.last_activity = datetime.datetime.utcnow()

# Helper function used to enqueue a message to be pushed to Dynxbot
def _push_to_webhook(self, message: typing.Dict[str, typing.Any]):
if not self.webhook_active:
Expand Down Expand Up @@ -1373,6 +1384,7 @@ async def on_client_joined(ctx: Context, client: Client):

# Ashipelago customization
ctx.dynx.push_player_connection()
ctx.dynx.refresh_room()

async def on_client_left(ctx: Context, client: Client):
if len(ctx.clients[client.team][client.slot]) < 1:
Expand Down
50 changes: 41 additions & 9 deletions Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,12 @@ class Range(NumericOption):
range_start = 0
range_end = 1

_RANDOM_OPTS = [
"random", "random-low", "random-middle", "random-high",
"random-range-low-<min>-<max>", "random-range-middle-<min>-<max>",
"random-range-high-<min>-<max>", "random-range-<min>-<max>",
]

def __init__(self, value: int):
if value < self.range_start:
raise Exception(f"{value} is lower than minimum {self.range_start} for option {self.__class__.__name__}")
Expand All @@ -713,9 +719,26 @@ def from_text(cls, text: str) -> Range:
# these are the conditions where "true" and "false" make sense
if text == "true":
return cls.from_any(cls.default)
else: # "false"
return cls(0)
return cls(int(text))
# "false"
return cls(0)

try:
num = int(text)
except ValueError:
# text is not a number
# Handle conditionally acceptable values here rather than in the f-string
default = ""
truefalse = ""
if hasattr(cls, "default"):
default = ", default"
if cls.range_start == 0 and cls.default != 0:
truefalse = ", \"true\", \"false\""
raise Exception(f"Invalid range value {text!r}. Acceptable values are: "
f"<int>{default}, high, low{truefalse}, "
f"{', '.join(cls._RANDOM_OPTS)}.")

return cls(num)


@classmethod
def weighted_range(cls, text) -> Range:
Expand All @@ -731,9 +754,7 @@ def weighted_range(cls, text) -> Range:
return cls(random.randint(cls.range_start, cls.range_end))
else:
raise Exception(f"random text \"{text}\" did not resolve to a recognized pattern. "
f"Acceptable values are: random, random-high, random-middle, random-low, "
f"random-range-low-<min>-<max>, random-range-middle-<min>-<max>, "
f"random-range-high-<min>-<max>, or random-range-<min>-<max>.")
f"Acceptable values are: {', '.join(cls._RANDOM_OPTS)}.")

@classmethod
def custom_range(cls, text) -> Range:
Expand Down Expand Up @@ -1018,6 +1039,8 @@ class PlandoTexts(Option[typing.List[PlandoText]], VerifyKeys):
supports_weighting = False
display_name = "Plando Texts"

visibility = Visibility.template | Visibility.complex_ui | Visibility.spoiler

def __init__(self, value: typing.Iterable[PlandoText]) -> None:
self.value = list(deepcopy(value))
super().__init__()
Expand Down Expand Up @@ -1144,6 +1167,8 @@ class PlandoConnections(Option[typing.List[PlandoConnection]], metaclass=Connect
entrances: typing.ClassVar[typing.AbstractSet[str]]
exits: typing.ClassVar[typing.AbstractSet[str]]

visibility = Visibility.template | Visibility.complex_ui | Visibility.spoiler

duplicate_exits: bool = False
"""Whether or not exits should be allowed to be duplicate."""

Expand Down Expand Up @@ -1435,6 +1460,7 @@ class DeathLink(Toggle):
class ItemLinks(OptionList):
"""Share part of your item pool with other players."""
display_name = "Item Links"
visibility = Visibility.template | Visibility.complex_ui | Visibility.spoiler
rich_text_doc = True
default = []
schema = Schema([
Expand Down Expand Up @@ -1519,6 +1545,7 @@ class PlandoItems(Option[typing.List[PlandoItem]]):
default = ()
supports_weighting = False
display_name = "Plando Items"
visibility = Visibility.template | Visibility.spoiler

def __init__(self, value: typing.Iterable[PlandoItem]) -> None:
self.value = list(deepcopy(value))
Expand Down Expand Up @@ -1726,11 +1753,16 @@ def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], ge

def dictify_range(option: Range):
data = {option.default: 50}
for sub_option in ["random", "random-low", "random-high"]:
for sub_option in ["random", "random-low", "random-high",
f"random-range-{option.range_start}-{option.range_end}"]:
if sub_option != option.default:
data[sub_option] = 0

notes = {}
notes = {
"random-low": "random value weighted towards lower values",
"random-high": "random value weighted towards higher values",
f"random-range-{option.range_start}-{option.range_end}": f"random value between "
f"{option.range_start} and {option.range_end}"
}
for name, number in getattr(option, "special_range_names", {}).items():
notes[name] = f"equivalent to {number}"
if number in data:
Expand Down
Loading
Loading