Merge branch 'main' into player-tracker

This commit is contained in:
Chris Wilson
2022-08-17 21:48:40 -04:00
14 changed files with 60 additions and 38 deletions

View File

@@ -493,7 +493,8 @@ async def server_loop(ctx: CommonContext, address=None):
logger.info(f'Connecting to Archipelago server at {address}')
try:
socket = await websockets.connect(address, port=port, ping_timeout=None, ping_interval=None)
ctx.ui.update_address_bar(server_url.netloc)
if ctx.ui is not None:
ctx.ui.update_address_bar(server_url.netloc)
ctx.server = Endpoint(socket)
logger.info('Connected')
ctx.server_address = address

View File

@@ -422,6 +422,10 @@ def get_text_between(text: str, start: str, end: str) -> str:
return text[text.index(start) + len(start): text.rindex(end)]
def get_text_after(text: str, start: str) -> str:
return text[text.index(start) + len(start):]
loglevel_mapping = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}

View File

@@ -42,12 +42,11 @@ def get_app():
def create_ordered_tutorials_file() -> typing.List[typing.Dict[str, typing.Any]]:
import json
import shutil
import pathlib
import zipfile
zfile: zipfile.ZipInfo
from worlds.AutoWorld import AutoWorldRegister, __file__
from worlds.AutoWorld import AutoWorldRegister
worlds = {}
data = []
for game, world in AutoWorldRegister.world_types.items():
@@ -60,8 +59,8 @@ def create_ordered_tutorials_file() -> typing.List[typing.Dict[str, typing.Any]]
target_path = os.path.join(base_target_path, game)
os.makedirs(target_path, exist_ok=True)
if world.is_zip:
zipfile_path = pathlib.Path(world.__file__).parents[1]
if world.zip_path:
zipfile_path = world.zip_path
assert os.path.isfile(zipfile_path), f"{zipfile_path} is not a valid file(path)."
assert zipfile.is_zipfile(zipfile_path), f"{zipfile_path} is not a valid zipfile."

View File

@@ -768,10 +768,10 @@ def getTracker(tracker: UUID):
if game_state == 30:
inventory[team][player][106] = 1 # Triforce
player_big_key_locations = {playernumber: set() for playernumber in range(1, len(names[0]) + 1) if playernumber not in groups}
player_small_key_locations = {playernumber: set() for playernumber in range(1, len(names[0]) + 1) if playernumber not in groups}
player_big_key_locations = {playernumber: set() for playernumber in range(1, len(names[0]) + 1)}
player_small_key_locations = {playernumber: set() for playernumber in range(1, len(names[0]) + 1)}
for loc_data in locations.values():
for values in loc_data.values():
for values in loc_data.values():
item_id, item_player, flags = values
if item_id in ids_big_key:

View File

