Skip to content

Commit

Permalink
Merge branch 'main' into smw-main
Browse files Browse the repository at this point in the history
  • Loading branch information
PoryGone committed Dec 19, 2022
2 parents f65d47b + e0be796 commit a2ffeec
Show file tree
Hide file tree
Showing 40 changed files with 3,371 additions and 101 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*_Spoiler.txt
*.bmbp
*.apbp
*.apl2ac
*.apm3
*.apmc
*.apz5
Expand Down Expand Up @@ -135,6 +136,7 @@ venv/
ENV/
env.bak/
venv.bak/
.code-workspace

# Spyder project settings
.spyderproject
Expand Down
17 changes: 15 additions & 2 deletions MultiServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import itertools
import time
import operator
import hashlib

import ModuleUpdate

Expand Down Expand Up @@ -58,6 +59,12 @@
}


def get_saving_second(seed_name: str, interval: int = 60) -> int:
# save at expected times so other systems using savegame can expect it
# represents the target second of the auto_save_interval at which to save
return int(hashlib.sha256(seed_name.encode()).hexdigest(), 16) % interval


class Client(Endpoint):
version = Version(0, 0, 0)
tags: typing.List[str] = []
Expand Down Expand Up @@ -463,10 +470,16 @@ def init_save(self, enabled: bool = True):
def _start_async_saving(self):
if not self.auto_saver_thread:
def save_regularly():
import time
# time.time() is platform dependent, so using the expensive datetime method instead
def get_datetime_second():
now = datetime.datetime.now()
return now.second + now.microsecond * 0.000001

second = get_saving_second(self.seed_name, self.auto_save_interval)
while not self.exit_event.is_set():
try:
time.sleep(self.auto_save_interval)
next_wakeup = (second - get_datetime_second()) % self.auto_save_interval
time.sleep(max(1.0, next_wakeup))
if self.save_dirty:
logging.debug("Saving via thread.")
self._save()
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Currently, the following games are supported:
* Hylics 2
* Overcooked! 2
* Zillion
* Lufia II Ancient Cave

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: 3 additions & 0 deletions Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,9 @@ def get_default_options() -> OptionsType:
"ffr_options": {
"display_msgs": True,
},
"lufia2ac_options": {
"rom_file": "Lufia II - Rise of the Sinistrals (USA).sfc",
},
}
return options

Expand Down
2 changes: 1 addition & 1 deletion WebHostLib/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def get_html_doc(option_type: type(Options.Option)) -> str:

del file_data

with open(os.path.join(target_folder, 'configs', game_name + ".yaml"), "w") as f:
with open(os.path.join(target_folder, "configs", game_name + ".yaml"), "w", encoding="utf-8") as f:
f.write(res)

# Generate JSON files for player-settings pages
Expand Down
14 changes: 11 additions & 3 deletions WebHostLib/static/assets/tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,18 @@ window.addEventListener('load', () => {
console.info(tables.search());
tables.draw();
});
const tracker = document.getElementById('tracker-wrapper').getAttribute('data-tracker');
const target_second = document.getElementById('tracker-wrapper').getAttribute('data-second') + 3;

function getSleepTimeSeconds(){
// -40 % 60 is -40, which is absolutely wrong and should burn
var sleepSeconds = (((target_second - new Date().getSeconds()) % 60) + 60) % 60;
return sleepSeconds || 60;
}

