WebHost: revamp /api/*tracker/ (#5388)
This commit is contained in:
@@ -11,6 +11,47 @@ from WebHostLib.models import Room
|
|||||||
from WebHostLib.tracker import TrackerData
|
from WebHostLib.tracker import TrackerData
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerAlias(TypedDict):
|
||||||
|
team: int
|
||||||
|
player: int
|
||||||
|
alias: str | None
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerItemsReceived(TypedDict):
|
||||||
|
team: int
|
||||||
|
player: int
|
||||||
|
items: list[NetworkItem]
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerChecksDone(TypedDict):
|
||||||
|
team: int
|
||||||
|
player: int
|
||||||
|
locations: list[int]
|
||||||
|
|
||||||
|
|
||||||
|
class TeamTotalChecks(TypedDict):
|
||||||
|
team: int
|
||||||
|
checks_done: int
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerHints(TypedDict):
|
||||||
|
team: int
|
||||||
|
player: int
|
||||||
|
hints: list[Hint]
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerTimer(TypedDict):
|
||||||
|
team: int
|
||||||
|
player: int
|
||||||
|
time: datetime | None
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerStatus(TypedDict):
|
||||||
|
team: int
|
||||||
|
player: int
|
||||||
|
status: ClientStatus
|
||||||
|
|
||||||
|
|
||||||
@api_endpoints.route("/tracker/<suuid:tracker>")
|
@api_endpoints.route("/tracker/<suuid:tracker>")
|
||||||
@cache.memoize(timeout=60)
|
@cache.memoize(timeout=60)
|
||||||
def tracker_data(tracker: UUID) -> dict[str, Any]:
|
def tracker_data(tracker: UUID) -> dict[str, Any]:
|
||||||
@@ -29,122 +70,77 @@ def tracker_data(tracker: UUID) -> dict[str, Any]:
|
|||||||
|
|
||||||
all_players: dict[int, list[int]] = tracker_data.get_all_players()
|
all_players: dict[int, list[int]] = tracker_data.get_all_players()
|
||||||
|
|
||||||
class PlayerAlias(TypedDict):
|
player_aliases: list[PlayerAlias] = []
|
||||||
player: int
|
|
||||||
name: str | None
|
|
||||||
|
|
||||||
player_aliases: list[dict[str, int | list[PlayerAlias]]] = []
|
|
||||||
"""Slot aliases of all players."""
|
"""Slot aliases of all players."""
|
||||||
for team, players in all_players.items():
|
for team, players in all_players.items():
|
||||||
team_player_aliases: list[PlayerAlias] = []
|
|
||||||
team_aliases = {"team": team, "players": team_player_aliases}
|
|
||||||
player_aliases.append(team_aliases)
|
|
||||||
for player in players:
|
for player in players:
|
||||||
team_player_aliases.append({"player": player, "alias": tracker_data.get_player_alias(team, player)})
|
player_aliases.append({"team": team, "player": player, "alias": tracker_data.get_player_alias(team, player)})
|
||||||
|
|
||||||
class PlayerItemsReceived(TypedDict):
|
player_items_received: list[PlayerItemsReceived] = []
|
||||||
player: int
|
|
||||||
items: list[NetworkItem]
|
|
||||||
|
|
||||||
player_items_received: list[dict[str, int | list[PlayerItemsReceived]]] = []
|
|
||||||
"""Items received by each player."""
|
"""Items received by each player."""
|
||||||
for team, players in all_players.items():
|
for team, players in all_players.items():
|
||||||
player_received_items: list[PlayerItemsReceived] = []
|
|
||||||
team_items_received = {"team": team, "players": player_received_items}
|
|
||||||
player_items_received.append(team_items_received)
|
|
||||||
for player in players:
|
for player in players:
|
||||||
player_received_items.append(
|
player_items_received.append(
|
||||||
{"player": player, "items": tracker_data.get_player_received_items(team, player)})
|
{"team": team, "player": player, "items": tracker_data.get_player_received_items(team, player)})
|
||||||
|
|
||||||
class PlayerChecksDone(TypedDict):
|
player_checks_done: list[PlayerChecksDone] = []
|
||||||
player: int
|
|
||||||
locations: list[int]
|
|
||||||
|
|
||||||
player_checks_done: list[dict[str, int | list[PlayerChecksDone]]] = []
|
|
||||||
"""ID of all locations checked by each player."""
|
"""ID of all locations checked by each player."""
|
||||||
for team, players in all_players.items():
|
for team, players in all_players.items():
|
||||||
per_player_checks: list[PlayerChecksDone] = []
|
|
||||||
team_checks_done = {"team": team, "players": per_player_checks}
|
|
||||||
player_checks_done.append(team_checks_done)
|
|
||||||
for player in players:
|
for player in players:
|
||||||
per_player_checks.append(
|
player_checks_done.append(
|
||||||
{"player": player, "locations": sorted(tracker_data.get_player_checked_locations(team, player))})
|
{"team": team, "player": player, "locations": sorted(tracker_data.get_player_checked_locations(team, player))})
|
||||||
|
|
||||||
total_checks_done: list[dict[str, int]] = [
|
total_checks_done: list[TeamTotalChecks] = [
|
||||||
{"team": team, "checks_done": checks_done}
|
{"team": team, "checks_done": checks_done}
|
||||||
for team, checks_done in tracker_data.get_team_locations_checked_count().items()
|
for team, checks_done in tracker_data.get_team_locations_checked_count().items()
|
||||||
]
|
]
|
||||||
"""Total number of locations checked for the entire multiworld per team."""
|
"""Total number of locations checked for the entire multiworld per team."""
|
||||||
|
|
||||||
class PlayerHints(TypedDict):
|
hints: list[PlayerHints] = []
|
||||||
player: int
|
|
||||||
hints: list[Hint]
|
|
||||||
|
|
||||||
hints: list[dict[str, int | list[PlayerHints]]] = []
|
|
||||||
"""Hints that all players have used or received."""
|
"""Hints that all players have used or received."""
|
||||||
for team, players in tracker_data.get_all_slots().items():
|
for team, players in tracker_data.get_all_slots().items():
|
||||||
per_player_hints: list[PlayerHints] = []
|
|
||||||
team_hints = {"team": team, "players": per_player_hints}
|
|
||||||
hints.append(team_hints)
|
|
||||||
for player in players:
|
for player in players:
|
||||||
player_hints = sorted(tracker_data.get_player_hints(team, player))
|
player_hints = sorted(tracker_data.get_player_hints(team, player))
|
||||||
per_player_hints.append({"player": player, "hints": player_hints})
|
hints.append({"team": team, "player": player, "hints": player_hints})
|
||||||
slot_info = tracker_data.get_slot_info(team, player)
|
slot_info = tracker_data.get_slot_info(player)
|
||||||
# this assumes groups are always after players
|
# this assumes groups are always after players
|
||||||
if slot_info.type != SlotType.group:
|
if slot_info.type != SlotType.group:
|
||||||
continue
|
continue
|
||||||
for member in slot_info.group_members:
|
for member in slot_info.group_members:
|
||||||
team_hints[member]["hints"] += player_hints
|
hints[member - 1]["hints"] += player_hints
|
||||||
|
|
||||||
class PlayerTimer(TypedDict):
|
activity_timers: list[PlayerTimer] = []
|
||||||
player: int
|
|
||||||
time: datetime | None
|
|
||||||
|
|
||||||
activity_timers: list[dict[str, int | list[PlayerTimer]]] = []
|
|
||||||
"""Time of last activity per player. Returned as RFC 1123 format and null if no connection has been made."""
|
"""Time of last activity per player. Returned as RFC 1123 format and null if no connection has been made."""
|
||||||
for team, players in all_players.items():
|
for team, players in all_players.items():
|
||||||
player_timers: list[PlayerTimer] = []
|
|
||||||
team_timers = {"team": team, "players": player_timers}
|
|
||||||
activity_timers.append(team_timers)
|
|
||||||
for player in players:
|
for player in players:
|
||||||
player_timers.append({"player": player, "time": None})
|
activity_timers.append({"team": team, "player": player, "time": None})
|
||||||
|
|
||||||
client_activity_timers: tuple[tuple[int, int], float] = tracker_data._multisave.get("client_activity_timers", ())
|
for (team, player), timestamp in tracker_data._multisave.get("client_activity_timers", []):
|
||||||
for (team, player), timestamp in client_activity_timers:
|
for entry in activity_timers:
|
||||||
# use index since we can rely on order
|
if entry["team"] == team and entry["player"] == player:
|
||||||
# FIX: key is "players" (not "player_timers")
|
entry["time"] = datetime.fromtimestamp(timestamp, timezone.utc)
|
||||||
activity_timers[team]["players"][player - 1]["time"] = datetime.fromtimestamp(timestamp, timezone.utc)
|
break
|
||||||
|
|
||||||
|
connection_timers: list[PlayerTimer] = []
|
||||||
connection_timers: list[dict[str, int | list[PlayerTimer]]] = []
|
|
||||||
"""Time of last connection per player. Returned as RFC 1123 format and null if no connection has been made."""
|
"""Time of last connection per player. Returned as RFC 1123 format and null if no connection has been made."""
|
||||||
for team, players in all_players.items():
|
for team, players in all_players.items():
|
||||||
player_timers: list[PlayerTimer] = []
|
|
||||||
team_connection_timers = {"team": team, "players": player_timers}
|
|
||||||
connection_timers.append(team_connection_timers)
|
|
||||||
for player in players:
|
for player in players:
|
||||||
player_timers.append({"player": player, "time": None})
|
connection_timers.append({"team": team, "player": player, "time": None})
|
||||||
|
|
||||||
client_connection_timers: tuple[tuple[int, int], float] = tracker_data._multisave.get(
|
for (team, player), timestamp in tracker_data._multisave.get("client_connection_timers", []):
|
||||||
"client_connection_timers", ())
|
# find the matching entry
|
||||||
for (team, player), timestamp in client_connection_timers:
|
for entry in connection_timers:
|
||||||
connection_timers[team]["players"][player - 1]["time"] = datetime.fromtimestamp(timestamp, timezone.utc)
|
if entry["team"] == team and entry["player"] == player:
|
||||||
|
entry["time"] = datetime.fromtimestamp(timestamp, timezone.utc)
|
||||||
|
break
|
||||||
|
|
||||||
class PlayerStatus(TypedDict):
|
player_status: list[PlayerStatus] = []
|
||||||
player: int
|
|
||||||
status: ClientStatus
|
|
||||||
|
|
||||||
player_status: list[dict[str, int | list[PlayerStatus]]] = []
|
|
||||||
"""The current client status for each player."""
|
"""The current client status for each player."""
|
||||||
for team, players in all_players.items():
|
for team, players in all_players.items():
|
||||||
player_statuses: list[PlayerStatus] = []
|
|
||||||
team_status = {"team": team, "players": player_statuses}
|
|
||||||
player_status.append(team_status)
|
|
||||||
for player in players:
|
for player in players:
|
||||||
player_statuses.append({"player": player, "status": tracker_data.get_player_client_status(team, player)})
|
player_status.append({"team": team, "player": player, "status": tracker_data.get_player_client_status(team, player)})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
**get_static_tracker_data(room),
|
|
||||||
"aliases": player_aliases,
|
"aliases": player_aliases,
|
||||||
"player_items_received": player_items_received,
|
"player_items_received": player_items_received,
|
||||||
"player_checks_done": player_checks_done,
|
"player_checks_done": player_checks_done,
|
||||||
@@ -153,80 +149,80 @@ def tracker_data(tracker: UUID) -> dict[str, Any]:
|
|||||||
"activity_timers": activity_timers,
|
"activity_timers": activity_timers,
|
||||||
"connection_timers": connection_timers,
|
"connection_timers": connection_timers,
|
||||||
"player_status": player_status,
|
"player_status": player_status,
|
||||||
"datapackage": tracker_data._multidata["datapackage"],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@cache.memoize()
|
|
||||||
def get_static_tracker_data(room: Room) -> dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Builds and caches the static data for this active session tracker, so that it doesn't need to be recalculated.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
class PlayerGroups(TypedDict):
|
||||||
|
slot: int
|
||||||
|
name: str
|
||||||
|
members: list[int]
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerSlotData(TypedDict):
|
||||||
|
player: int
|
||||||
|
slot_data: dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
@api_endpoints.route("/static_tracker/<suuid:tracker>")
|
||||||
|
@cache.memoize(timeout=300)
|
||||||
|
def static_tracker_data(tracker: UUID) -> dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Outputs json data to <root_path>/api/static_tracker/<id of current session tracker>.
|
||||||
|
|
||||||
|
:param tracker: UUID of current session tracker.
|
||||||
|
|
||||||
|
:return: Static tracking data for all players in the room. Typing and docstrings describe the format of each value.
|
||||||
|
"""
|
||||||
|
room: Room | None = Room.get(tracker=tracker)
|
||||||
|
if not room:
|
||||||
|
abort(404)
|
||||||
tracker_data = TrackerData(room)
|
tracker_data = TrackerData(room)
|
||||||
|
|
||||||
all_players: dict[int, list[int]] = tracker_data.get_all_players()
|
all_players: dict[int, list[int]] = tracker_data.get_all_players()
|
||||||
|
|
||||||
class PlayerGroups(TypedDict):
|
groups: list[PlayerGroups] = []
|
||||||
slot: int
|
|
||||||
name: str
|
|
||||||
members: list[int]
|
|
||||||
|
|
||||||
groups: list[dict[str, int | list[PlayerGroups]]] = []
|
|
||||||
"""The Slot ID of groups and the IDs of the group's members."""
|
"""The Slot ID of groups and the IDs of the group's members."""
|
||||||
for team, players in tracker_data.get_all_slots().items():
|
for team, players in tracker_data.get_all_slots().items():
|
||||||
groups_in_team: list[PlayerGroups] = []
|
|
||||||
team_groups = {"team": team, "groups": groups_in_team}
|
|
||||||
groups.append(team_groups)
|
|
||||||
for player in players:
|
for player in players:
|
||||||
slot_info = tracker_data.get_slot_info(team, player)
|
slot_info = tracker_data.get_slot_info(player)
|
||||||
if slot_info.type != SlotType.group or not slot_info.group_members:
|
if slot_info.type != SlotType.group or not slot_info.group_members:
|
||||||
continue
|
continue
|
||||||
groups_in_team.append(
|
groups.append(
|
||||||
{
|
{
|
||||||
"slot": player,
|
"slot": player,
|
||||||
"name": slot_info.name,
|
"name": slot_info.name,
|
||||||
"members": list(slot_info.group_members),
|
"members": list(slot_info.group_members),
|
||||||
})
|
})
|
||||||
class PlayerName(TypedDict):
|
break
|
||||||
player: int
|
|
||||||
name: str
|
|
||||||
|
|
||||||
player_names: list[dict[str, str | list[PlayerName]]] = []
|
|
||||||
"""Slot names of all players."""
|
|
||||||
for team, players in all_players.items():
|
|
||||||
per_team_player_names: list[PlayerName] = []
|
|
||||||
team_names = {"team": team, "players": per_team_player_names}
|
|
||||||
player_names.append(team_names)
|
|
||||||
for player in players:
|
|
||||||
per_team_player_names.append({"player": player, "name": tracker_data.get_player_name(team, player)})
|
|
||||||
|
|
||||||
class PlayerGame(TypedDict):
|
|
||||||
player: int
|
|
||||||
game: str
|
|
||||||
|
|
||||||
games: list[dict[str, int | list[PlayerGame]]] = []
|
|
||||||
"""The game each player is playing."""
|
|
||||||
for team, players in all_players.items():
|
|
||||||
player_games: list[PlayerGame] = []
|
|
||||||
team_games = {"team": team, "players": player_games}
|
|
||||||
games.append(team_games)
|
|
||||||
for player in players:
|
|
||||||
player_games.append({"player": player, "game": tracker_data.get_player_game(team, player)})
|
|
||||||
|
|
||||||
class PlayerSlotData(TypedDict):
|
|
||||||
player: int
|
|
||||||
slot_data: dict[str, Any]
|
|
||||||
|
|
||||||
slot_data: list[dict[str, int | list[PlayerSlotData]]] = []
|
|
||||||
"""Slot data for each player."""
|
|
||||||
for team, players in all_players.items():
|
|
||||||
player_slot_data: list[PlayerSlotData] = []
|
|
||||||
team_slot_data = {"team": team, "players": player_slot_data}
|
|
||||||
slot_data.append(team_slot_data)
|
|
||||||
for player in players:
|
|
||||||
player_slot_data.append({"player": player, "slot_data": tracker_data.get_slot_data(team, player)})
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"groups": groups,
|
"groups": groups,
|
||||||
"slot_data": slot_data,
|
"datapackage": tracker_data._multidata["datapackage"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# It should be exceedingly rare that slot data is needed, so it's separated out.
|
||||||
|
@api_endpoints.route("/slot_data_tracker/<suuid:tracker>")
|
||||||
|
@cache.memoize(timeout=300)
|
||||||
|
def tracker_slot_data(tracker: UUID) -> list[PlayerSlotData]:
|
||||||
|
"""
|
||||||
|
Outputs json data to <root_path>/api/slot_data_tracker/<id of current session tracker>.
|
||||||
|
|
||||||
|
:param tracker: UUID of current session tracker.
|
||||||
|
|
||||||
|
:return: Slot data for all players in the room. Typing completely arbitrary per game.
|
||||||
|
"""
|
||||||
|
room: Room | None = Room.get(tracker=tracker)
|
||||||
|
if not room:
|
||||||
|
abort(404)
|
||||||
|
tracker_data = TrackerData(room)
|
||||||
|
|
||||||
|
all_players: dict[int, list[int]] = tracker_data.get_all_players()
|
||||||
|
|
||||||
|
slot_data: list[PlayerSlotData] = []
|
||||||
|
"""Slot data for each player."""
|
||||||
|
for team, players in all_players.items():
|
||||||
|
for player in players:
|
||||||
|
slot_data.append({"player": player, "slot_data": tracker_data.get_slot_data(player)})
|
||||||
|
break
|
||||||
|
|
||||||
|
return slot_data
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ from .models import GameDataPackage, Room
|
|||||||
# Multisave is currently updated, at most, every minute.
|
# Multisave is currently updated, at most, every minute.
|
||||||
TRACKER_CACHE_TIMEOUT_IN_SECONDS = 60
|
TRACKER_CACHE_TIMEOUT_IN_SECONDS = 60
|
||||||
|
|
||||||
_multidata_cache = {}
|
|
||||||
_multiworld_trackers: Dict[str, Callable] = {}
|
_multiworld_trackers: Dict[str, Callable] = {}
|
||||||
_player_trackers: Dict[str, Callable] = {}
|
_player_trackers: Dict[str, Callable] = {}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ Current endpoints:
|
|||||||
- [`/room_status/<suuid:room_id>`](#roomstatus)
|
- [`/room_status/<suuid:room_id>`](#roomstatus)
|
||||||
- Tracker API
|
- Tracker API
|
||||||
- [`/tracker/<suuid:tracker>`](#tracker)
|
- [`/tracker/<suuid:tracker>`](#tracker)
|
||||||
|
- [`/static_tracker/<suuid:tracker>`](#statictracker)
|
||||||
|
- [`/slot_data_tracker/<suuid:tracker>`](#slotdatatracker)
|
||||||
- User API
|
- User API
|
||||||
- [`/get_rooms`](#getrooms)
|
- [`/get_rooms`](#getrooms)
|
||||||
- [`/get_seeds`](#getseeds)
|
- [`/get_seeds`](#getseeds)
|
||||||
@@ -254,8 +256,6 @@ can either be viewed while on a room tracker page, or from the [room's endpoint]
|
|||||||
<a name=tracker></a>
|
<a name=tracker></a>
|
||||||
Will provide a dict of tracker data with the following keys:
|
Will provide a dict of tracker data with the following keys:
|
||||||
|
|
||||||
- item_link groups and their players (`groups`)
|
|
||||||
- Each player's slot_data (`slot_data`)
|
|
||||||
- Each player's current alias (`aliases`)
|
- Each player's current alias (`aliases`)
|
||||||
- Will return the name if there is none
|
- Will return the name if there is none
|
||||||
- A list of items each player has received as a NetworkItem (`player_items_received`)
|
- A list of items each player has received as a NetworkItem (`player_items_received`)
|
||||||
@@ -265,111 +265,55 @@ Will provide a dict of tracker data with the following keys:
|
|||||||
- The time of last activity of each player in RFC 1123 format (`activity_timers`)
|
- The time of last activity of each player in RFC 1123 format (`activity_timers`)
|
||||||
- The time of last active connection of each player in RFC 1123 format (`connection_timers`)
|
- The time of last active connection of each player in RFC 1123 format (`connection_timers`)
|
||||||
- The current client status of each player (`player_status`)
|
- The current client status of each player (`player_status`)
|
||||||
- The datapackage hash for each player (`datapackage`)
|
|
||||||
- This hash can then be sent to the datapackage API to receive the appropriate datapackage as necessary
|
|
||||||
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"team": 0,
|
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"slot": 5,
|
|
||||||
"name": "testGroup",
|
|
||||||
"members": [
|
|
||||||
1,
|
|
||||||
2
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"slot": 6,
|
|
||||||
"name": "myCoolLink",
|
|
||||||
"members": [
|
|
||||||
3,
|
|
||||||
4
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"slot_data": [
|
|
||||||
{
|
|
||||||
"team": 0,
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"player": 1,
|
|
||||||
"slot_data": {
|
|
||||||
"example_option": 1,
|
|
||||||
"other_option": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"player": 2,
|
|
||||||
"slot_data": {
|
|
||||||
"example_option": 1,
|
|
||||||
"other_option": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"aliases": [
|
"aliases": [
|
||||||
{
|
{
|
||||||
"team": 0,
|
"team": 0,
|
||||||
"players": [
|
"player": 1,
|
||||||
{
|
"alias": "Incompetence"
|
||||||
"player": 1,
|
},
|
||||||
"alias": "Incompetence"
|
{
|
||||||
},
|
"team": 0,
|
||||||
{
|
"player": 2,
|
||||||
"player": 2,
|
"alias": "Slot_Name_2"
|
||||||
"alias": "Slot_Name_2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"player_items_received": [
|
"player_items_received": [
|
||||||
{
|
{
|
||||||
"team": 0,
|
"team": 0,
|
||||||
"players": [
|
"player": 1,
|
||||||
{
|
"items": [
|
||||||
"player": 1,
|
[1, 1, 1, 0],
|
||||||
"items": [
|
[2, 2, 2, 1]
|
||||||
[1, 1, 1, 0],
|
]
|
||||||
[2, 2, 2, 1]
|
},
|
||||||
]
|
{
|
||||||
},
|
"team": 0,
|
||||||
{
|
"player": 2,
|
||||||
"player": 2,
|
"items": [
|
||||||
"items": [
|
[1, 1, 1, 2],
|
||||||
[1, 1, 1, 2],
|
[2, 2, 2, 0]
|
||||||
[2, 2, 2, 0]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"player_checks_done": [
|
"player_checks_done": [
|
||||||
{
|
{
|
||||||
"team": 0,
|
"team": 0,
|
||||||
"players": [
|
"player": 1,
|
||||||
{
|
"locations": [
|
||||||
"player": 1,
|
1,
|
||||||
"locations": [
|
2
|
||||||
1,
|
]
|
||||||
2
|
},
|
||||||
]
|
{
|
||||||
},
|
"team": 0,
|
||||||
{
|
"player": 2,
|
||||||
"player": 2,
|
"locations": [
|
||||||
"locations": [
|
1,
|
||||||
1,
|
2
|
||||||
2
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -382,78 +326,120 @@ Example:
|
|||||||
"hints": [
|
"hints": [
|
||||||
{
|
{
|
||||||
"team": 0,
|
"team": 0,
|
||||||
"players": [
|
"player": 1,
|
||||||
{
|
"hints": [
|
||||||
"player": 1,
|
[1, 2, 4, 6, 0, "", 4, 0]
|
||||||
"hints": [
|
|
||||||
[1, 2, 4, 6, 0, "", 4, 0]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"player": 2,
|
|
||||||
"hints": []
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"team": 0,
|
||||||
|
"player": 2,
|
||||||
|
"hints": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"activity_timers": [
|
"activity_timers": [
|
||||||
{
|
{
|
||||||
"team": 0,
|
"team": 0,
|
||||||
"players": [
|
"player": 1,
|
||||||
{
|
"time": "Fri, 18 Apr 2025 20:35:45 GMT"
|
||||||
"player": 1,
|
},
|
||||||
"time": "Fri, 18 Apr 2025 20:35:45 GMT"
|
{
|
||||||
},
|
"team": 0,
|
||||||
{
|
"player": 2,
|
||||||
"player": 2,
|
"time": "Fri, 18 Apr 2025 20:42:46 GMT"
|
||||||
"time": "Fri, 18 Apr 2025 20:42:46 GMT"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"connection_timers": [
|
"connection_timers": [
|
||||||
{
|
{
|
||||||
"team": 0,
|
"team": 0,
|
||||||
"players": [
|
"player": 1,
|
||||||
{
|
"time": "Fri, 18 Apr 2025 20:38:25 GMT"
|
||||||
"player": 1,
|
},
|
||||||
"time": "Fri, 18 Apr 2025 20:38:25 GMT"
|
{
|
||||||
},
|
"team": 0,
|
||||||
{
|
"player": 2,
|
||||||
"player": 2,
|
"time": "Fri, 18 Apr 2025 21:03:00 GMT"
|
||||||
"time": "Fri, 18 Apr 2025 21:03:00 GMT"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"player_status": [
|
"player_status": [
|
||||||
{
|
{
|
||||||
"team": 0,
|
"team": 0,
|
||||||
"players": [
|
"player": 1,
|
||||||
{
|
"status": 0
|
||||||
"player": 1,
|
},
|
||||||
"status": 0
|
{
|
||||||
},
|
"team": 0,
|
||||||
{
|
"player": 2,
|
||||||
"player": 2,
|
"status": 0
|
||||||
"status": 0
|
}
|
||||||
}
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `/static_tracker/<suuid:tracker>`
|
||||||
|
<a name=statictracker></a>
|
||||||
|
Will provide a dict of static tracker data with the following keys:
|
||||||
|
|
||||||
|
- item_link groups and their players (`groups`)
|
||||||
|
- The datapackage hash for each game (`datapackage`)
|
||||||
|
- This hash can then be sent to the datapackage API to receive the appropriate datapackage as necessary
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"slot": 5,
|
||||||
|
"name": "testGroup",
|
||||||
|
"members": [
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slot": 6,
|
||||||
|
"name": "myCoolLink",
|
||||||
|
"members": [
|
||||||
|
3,
|
||||||
|
4
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"datapackage": {
|
"datapackage": {
|
||||||
"Archipelago": {
|
"Archipelago": {
|
||||||
"checksum": "ac9141e9ad0318df2fa27da5f20c50a842afeecb",
|
"checksum": "ac9141e9ad0318df2fa27da5f20c50a842afeecb",
|
||||||
"version": 0
|
|
||||||
},
|
},
|
||||||
"The Messenger": {
|
"The Messenger": {
|
||||||
"checksum": "6991cbcda7316b65bcb072667f3ee4c4cae71c0b",
|
"checksum": "6991cbcda7316b65bcb072667f3ee4c4cae71c0b",
|
||||||
"version": 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `/slot_data_tracker/<suuid:tracker>`
|
||||||
|
<a name=slotdatatracker></a>
|
||||||
|
Will provide a list of each player's slot_data.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"player": 1,
|
||||||
|
"slot_data": {
|
||||||
|
"example_option": 1,
|
||||||
|
"other_option": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"player": 2,
|
||||||
|
"slot_data": {
|
||||||
|
"example_option": 1,
|
||||||
|
"other_option": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## User Endpoints
|
## User Endpoints
|
||||||
User endpoints can get room and seed details from the current session tokens (cookies)
|
User endpoints can get room and seed details from the current session tokens (cookies)
|
||||||
|
|
||||||
@@ -554,4 +540,4 @@ Example:
|
|||||||
"seed_id": "a528e34c-3b4f-42a9-9f8f-00a4fd40bacb"
|
"seed_id": "a528e34c-3b4f-42a9-9f8f-00a4fd40bacb"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
@@ -93,3 +93,13 @@ class TestTracker(TestBase):
|
|||||||
headers={"If-Modified-Since": "Wed, 21 Oct 2015 07:28:00"}, # missing timezone
|
headers={"If-Modified-Since": "Wed, 21 Oct 2015 07:28:00"}, # missing timezone
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def test_tracker_api(self) -> None:
|
||||||
|
"""Verify that tracker api gives a reply for the room."""
|
||||||
|
with self.app.test_request_context():
|
||||||
|
with self.client.open(url_for("api.tracker_data", tracker=self.tracker_uuid)) as response:
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
with self.client.open(url_for("api.static_tracker_data", tracker=self.tracker_uuid)) as response:
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
with self.client.open(url_for("api.tracker_slot_data", tracker=self.tracker_uuid)) as response:
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|||||||
Reference in New Issue
Block a user