Skip to content

Commit c593a96

Browse files
author
Zach Parks
authored
WebHost: Fix 500 Server errors relating to player/multi trackers. (#2664)
* WebHost: Fix player tracker issue with items missing from data package. Reported in https://discord.com/channels/731205301247803413/1192202112172576819 * WebHost: Fix multi-tracker error when item links are present. Reported in https://discord.com/channels/731205301247803413/1192104719959724062 * Use Utils.KeyedDefaultDict instead of checking for key * formatted revert * import tweak
1 parent 7406a1e commit c593a96

File tree

1 file changed

+52
-30
lines changed

1 file changed

+52
-30
lines changed

WebHostLib/tracker.py

+52-30
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import datetime
2+
import collections
23
from dataclasses import dataclass
34
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
45
from uuid import UUID
@@ -8,7 +9,7 @@
89

910
from MultiServer import Context, get_saving_second
1011
from NetUtils import ClientStatus, Hint, NetworkItem, NetworkSlot, SlotType
11-
from Utils import restricted_loads
12+
from Utils import restricted_loads, KeyedDefaultDict
1213
from . import app, cache
1314
from .models import GameDataPackage, Room
1415

@@ -62,12 +63,18 @@ def __init__(self, room: Room):
6263
self.location_name_to_id: Dict[str, Dict[str, int]] = {}
6364

6465
# Generate inverse lookup tables from data package, useful for trackers.
65-
self.item_id_to_name: Dict[str, Dict[int, str]] = {}
66-
self.location_id_to_name: Dict[str, Dict[int, str]] = {}
66+
self.item_id_to_name: Dict[str, Dict[int, str]] = KeyedDefaultDict(lambda game_name: {
67+
game_name: KeyedDefaultDict(lambda code: f"Unknown Game {game_name} - Item (ID: {code})")
68+
})
69+
self.location_id_to_name: Dict[str, Dict[int, str]] = KeyedDefaultDict(lambda game_name: {
70+
game_name: KeyedDefaultDict(lambda code: f"Unknown Game {game_name} - Location (ID: {code})")
71+
})
6772
for game, game_package in self._multidata["datapackage"].items():
6873
game_package = restricted_loads(GameDataPackage.get(checksum=game_package["checksum"]).data)
69-
self.item_id_to_name[game] = {id: name for name, id in game_package["item_name_to_id"].items()}
70-
self.location_id_to_name[game] = {id: name for name, id in game_package["location_name_to_id"].items()}
74+
self.item_id_to_name[game] = KeyedDefaultDict(lambda code: f"Unknown Item (ID: {code})", {
75+
id: name for name, id in game_package["item_name_to_id"].items()})
76+
self.location_id_to_name[game] = KeyedDefaultDict(lambda code: f"Unknown Location (ID: {code})", {
77+
id: name for name, id in game_package["location_name_to_id"].items()})
7178

7279
# Normal lookup tables as well.
7380
self.item_name_to_id[game] = game_package["item_name_to_id"]
@@ -115,10 +122,10 @@ def get_player_received_items(self, team: int, player: int) -> List[NetworkItem]
115122
return self._multisave.get("received_items", {}).get((team, player, True), [])
116123

117124
@_cache_results
118-
def get_player_inventory_counts(self, team: int, player: int) -> Dict[int, int]:
125+
def get_player_inventory_counts(self, team: int, player: int) -> collections.Counter:
119126
"""Retrieves a dictionary of all items received by their id and their received count."""
120127
items = self.get_player_received_items(team, player)
121-
inventory = {item: 0 for item in self.item_id_to_name[self.get_player_game(team, player)]}
128+
inventory = collections.Counter()
122129
for item in items:
123130
inventory[item.item] += 1
124131

@@ -149,16 +156,15 @@ def get_team_completed_worlds_count(self) -> Dict[int, int]:
149156
"""Retrieves a dictionary of number of completed worlds per team."""
150157
return {
151158
team: sum(
152-
self.get_player_client_status(team, player) == ClientStatus.CLIENT_GOAL
153-
for player in players if self.get_slot_info(team, player).type == SlotType.player
154-
) for team, players in self.get_team_players().items()
159+
self.get_player_client_status(team, player) == ClientStatus.CLIENT_GOAL for player in players
160+
) for team, players in self.get_all_players().items()
155161
}
156162