@@ -2,6 +2,7 @@ from __future__ import annotations
import logging
import sys
import pathlib
from typing import Dict, FrozenSet, Set, Tuple, List, Optional, TextIO, Any, Callable, Union, TYPE_CHECKING
from Options import Option
@@ -48,13 +49,14 @@ class AutoWorldRegister(type):
raise RuntimeError(f"""Game {dct["game"]} already registered.""")
AutoWorldRegister.world_types[dct["game"]] = new_class
new_class.__file__ = sys.modules[new_class.__module__].__file__
new_class.is_zip = ".apworld" in new_class.__file__
if ".apworld" in new_class.__file__:
new_class.zip_path = pathlib.Path(new_class.__file__).parents[1]
return new_class
class AutoLogicRegister(type):
def __new__(cls, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> AutoLogicRegister:
new_class = super().__new__(cls, name, bases, dct)
def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> AutoLogicRegister:
new_class = super().__new__(mcs, name, bases, dct)
function: Callable[..., Any]
for item_name, function in dct.items():
if item_name == "copy_mixin":
@@ -189,7 +191,7 @@ class World(metaclass=AutoWorldRegister):
item_names: Set[str] # set of all potential item names
location_names: Set[str] # set of all potential location names
is_zip: bool # was loaded from a .apworld ?
zip_path: Optional[pathlib.Path] = None # If loaded from a .apworld, this is the Path to it.
__file__: str # path it was loaded from
def __init__(self, world: "MultiWorld", player: int):

View File

@@ -37,7 +37,6 @@ for file in os.scandir(folder):
world_sources.sort()
for world_source in world_sources:
if world_source.is_zip:
importer = zipimport.zipimporter(os.path.join(folder, world_source.path))
importer.load_module(world_source.path.split(".", 1)[0])
else:

View File

@@ -1,26 +1,23 @@
import random
import logging
import os
import random
import threading
import typing
from BaseClasses import Item, CollectionState, Tutorial
from .SubClasses import ALttPItem
from ..AutoWorld import World, WebWorld, LogicMixin
from .Options import alttp_options, smallkey_shuffle
from .Items import item_init_table, item_name_groups, item_table, GetBeemizerItem
from .Regions import lookup_name_to_id, create_regions, mark_light_world_regions
from .Rules import set_rules
from .ItemPool import generate_itempool, difficulties
from .Shops import create_shops, ShopSlotFill
from .Dungeons import create_dungeons
from .EntranceShuffle import link_entrances, link_inverted_entrances, plando_connect
from .InvertedRegions import create_inverted_regions, mark_dark_world_regions
from .ItemPool import generate_itempool, difficulties
from .Items import item_init_table, item_name_groups, item_table, GetBeemizerItem
from .Options import alttp_options, smallkey_shuffle
from .Regions import lookup_name_to_id, create_regions, mark_light_world_regions
from .Rom import LocalRom, patch_rom, patch_race_rom, check_enemizer, patch_enemizer, apply_rom_settings, \
get_hash_string, get_base_rom_path, LttPDeltaPatch
import Patch
from itertools import chain
from .InvertedRegions import create_inverted_regions, mark_dark_world_regions
from .EntranceShuffle import link_entrances, link_inverted_entrances, plando_connect
from .Rules import set_rules
from .Shops import create_shops, ShopSlotFill
from .SubClasses import ALttPItem
from ..AutoWorld import World, WebWorld, LogicMixin
lttp_logger = logging.getLogger("A Link to the Past")

View File

@@ -78,9 +78,14 @@ def generate_mod(world, output_directory: str):
global data_final_template, locale_template, control_template, data_template, settings_template
with template_load_lock:
if not data_final_template:
mod_template_folder = os.path.join(os.path.dirname(__file__), "data", "mod_template")
def load_template(name: str):
import pkgutil
data = pkgutil.get_data(__name__, "data/mod_template/" + name).decode()
return data, name, lambda: False
template_env: Optional[jinja2.Environment] = \
jinja2.Environment(loader=jinja2.FileSystemLoader([mod_template_folder]))
jinja2.Environment(loader=jinja2.FunctionLoader(load_template))
data_template = template_env.get_template("data.lua")
data_final_template = template_env.get_template("data-final-fixes.lua")
locale_template = template_env.get_template(r"locale/en/locale.cfg")
@@ -158,7 +163,21 @@ def generate_mod(world, output_directory: str):
mod_dir = os.path.join(output_directory, mod_name + "_" + Utils.__version__)
en_locale_dir = os.path.join(mod_dir, "locale", "en")
os.makedirs(en_locale_dir, exist_ok=True)
shutil.copytree(os.path.join(os.path.dirname(__file__), "data", "mod"), mod_dir, dirs_exist_ok=True)
if world.zip_path:
# Maybe investigate read from zip, write to zip, without temp file?
with zipfile.ZipFile(world.zip_path) as zf:
for file in zf.infolist():
if not file.is_dir() and "/data/mod/" in file.filename:
path_part = Utils.get_text_after(file.filename, "/data/mod/")
target = os.path.join(mod_dir, path_part)
os.makedirs(os.path.split(target)[0], exist_ok=True)
with open(target, "wb") as f:
f.write(zf.read(file))
else:
shutil.copytree(os.path.join(os.path.dirname(__file__), "data", "mod"), mod_dir, dirs_exist_ok=True)
with open(os.path.join(mod_dir, "data.lua"), "wt") as f:
f.write(data_template_code)
with open(os.path.join(mod_dir, "data-final-fixes.lua"), "wt") as f:

View File

@@ -1,7 +1,7 @@
from typing import Dict, List, Set
from collections import deque
from worlds.factorio.Options import TechTreeLayout
from .Options import TechTreeLayout
funnel_layers = {TechTreeLayout.option_small_funnels: 3,
TechTreeLayout.option_medium_funnels: 4,

View File

@@ -19,8 +19,8 @@ pool = ThreadPoolExecutor(1)
def load_json_data(data_name: str) -> Union[List[str], Dict[str, Any]]:
with open(os.path.join(source_folder, f"{data_name}.json")) as f:
return json.load(f)
import pkgutil
return json.loads(pkgutil.get_data(__name__, "data/" + data_name + ".json").decode())
techs_future = pool.submit(load_json_data, "techs")

View File

@@ -1,7 +1,7 @@
import collections
import typing
from ..AutoWorld import World, WebWorld
from worlds.AutoWorld import World, WebWorld
from BaseClasses import Region, Entrance, Location, Item, RegionType, Tutorial, ItemClassification
from .Technologies import base_tech_table, recipe_sources, base_technology_table, \

View File

@@ -33,12 +33,12 @@ leave this window open as this is your server console.
### Connect to the MultiServer
Using Minecraft 1.18.2 connect to the server `localhost`.
Open Minecraft, go to `Multiplayer > Direct Connection`, and join the `localhost` server address.
If you are using the website to host the game then it should auto-connect to the AP server without the need to `/connect`
otherwise once you are in game type `/connect <AP-Address> (Port) (Password)` where `<AP-Address>` is the address of the
Archipelago server. `(Port)` is only required if the Archipelago server is not using the default port of 38281.
Archipelago server. `(Port)` is only required if the Archipelago server is not using the default port of 38281. Note that there is no colon between `<AP-Address>` and `(Port)`.
`(Password)` is only required if the Archipelago server you are using has a password set.
### Play the game

View File

@@ -1,6 +1,7 @@
import random
from BaseClasses import LocationProgressType
from .Items import OOTItem
# Abbreviations
# DMC Death Mountain Crater
@@ -1260,7 +1261,7 @@ def hintExclusions(world, clear_cache=False):
world.hint_exclusions = []
for location in world.get_locations():
if (location.locked and (location.item.type != 'Song' or world.shuffle_song_items != 'song')) or location.progress_type == LocationProgressType.EXCLUDED:
if (location.locked and ((isinstance(location.item, OOTItem) and location.item.type != 'Song') or world.shuffle_song_items != 'song')) or location.progress_type == LocationProgressType.EXCLUDED:
world.hint_exclusions.append(location.name)
world_location_names = [

View File

@@ -2104,7 +2104,7 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F
shop_objs = { 0x0148 } # "Sold Out" object
for location in locations:
if location.item.type == 'Shop':
if isinstance(location.item, OOTItem) and location.item.type == 'Shop':
shop_objs.add(location.item.special['object'])
rom.write_int16(location.address1, location.item.index)
else: