Compare commits

..

1 Commits

Author SHA1 Message Date
NewSoupVi
a6e5d7f872 Update AutoWorld.py 2025-07-06 20:15:54 +02:00
9 changed files with 44 additions and 76 deletions

View File

@@ -12,7 +12,6 @@ import worlds
from BaseClasses import CollectionState, Item, Location, LocationProgressType, MultiWorld
from Fill import FillError, balance_multiworld_progression, distribute_items_restrictive, flood_items, \
parse_planned_blocks, distribute_planned_blocks, resolve_early_locations_for_planned
from NetUtils import convert_to_base_types
from Options import StartInventoryPool
from Utils import __version__, output_path, version_tuple
from settings import get_settings
@@ -335,9 +334,6 @@ def main(args, seed=None, baked_server_options: dict[str, object] | None = None)
}
AutoWorld.call_all(multiworld, "modify_multidata", multidata)
for key in ("slot_data", "er_hint_data"):
multidata[key] = convert_to_base_types(multidata[key])
multidata = zlib.compress(pickle.dumps(multidata), 9)
with open(os.path.join(temp_dir, f'{outfilebase}.archipelago'), 'wb') as f:

View File

@@ -106,27 +106,6 @@ def _scan_for_TypedTuples(obj: typing.Any) -> typing.Any:
return obj
_base_types = str | int | bool | float | None | tuple["_base_types", ...] | dict["_base_types", "base_types"]
def convert_to_base_types(obj: typing.Any) -> _base_types:
if isinstance(obj, (tuple, list, set, frozenset)):
return tuple(convert_to_base_types(o) for o in obj)
elif isinstance(obj, dict):
return {convert_to_base_types(key): convert_to_base_types(value) for key, value in obj.items()}
elif obj is None or type(obj) in (str, int, float, bool):
return obj
# unwrap simple types to their base, such as StrEnum
elif isinstance(obj, str):
return str(obj)
elif isinstance(obj, int):
return int(obj)
elif isinstance(obj, float):
return float(obj)
else:
raise Exception(f"Cannot handle {type(obj)}")
_encode = JSONEncoder(
ensure_ascii=False,
check_circular=False,

View File

@@ -441,6 +441,9 @@ class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module: str, name: str) -> type:
if module == "builtins" and name in safe_builtins:
return getattr(builtins, name)
# used by OptionCounter
if module == "collections" and name == "Counter":
return collections.Counter
# used by MultiServer -> savegame/multidata
if module == "NetUtils" and name in {"NetworkItem", "ClientStatus", "Hint",
"SlotType", "NetworkSlot", "HintStatus"}:

View File

@@ -1,7 +1,7 @@
import unittest
from Fill import distribute_items_restrictive
from NetUtils import convert_to_base_types
from NetUtils import encode
from worlds.AutoWorld import AutoWorldRegister, call_all
from worlds import failed_world_loads
from . import setup_solo_multiworld
@@ -47,7 +47,7 @@ class TestImplemented(unittest.TestCase):
call_all(multiworld, "post_fill")
for key, data in multiworld.worlds[1].fill_slot_data().items():
self.assertIsInstance(key, str, "keys in slot data must be a string")
convert_to_base_types(data) # only put base data types into slot data
self.assertIsInstance(encode(data), str, f"object {type(data).__name__} not serializable.")
def test_no_failed_world_loads(self):
if failed_world_loads:

View File