const update = () => {
const target = $("<div></div>");
const tracker = document.getElementById('tracker-wrapper').getAttribute('data-tracker');
console.log("Updating Tracker...");
target.load("/tracker/" + tracker, function (response, status) {
if (status === "success") {
target.find(".table").each(function (i, new_table) {
Expand All @@ -97,9 +105,9 @@ window.addEventListener('load', () => {
console.log(response);
}
})
setTimeout(update, getSleepTimeSeconds()*1000);
}

setInterval(update, 30000);
setTimeout(update, getSleepTimeSeconds()*1000);

window.addEventListener('resize', () => {
adjustTableHeight();
Expand Down
2 changes: 1 addition & 1 deletion WebHostLib/templates/genericTracker.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

{% block body %}
{% include 'header/dirtHeader.html' %}
<div id="tracker-wrapper" data-tracker="{{ room.tracker|suuid }}/{{ team }}/{{ player }}">
<div id="tracker-wrapper" data-tracker="{{ room.tracker|suuid }}/{{ team }}/{{ player }}" data-second="{{ saving_second }}">
<div id="tracker-header-bar">
<input placeholder="Search" id="search"/>
<span class="info">This tracker will automatically update itself periodically.</span>
Expand Down
2 changes: 1 addition & 1 deletion WebHostLib/templates/macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
No file to download for this game.
{% endif %}
</td>
<td><a href="{{ url_for("getPlayerTracker", tracker=room.tracker, tracked_team=0, tracked_player=patch.player_id) }}">Tracker</a></td>
<td><a href="{{ url_for("get_player_tracker", tracker=room.tracker, tracked_team=0, tracked_player=patch.player_id) }}">Tracker</a></td>
</tr>
{% endfor %}
</tbody>
Expand Down
6 changes: 3 additions & 3 deletions WebHostLib/templates/sc2wolTracker.html
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@
</td>
</tr>
<tr>
<td colspan="2"><img src="{{ icons['Ghost'] }}" class="{{ 'acquired' if 'Medivac' in acquired_items }}" title="Ghost" /></td>
<td colspan="2"><img src="{{ icons['Spectre'] }}" class="{{ 'acquired' if 'Wraith' in acquired_items }}" title="Spectre" /></td>
<td colspan="2"><img src="{{ icons['Thor'] }}" class="{{ 'acquired' if 'Viking' in acquired_items }}" title="Thor" /></td>
<td colspan="2"><img src="{{ icons['Ghost'] }}" class="{{ 'acquired' if 'Ghost' in acquired_items }}" title="Ghost" /></td>
<td colspan="2"><img src="{{ icons['Spectre'] }}" class="{{ 'acquired' if 'Spectre' in acquired_items }}" title="Spectre" /></td>
<td colspan="2"><img src="{{ icons['Thor'] }}" class="{{ 'acquired' if 'Thor' in acquired_items }}" title="Thor" /></td>
</tr>
<tr>
<td><img src="{{ icons['Ocular Implants (Ghost)'] }}" class="{{ 'acquired' if 'Ocular Implants (Ghost)' in acquired_items }}" title="Ocular Implants (Ghost)" /></td>
Expand Down
4 changes: 2 additions & 2 deletions WebHostLib/templates/tracker.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
<tbody>
{%- for player, items in players.items() -%}
<tr>
<td><a href="{{ url_for("getPlayerTracker", tracker=room.tracker,
<td><a href="{{ url_for("get_player_tracker", tracker=room.tracker,
tracked_team=team, tracked_player=player)}}">{{ loop.index }}</a></td>
{%- if (team, loop.index) in video -%}
{%- if video[(team, loop.index)][0] == "Twitch" -%}
Expand Down Expand Up @@ -121,7 +121,7 @@
<tbody>
{%- for player, checks in players.items() -%}
<tr>
<td><a href="{{ url_for("getPlayerTracker", tracker=room.tracker,
<td><a href="{{ url_for("get_player_tracker", tracker=room.tracker,
tracked_team=team, tracked_player=player)}}">{{ loop.index }}</a></td>
<td>{{ player_names[(team, loop.index)]|e }}</td>
{%- for area in ordered_areas -%}
Expand Down
76 changes: 48 additions & 28 deletions WebHostLib/tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from flask import render_template
from werkzeug.exceptions import abort

from MultiServer import Context
from MultiServer import Context, get_saving_second
from NetUtils import SlotType
from Utils import restricted_loads
from worlds import lookup_any_item_id_to_name, lookup_any_location_id_to_name
Expand Down Expand Up @@ -280,16 +280,25 @@ def get_static_room_data(room: Room):
player_location_to_area = {playernumber: get_location_table(multidata["checks_in_area"][playernumber])
for playernumber in range(1, len(names[0]) + 1)
if playernumber not in groups}

saving_second = get_saving_second(multidata["seed_name"])
result = locations, names, use_door_tracker, player_checks_in_area, player_location_to_area, \
multidata["precollected_items"], multidata["games"], multidata["slot_data"], groups
multidata["precollected_items"], multidata["games"], multidata["slot_data"], groups, saving_second
_multidata_cache[room.seed.id] = result
return result