157163
@_cache_results
158164
def get_team_hints(self) -> Dict[int, Set[Hint]]:
159165
"""Retrieves a dictionary of all hints per team."""
160166
hints = {}
161-
for team, players in self.get_team_players().items():
167+
for team, players in self.get_all_slots().items():
162168
hints[team] = set()
163169
for player in players:
164170
hints[team] |= self.get_player_hints(team, player)
@@ -170,24 +176,38 @@ def get_team_locations_total_count(self) -> Dict[int, int]:
170176
"""Retrieves a dictionary of total player locations each team has."""
171177
return {
172178
team: sum(len(self.get_player_locations(team, player)) for player in players)
173-
for team, players in self.get_team_players().items()
179+
for team, players in self.get_all_players().items()
174180
}
175181

176182
@_cache_results
177183
def get_team_locations_checked_count(self) -> Dict[int, int]:
178184
"""Retrieves a dictionary of checked player locations each team has."""
179185
return {
180186
team: sum(len(self.get_player_checked_locations(team, player)) for player in players)
181-
for team, players in self.get_team_players().items()
187+
for team, players in self.get_all_players().items()
182188
}
183189

184190
# TODO: Change this method to properly build for each team once teams are properly implemented, as they don't
185191
# currently exist in multidata to easily look up, so these are all assuming only 1 team: Team #0
186192
@_cache_results
187-
def get_team_players(self) -> Dict[int, List[int]]:
193+
def get_all_slots(self) -> Dict[int, List[int]]:
188194
"""Retrieves a dictionary of all players ids on each team."""
189195
return {
190-
0: [player for player, slot_info in self._multidata["slot_info"].items()]
196+
0: [
197+
player for player, slot_info in self._multidata["slot_info"].items()
198+
]
199+
}
200+
201+
# TODO: Change this method to properly build for each team once teams are properly implemented, as they don't
202+
# currently exist in multidata to easily look up, so these are all assuming only 1 team: Team #0
203+
@_cache_results
204+
def get_all_players(self) -> Dict[int, List[int]]:
205+
"""Retrieves a dictionary of all player slot-type players ids on each team."""
206+
return {
207+
0: [
208+
player for player, slot_info in self._multidata["slot_info"].items()
209+
if self.get_slot_info(0, player).type == SlotType.player
210+
]
191211
}
192212

193213
@_cache_results
@@ -203,38 +223,38 @@ def get_room_locations(self) -> Dict[TeamPlayer, Dict[int, ItemMetadata]]:
203223
"""Retrieves a dictionary of all locations and their associated item metadata per player."""
204224
return {
205225
(team, player): self.get_player_locations(team, player)
206-
for team, players in self.get_team_players().items() for player in players
226+
for team, players in self.get_all_players().items() for player in players
207227
}
208228

209229
@_cache_results
210230
def get_room_games(self) -> Dict[TeamPlayer, str]:
211231
"""Retrieves a dictionary of games for each player."""
212232
return {
213233
(team, player): self.get_player_game(team, player)
214-
for team, players in self.get_team_players().items() for player in players
234+
for team, players in self.get_all_slots().items() for player in players
215235
}
216236

217237
@_cache_results
218238
def get_room_locations_complete(self) -> Dict[TeamPlayer, int]:
219239
"""Retrieves a dictionary of all locations complete per player."""
220240
return {
221241
(team, player): len(self.get_player_checked_locations(team, player))
222-
for team, players in self.get_team_players().items() for player in players
242+
for team, players in self.get_all_players().items() for player in players
223243
}
224244

225245
@_cache_results
226246
def get_room_client_statuses(self) -> Dict[TeamPlayer, ClientStatus]:
227247
"""Retrieves a dictionary of all ClientStatus values per player."""
228248
return {
229249
(team, player): self.get_player_client_status(team, player)
230-
for team, players in self.get_team_players().items() for player in players
250+
for team, players in self.get_all_players().items() for player in players
231251
}
232252

