Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into tunic/grass-rando
Browse files Browse the repository at this point in the history
  • Loading branch information
silent-destroyer committed Dec 15, 2024
2 parents b5f05aa + 728d249 commit 56be193
Show file tree
Hide file tree
Showing 81 changed files with 7,935 additions and 350 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ Currently, the following games are supported:
* Yacht Dice
* Faxanadu
* Saving Princess
* Castlevania: Circle of the Moon

For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled
Expand Down
3 changes: 2 additions & 1 deletion Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,8 @@ def handle_exception(exc_type, exc_value, exc_traceback):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logging.getLogger(exception_logger).exception("Uncaught exception",
exc_info=(exc_type, exc_value, exc_traceback))
exc_info=(exc_type, exc_value, exc_traceback),
extra={"NoStream": exception_logger is None})
return orig_hook(exc_type, exc_value, exc_traceback)

handle_exception._wrapped = True
Expand Down
8 changes: 4 additions & 4 deletions WebHostLib/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ def get_meta(options_source: dict, race: bool = False) -> Dict[str, Union[List[s

server_options = {
"hint_cost": int(options_source.get("hint_cost", ServerOptions.hint_cost)),
"release_mode": options_source.get("release_mode", ServerOptions.release_mode),
"remaining_mode": options_source.get("remaining_mode", ServerOptions.remaining_mode),
"collect_mode": options_source.get("collect_mode", ServerOptions.collect_mode),
"release_mode": str(options_source.get("release_mode", ServerOptions.release_mode)),
"remaining_mode": str(options_source.get("remaining_mode", ServerOptions.remaining_mode)),
"collect_mode": str(options_source.get("collect_mode", ServerOptions.collect_mode)),
"item_cheat": bool(int(options_source.get("item_cheat", not ServerOptions.disable_item_cheat))),
"server_password": options_source.get("server_password", None),
"server_password": str(options_source.get("server_password", None)),
}
generator_options = {
"spoiler": int(options_source.get("spoiler", GeneratorOptions.spoiler)),
Expand Down
3 changes: 3 additions & 0 deletions docs/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
# Castlevania 64
/worlds/cv64/ @LiquidCat64

# Castlevania: Circle of the Moon
/worlds/cvcotm/ @LiquidCat64

# Celeste 64
/worlds/celeste64/ @PoryGone

Expand Down
23 changes: 23 additions & 0 deletions docs/apworld_dev_faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,26 @@ A faster alternative to the `for` loop would be to use a [list comprehension](ht
```py
item_pool += [self.create_filler() for _ in range(total_locations - len(item_pool))]
```

---

### I learned about indirect conditions in the world API document, but I want to know more. What are they and why are they necessary?

The world API document mentions how to use `multiworld.register_indirect_condition` to register indirect conditions and **when** you should use them, but not *how* they work and *why* they are necessary. This is because the explanation is quite complicated.

Region sweep (the algorithm that determines which regions are reachable) is a Breadth-First Search of the region graph. It starts from the origin region, checks entrances one by one, and adds newly reached regions and their entrances to the queue until there is nothing more to check.

For performance reasons, AP only checks every entrance once. However, if an entrance's access_rule depends on region access, then the following may happen:
1. The entrance is checked and determined to be nontraversable because the region in its access_rule hasn't been reached yet during the graph search.
2. Then, the region in its access_rule is determined to be reachable.

This entrance *would* be in logic if it were rechecked, but it won't be rechecked this cycle.
To account for this case, AP would have to recheck all entrances every time a new region is reached until no new regions are reached.

An indirect condition is how you can manually define that a specific entrance needs to be rechecked during region sweep if a specific region is reached during it.
This keeps most of the performance upsides. Even in a game making heavy use of indirect conditions (ex: The Witness), using them is significantly faster than just "rechecking each entrance until nothing new is found".
The reason entrance access rules using `location.can_reach` and `entrance.can_reach` are also affected is because they call `region.can_reach` on their respective parent/source region.

We recognize it can feel like a trap since it will not alert you when you are missing an indirect condition, and that some games have very complex access rules.
As of [PR #3682 (Core: Region handling customization)](https://github.com/ArchipelagoMW/Archipelago/pull/3682) being merged, it is possible for a world to opt out of indirect conditions entirely, instead using the system of checking each entrance whenever a region has been reached, although this does come with a performance cost.
Opting out of using indirect conditions should only be used by games that *really* need it. For most games, it should be reasonable to know all entrance → region dependencies, making indirect conditions preferred because they are much faster.
6 changes: 3 additions & 3 deletions docs/running from source.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ Recommended steps
[Discord in #ap-core-dev](https://discord.com/channels/731205301247803413/731214280439103580/905154456377757808)

* It is recommended to use [PyCharm IDE](https://www.jetbrains.com/pycharm/)
* Run Generate.py which will prompt installation of missing modules, press enter to confirm
* In PyCharm: right-click Generate.py and select `Run 'Generate'`
* Without PyCharm: open a command prompt in the source folder and type `py Generate.py`
* Run ModuleUpdate.py which will prompt installation of missing modules, press enter to confirm
* In PyCharm: right-click ModuleUpdate.py and select `Run 'ModuleUpdate'`
* Without PyCharm: open a command prompt in the source folder and type `py ModuleUpdate.py`


## macOS
Expand Down
5 changes: 5 additions & 0 deletions inno_setup.iss
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ Root: HKCR; Subkey: "{#MyAppName}cv64patch"; ValueData: "Arc
Root: HKCR; Subkey: "{#MyAppName}cv64patch\DefaultIcon"; ValueData: "{app}\ArchipelagoBizHawkClient.exe,0"; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}cv64patch\shell\open\command"; ValueData: """{app}\ArchipelagoBizHawkClient.exe"" ""%1"""; ValueType: string; ValueName: "";

Root: HKCR; Subkey: ".apcvcotm"; ValueData: "{#MyAppName}cvcotmpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}cvcotmpatch"; ValueData: "Archipelago Castlevania Circle of the Moon Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}cvcotmpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoBizHawkClient.exe,0"; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}cvcotmpatch\shell\open\command"; ValueData: """{app}\ArchipelagoBizHawkClient.exe"" ""%1"""; ValueType: string; ValueName: "";

Root: HKCR; Subkey: ".apmm2"; ValueData: "{#MyAppName}mm2patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mm2patch"; ValueData: "Archipelago Mega Man 2 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mm2patch\DefaultIcon"; ValueData: "{app}\ArchipelagoBizHawkClient.exe,0"; ValueType: string; ValueName: "";
Expand Down
14 changes: 13 additions & 1 deletion worlds/AutoWorld.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import time
from random import Random
from dataclasses import make_dataclass
from typing import (Any, Callable, ClassVar, Dict, FrozenSet, List, Mapping, Optional, Set, TextIO, Tuple,
from typing import (Any, Callable, ClassVar, Dict, FrozenSet, Iterable, List, Mapping, Optional, Set, TextIO, Tuple,
TYPE_CHECKING, Type, Union)

from Options import item_and_loc_options, ItemsAccessibility, OptionGroup, PerGameCommonOptions
Expand Down Expand Up @@ -534,12 +534,24 @@ def create_filler(self) -> "Item":
def get_location(self, location_name: str) -> "Location":
return self.multiworld.get_location(location_name, self.player)

def get_locations(self) -> "Iterable[Location]":
return self.multiworld.get_locations(self.player)

def get_entrance(self, entrance_name: str) -> "Entrance":
return self.multiworld.get_entrance(entrance_name, self.player)

def get_entrances(self) -> "Iterable[Entrance]":
return self.multiworld.get_entrances(self.player)

def get_region(self, region_name: str) -> "Region":
return self.multiworld.get_region(region_name, self.player)

def get_regions(self) -> "Iterable[Region]":
return self.multiworld.get_regions(self.player)

def push_precollected(self, item: Item) -> None:
self.multiworld.push_precollected(item)

@property
def player_name(self) -> str:
return self.multiworld.get_player_name(self.player)
Expand Down
Loading

0 comments on commit 56be193

Please sign in to comment.