@@ -295,7 +295,7 @@ class World(metaclass=AutoWorldRegister):
future. Protocol level compatibility check moved to MultiServer.min_client_version.
"""
required_server_version: Tuple[int, int, int] = (0, 5, 0)
required_server_version: Tuple[int, int, int] = (0, 6, 2)
"""update this if the resulting multidata breaks forward-compatibility of the server"""
hint_blacklist: ClassVar[FrozenSet[str]] = frozenset()

View File

@@ -30,6 +30,7 @@ class Group(enum.Enum):
Deprecated = enum.auto()
@dataclass(frozen=True)
class ItemData:
code_without_offset: offset
@@ -97,15 +98,14 @@ def create_trap_items(world, world_options: Options.DLCQuestOptions, trap_needed
return traps
def create_items(world, world_options: Options.DLCQuestOptions, locations_count: int, excluded_items: list[str],
random: Random):
def create_items(world, world_options: Options.DLCQuestOptions, locations_count: int, excluded_items: list[str], random: Random):
created_items = []
if world_options.campaign == Options.Campaign.option_basic or world_options.campaign == Options.Campaign.option_both:
create_items_campaign(world_options, created_items, world, excluded_items, Group.DLCQuest, 825, 250)
create_items_basic(world_options, created_items, world, excluded_items)
if (world_options.campaign == Options.Campaign.option_live_freemium_or_die or
world_options.campaign == Options.Campaign.option_both):
create_items_campaign(world_options, created_items, world, excluded_items, Group.Freemium, 889, 200)
create_items_lfod(world_options, created_items, world, excluded_items)
trap_items = create_trap_items(world, world_options, locations_count - len(created_items), random)
created_items += trap_items
@@ -113,8 +113,27 @@ def create_items(world, world_options: Options.DLCQuestOptions, locations_count:
return created_items
def create_items_campaign(world_options: Options.DLCQuestOptions, created_items: list[DLCQuestItem], world, excluded_items: list[str], group: Group, total_coins: int, required_coins: int):
for item in items_by_group[group]:
def create_items_lfod(world_options, created_items, world, excluded_items):
for item in items_by_group[Group.Freemium]:
if item.name in excluded_items:
excluded_items.remove(item)
continue
if item.has_any_group(Group.DLC):
created_items.append(world.create_item(item))
if item.has_any_group(Group.Item) and world_options.item_shuffle == Options.ItemShuffle.option_shuffled:
created_items.append(world.create_item(item))
if item.has_any_group(Group.Twice):
created_items.append(world.create_item(item))
if world_options.coinsanity == Options.CoinSanity.option_coin:
if world_options.coinbundlequantity == -1:
create_coin_piece(created_items, world, 889, 200, Group.Freemium)
return
create_coin(world_options, created_items, world, 889, 200, Group.Freemium)
def create_items_basic(world_options, created_items, world, excluded_items):
for item in items_by_group[Group.DLCQuest]:
if item.name in excluded_items:
excluded_items.remove(item.name)
continue
@@ -127,15 +146,14 @@ def create_items_campaign(world_options: Options.DLCQuestOptions, created_items:
created_items.append(world.create_item(item))
if world_options.coinsanity == Options.CoinSanity.option_coin:
if world_options.coinbundlequantity == -1:
create_coin_piece(created_items, world, total_coins, required_coins, group)
create_coin_piece(created_items, world, 825, 250, Group.DLCQuest)
return
create_coin(world_options, created_items, world, total_coins, required_coins, group)
create_coin(world_options, created_items, world, 825, 250, Group.DLCQuest)
def create_coin(world_options, created_items, world, total_coins, required_coins, group):
coin_bundle_required = math.ceil(required_coins / world_options.coinbundlequantity)
coin_bundle_useful = math.ceil(
(total_coins - coin_bundle_required * world_options.coinbundlequantity) / world_options.coinbundlequantity)
coin_bundle_useful = math.ceil((total_coins - coin_bundle_required * world_options.coinbundlequantity) / world_options.coinbundlequantity)
for item in items_by_group[group]:
if item.has_any_group(Group.Coin):
for i in range(coin_bundle_required):
@@ -147,7 +165,7 @@ def create_coin(world_options, created_items, world, total_coins, required_coins
def create_coin_piece(created_items, world, total_coins, required_coins, group):
for item in items_by_group[group]:
if item.has_any_group(Group.Piece):
for i in range(required_coins * 10):
for i in range(required_coins*10):
created_items.append(world.create_item(item))
for i in range((total_coins - required_coins) * 10):
created_items.append(world.create_item(item, ItemClassification.useful))

View File

@@ -321,7 +321,7 @@ class InventorySpillTrapCount(TrapCount):
class FactorioWorldGen(OptionDict):
"""World Generation settings. Overview of options at https://wiki.factorio.com/Map_generator,
with in-depth documentation at https://lua-api.factorio.com/latest/concepts/MapGenSettings.html"""
with in-depth documentation at https://lua-api.factorio.com/latest/Concepts.html#MapGenSettings"""
display_name = "World Generation"
# FIXME: do we want default be a rando-optimized default or in-game DS?
value: dict[str, dict[str, typing.Any]]

View File