@app.route('/tracker/<suuid:tracker>/<int:tracked_team>/<int:tracked_player>')
@cache.memoize(timeout=60) # multisave is currently created at most every minute
def getPlayerTracker(tracker: UUID, tracked_team: int, tracked_player: int, want_generic: bool = False):
def get_player_tracker(tracker: UUID, tracked_team: int, tracked_player: int, want_generic: bool = False):
key = f"{tracker}_{tracked_team}_{tracked_player}_{want_generic}"
tracker_page = cache.get(key)
if tracker_page:
return tracker_page
timeout, tracker_page = _get_player_tracker(tracker, tracked_team, tracked_player, want_generic)
cache.set(key, tracker_page, timeout)
return tracker_page


def _get_player_tracker(tracker: UUID, tracked_team: int, tracked_player: int, want_generic: bool):
# Team and player must be positive and greater than zero
if tracked_team < 0 or tracked_player < 1:
abort(404)
Expand All @@ -300,7 +309,7 @@ def getPlayerTracker(tracker: UUID, tracked_team: int, tracked_player: int, want

# Collect seed information and pare it down to a single player
locations, names, use_door_tracker, seed_checks_in_area, player_location_to_area, \
precollected_items, games, slot_data, groups = get_static_room_data(room)
precollected_items, games, slot_data, groups, saving_second = get_static_room_data(room)
player_name = names[tracked_team][tracked_player - 1]
location_to_area = player_location_to_area[tracked_player]
inventory = collections.Counter()
Expand Down Expand Up @@ -338,21 +347,24 @@ def getPlayerTracker(tracker: UUID, tracked_team: int, tracked_player: int, want
checks_done["Total"] += 1
specific_tracker = game_specific_trackers.get(games[tracked_player], None)
if specific_tracker and not want_generic:
return specific_tracker(multisave, room, locations, inventory, tracked_team, tracked_player, player_name,
seed_checks_in_area, checks_done, slot_data[tracked_player])
tracker = specific_tracker(multisave, room, locations, inventory, tracked_team, tracked_player, player_name,
seed_checks_in_area, checks_done, slot_data[tracked_player], saving_second)
else:
return __renderGenericTracker(multisave, room, locations, inventory, tracked_team, tracked_player, player_name,
seed_checks_in_area, checks_done)
tracker = __renderGenericTracker(multisave, room, locations, inventory, tracked_team, tracked_player, player_name,
seed_checks_in_area, checks_done, saving_second)

return (saving_second - datetime.datetime.now().second) % 60 or 60, tracker


@app.route('/generic_tracker/<suuid:tracker>/<int:tracked_team>/<int:tracked_player>')
def get_generic_tracker(tracker: UUID, tracked_team: int, tracked_player: int):
return getPlayerTracker(tracker, tracked_team, tracked_player, True)
return get_player_tracker(tracker, tracked_team, tracked_player, True)


def __renderAlttpTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int, int]]],
inventory: Counter, team: int, player: int, player_name: str,
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict) -> str:
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict,
saving_second: int) -> str:

# Note the presence of the triforce item
game_state = multisave.get("client_game_state", {}).get((team, player), 0)
Expand Down Expand Up @@ -414,7 +426,8 @@ def __renderAlttpTracker(multisave: Dict[str, Any], room: Room, locations: Dict[

def __renderMinecraftTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int, int]]],
inventory: Counter, team: int, player: int, playerName: str,
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict) -> str:
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict,
saving_second: int) -> str:

icons = {
"Wooden Pickaxe": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/d/d2/Wooden_Pickaxe_JE3_BE3.png",
Expand Down Expand Up @@ -516,14 +529,15 @@ def __renderMinecraftTracker(multisave: Dict[str, Any], room: Room, locations: D
inventory=inventory, icons=icons,
acquired_items={lookup_any_item_id_to_name[id] for id in inventory if
id in lookup_any_item_id_to_name},
player=player, team=team, room=room, player_name=playerName,
player=player, team=team, room=room, player_name=playerName, saving_second = saving_second,
checks_done=checks_done, checks_in_area=checks_in_area, location_info=location_info,
**display_data)


def __renderOoTTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int, int]]],
inventory: Counter, team: int, player: int, playerName: str,
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict) -> str:
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict,
saving_second: int) -> str:

icons = {
"Fairy Ocarina": "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/9/97/OoT_Fairy_Ocarina_Icon.png",
Expand Down Expand Up @@ -725,7 +739,8 @@ def lookup_and_trim(id, area):

def __renderTimespinnerTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int, int]]],
inventory: Counter, team: int, player: int, playerName: str,
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict[str, Any]) -> str:
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int],
slot_data: Dict[str, Any], saving_second: int) -> str:

icons = {
"Timespinner Wheel": "https://timespinnerwiki.com/mediawiki/images/7/76/Timespinner_Wheel.png",
Expand Down Expand Up @@ -831,7 +846,8 @@ def __renderTimespinnerTracker(multisave: Dict[str, Any], room: Room, locations:

def __renderSuperMetroidTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int, int]]],
inventory: Counter, team: int, player: int, playerName: str,
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict) -> str:
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict,
saving_second: int) -> str:

icons = {
"Energy Tank": "https://randommetroidsolver.pythonanywhere.com/solver/static/images/tracker/inventory/ETank.png",
Expand Down Expand Up @@ -930,8 +946,9 @@ def __renderSuperMetroidTracker(multisave: Dict[str, Any], room: Room, locations
**display_data)

def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int, int]]],
inventory: Counter, team: int, player: int, playerName: str,
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int], slot_data: Dict) -> str:
inventory: Counter, team: int, player: int, playerName: str,
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int],
slot_data: Dict, saving_second: int) -> str:

SC2WOL_LOC_ID_OFFSET = 1000
SC2WOL_ITEM_ID_OFFSET = 1000
Expand Down Expand Up @@ -1173,37 +1190,40 @@ def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict
checks_done=checks_done, checks_in_area=checks_in_area, location_info=location_info,
**display_data)


def __renderGenericTracker(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int, int]]],
inventory: Counter, team: int, player: int, playerName: str,
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int]) -> str:
seed_checks_in_area: Dict[int, Dict[str, int]], checks_done: Dict[str, int],
saving_second: int) -> str:

checked_locations = multisave.get("location_checks", {}).get((team, player), set())
player_received_items = {}
if multisave.get('version', 0) > 0:
# add numbering to all items but starter_inventory
ordered_items = multisave.get('received_items', {}).get((team, player, True), [])
else:
ordered_items = multisave.get('received_items', {}).get((team, player), [])

# add numbering to all items but starter_inventory
for order_index, networkItem in enumerate(ordered_items, start=1):
player_received_items[networkItem.item] = order_index

return render_template("genericTracker.html",
inventory=inventory,
player=player, team=team, room=room, player_name=playerName,
checked_locations=checked_locations,
not_checked_locations=set(locations[player]) - checked_locations,
received_items=player_received_items)
inventory=inventory,
player=player, team=team, room=room, player_name=playerName,
checked_locations=checked_locations,
not_checked_locations=set(locations[player]) - checked_locations,
received_items=player_received_items,
saving_second=saving_second)


@app.route('/tracker/<suuid:tracker>')
@cache.memoize(timeout=60) # multisave is currently created at most every minute
@cache.memoize(timeout=1) # multisave is currently created at most every minute
def getTracker(tracker: UUID):
room: Room = Room.get(tracker=tracker)
if not room:
abort(404)
locations, names, use_door_tracker, seed_checks_in_area, player_location_to_area, \
precollected_items, games, slot_data, groups = get_static_room_data(room)
precollected_items, games, slot_data, groups, saving_second = get_static_room_data(room)

inventory = {teamnumber: {playernumber: collections.Counter() for playernumber in range(1, len(team) + 1) if playernumber not in groups}
for teamnumber, team in enumerate(names)}
Expand Down
3 changes: 3 additions & 0 deletions host.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ sni_options:
lttp_options:
# File name of the v1.0 J rom
rom_file: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"
lufia2ac_options:
# File name of the US rom
rom_file: "Lufia II - Rise of the Sinistrals (USA).sfc"
sm_options:
# File name of the v1.0 J rom
rom_file: "Super Metroid (JU).sfc"
Expand Down
Loading

0 comments on commit a2ffeec

Please sign in to comment.