233253
@_cache_results
234254
def get_room_long_player_names(self) -> Dict[TeamPlayer, str]:
235255
"""Retrieves a dictionary of names with aliases for each player."""
236256
long_player_names = {}
237-
for team, players in self.get_team_players().items():
257+
for team, players in self.get_all_slots().items():
238258
for player in players:
239259
alias = self.get_player_alias(team, player)
240260
if alias:
@@ -370,7 +390,8 @@ def render_generic_multiworld_tracker(tracker_data: TrackerData, enabled_tracker
370390
enabled_trackers=enabled_trackers,
371391
current_tracker="Generic",
372392
room=tracker_data.room,
373-
room_players=tracker_data.get_team_players(),
393+
all_slots=tracker_data.get_all_slots(),
394+
room_players=tracker_data.get_all_players(),
374395
locations=tracker_data.get_room_locations(),
375396
locations_complete=tracker_data.get_room_locations_complete(),
376397
total_team_locations=tracker_data.get_team_locations_total_count(),
@@ -389,7 +410,6 @@ def render_generic_multiworld_tracker(tracker_data: TrackerData, enabled_tracker
389410

390411
# TODO: This is a temporary solution until a proper Tracker API can be implemented for tracker templates and data to
391412
# live in their respective world folders.
392-
import collections
393413

394414
from worlds import network_data_package
395415

@@ -400,7 +420,7 @@ def render_Factorio_multiworld_tracker(tracker_data: TrackerData, enabled_tracke
400420
(team, player): {
401421
tracker_data.item_id_to_name["Factorio"][item_id]: count
402422
for item_id, count in tracker_data.get_player_inventory_counts(team, player).items()
403-
} for team, players in tracker_data.get_team_players().items() for player in players
423+
} for team, players in tracker_data.get_all_slots().items() for player in players
404424
if tracker_data.get_player_game(team, player) == "Factorio"
405425
}
406426

@@ -409,7 +429,8 @@ def render_Factorio_multiworld_tracker(tracker_data: TrackerData, enabled_tracke
409429
enabled_trackers=enabled_trackers,
410430
current_tracker="Factorio",
411431
room=tracker_data.room,
412-
room_players=tracker_data.get_team_players(),
432+
all_slots=tracker_data.get_all_slots(),
433+
room_players=tracker_data.get_all_players(),
413434
locations=tracker_data.get_room_locations(),
414435
locations_complete=tracker_data.get_room_locations_complete(),
415436
total_team_locations=tracker_data.get_team_locations_total_count(),
@@ -547,7 +568,7 @@ def render_ALinkToThePast_multiworld_tracker(tracker_data: TrackerData, enabled_
547568
if area_name != "Total" else tracker_data._multidata["checks_in_area"][player]["Total"]
548569
for area_name in ordered_areas
549570
}
550-
for team, players in tracker_data.get_team_players().items()
571+
for team, players in tracker_data.get_all_slots().items()
551572
for player in players
552573
if tracker_data.get_slot_info(team, player).type != SlotType.group and
553574
tracker_data.get_slot_info(team, player).game == "A Link to the Past"
@@ -585,23 +606,23 @@ def _get_location_table(checks_table: dict) -> dict:
585606

586607
player_location_to_area = {
587608
(team, player): _get_location_table(tracker_data._multidata["checks_in_area"][player])
588-
for team, players in tracker_data.get_team_players().items()
609+
for team, players in tracker_data.get_all_slots().items()
589610
for player in players
590611
if tracker_data.get_slot_info(team, player).type != SlotType.group and
591612
tracker_data.get_slot_info(team, player).game == "A Link to the Past"
592613
}
593614

594615
checks_done: Dict[TeamPlayer, Dict[str: int]] = {
595616
(team, player): {location_name: 0 for location_name in default_locations}
596-
for team, players in tracker_data.get_team_players().items()
617+
for team, players in tracker_data.get_all_slots().items()
597618
for player in players
598619
if tracker_data.get_slot_info(team, player).type != SlotType.group and
599620
tracker_data.get_slot_info(team, player).game == "A Link to the Past"
600621
}
601622

602623
inventories: Dict[TeamPlayer, Dict[int, int]] = {}
603-
player_big_key_locations = {(player): set() for player in tracker_data.get_team_players()[0]}
604-
player_small_key_locations = {player: set() for player in tracker_data.get_team_players()[0]}
624+
player_big_key_locations = {(player): set() for player in tracker_data.get_all_slots()[0]}
625+
player_small_key_locations = {player: set() for player in tracker_data.get_all_slots()[0]}
605626
group_big_key_locations = set()
606627
group_key_locations = set()
607628

@@ -639,7 +660,8 @@ def _get_location_table(checks_table: dict) -> dict:
639660
enabled_trackers=enabled_trackers,
640661
current_tracker="A Link to the Past",
641662
room=tracker_data.room,
642-
room_players=tracker_data.get_team_players(),
663+
all_slots=tracker_data.get_all_slots(),
664+
room_players=tracker_data.get_all_players(),
643665
locations=tracker_data.get_room_locations(),
644666
locations_complete=tracker_data.get_room_locations_complete(),
645667
total_team_locations=tracker_data.get_team_locations_total_count(),

0 commit comments

Comments
 (0)