@@ -367,7 +367,7 @@ def find_root_directory(ctx: JakAndDaxterContext):
f" Close all launchers, games, clients, and console windows, then restart Archipelago.")
if not os.path.exists(settings_path):
msg = (f"{err_title}: The OpenGOAL settings file does not exist.\n"
msg = (f"{err_title}: the OpenGOAL settings file does not exist.\n"
f"{alt_instructions}")
ctx.on_log_error(logger, msg)
return
@@ -375,44 +375,14 @@ def find_root_directory(ctx: JakAndDaxterContext):
with open(settings_path, "r") as f:
load = json.load(f)
# This settings file has changed format once before, and may do so again in the future.
# Guard against future incompatibilities by checking the file version first, and use that to determine
# what JSON keys to look for next.
try:
settings_version = load["version"]
logger.debug(f"OpenGOAL settings file version: {settings_version}")
except KeyError:
msg = (f"{err_title}: The OpenGOAL settings file has no version number!\n"
f"{alt_instructions}")
ctx.on_log_error(logger, msg)
return
try:
if settings_version == "2.0":
jak1_installed = load["games"]["Jak 1"]["isInstalled"]
mod_sources = load["games"]["Jak 1"]["modsInstalledVersion"]
elif settings_version == "3.0":
jak1_installed = load["games"]["jak1"]["isInstalled"]
mod_sources = load["games"]["jak1"]["mods"]
else:
msg = (f"{err_title}: The OpenGOAL settings file has an unknown version number ({settings_version}).\n"
f"{alt_instructions}")
ctx.on_log_error(logger, msg)
return
except KeyError as e:
msg = (f"{err_title}: The OpenGOAL settings file does not contain key entry {e}!\n"
f"{alt_instructions}")
ctx.on_log_error(logger, msg)
return
jak1_installed = load["games"]["Jak 1"]["isInstalled"]
if not jak1_installed:
msg = (f"{err_title}: The OpenGOAL Launcher is missing a normal install of Jak 1!\n"
f"{alt_instructions}")
ctx.on_log_error(logger, msg)
return
mod_sources = load["games"]["Jak 1"]["modsInstalledVersion"]
if mod_sources is None:
msg = (f"{err_title}: No mod sources have been configured in the OpenGOAL Launcher!\n"
f"{alt_instructions}")

View File

@@ -255,6 +255,7 @@ Try Bumper Stickers!
Try Castlevania 64!
Try Celeste 64!
Try ChecksFinder!
Try Clique!
Try Dark Souls III!
Try DLCQuest!
Try Donkey Kong Country 3!
@@ -267,7 +268,6 @@ Try A Hat in Time!
Try Heretic!
Try Hollow Knight!
Try Hylics 2!
Try Jak and Daxter: The Precursor Legacy!
Try Kingdom Hearts 2!
Try Kirby's Dream Land 3!
Try Landstalker - The Treasures of King Nole!
@@ -288,10 +288,11 @@ Try Pokemon Emerald!
Try Pokemon Red and Blue!
Try Raft!
Try Risk of Rain 2!
Try Rogue Legacy!
Try Secret of Evermore!
Try shapez!
Try Shivers!
Try A Short Hike!
Try Slay the Spire!
Try SMZ3!
Try Sonic Adventure 2 Battle!
Try Starcraft 2!
@@ -299,7 +300,6 @@ Try Stardew Valley!
Try Subnautica!
Try Sudoku!
Try Super Mario 64!
Try Super Mario Land 2: 6 Golden Coins!
Try Super Mario World!
Try Super Metroid!
Try Terraria!
@@ -312,6 +312,7 @@ Try The Witness!
Try Yoshi's Island!
Try Yu-Gi-Oh! 2006!
Try Zillion!
Try Zork Grand Inquisitor!
Try Old School Runescape!
Try Kingdom Hearts!
Try Mega Man 2!
@@ -368,6 +369,7 @@ Have they added Among Us to AP yet?
Every copy of LADX is personalized, David.
Looks like you're going on A Short Hike. Bring back feathers please?
Functioning Brain is at...\nWait. This isn't Witness. Wrong game, sorry.
Don't forget to check your Clique!\nIf, y'know, you have one. No pressure...
:3
Sorry ######, but your progression item is in another world.
&newgames\n&oldgames