Skip to content
This repository has been archived by the owner on Jul 6, 2024. It is now read-only.

INTRN-220: Catch logout packet up to our progress #98

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/staging-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- INTRN-220-complete-logout-handling

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
4 changes: 2 additions & 2 deletions app/adapters/osu_api_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ async def lookup_beatmap(
if aiosu_beatmap is None:
logger.warning(
"Beatmap not found",
checksum=beatmap_md5,
beatmap_md5=beatmap_md5,
file_name=file_name,
beatmap_id=beatmap_id,
)
Expand All @@ -266,7 +266,7 @@ async def lookup_beatmap(
# TODO: handle this case
logger.error(
"Beatmapset is None",
checksum=beatmap_md5,
beatmap_md5=beatmap_md5,
file_name=file_name,
beatmap_id=beatmap_id,
)
Expand Down
143 changes: 139 additions & 4 deletions app/packet_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,147 @@ async def logout_handler(session: "Session", packet_data: bytes) -> None:

await sessions.delete_by_id(session["session_id"])

# TODO: spectator
# TODO: multiplayer
# TODO: channels
# handle the user spectating another user
if own_presence["spectator_host_session_id"]:
spectator_session_id_removed = await spectators.remove(
own_presence["spectator_host_session_id"],
session["session_id"],
)
assert spectator_session_id_removed is not None

# handle some users spectating us
else:
our_spectators = await spectators.members(session["session_id"])

if len(our_spectators) != 0:
# we have spectators

spectator_channel = await channels.fetch_one_by_name(
f"#spec_{session['session_id']}"
)
assert spectator_channel is not None

for spectator_session_id in our_spectators:
# remove them from our spectators
await spectators.remove(session["session_id"], spectator_session_id)

# remove them from the #spectator channel
await channel_members.remove(
spectator_channel["channel_id"],
spectator_session_id,
)
await packet_bundles.enqueue(
spectator_session_id,
packets.write_channel_kick_packet("#spectator"),
)

# remove us from the #spectator channel
await channel_members.remove(
spectator_channel["channel_id"],
session["session_id"],
)

# delete the #spectator channel
await channels.delete(spectator_channel["channel_id"])

# handle the player being in a multiplayer match
if own_presence["multiplayer_match_id"]:
match = await multiplayer_matches.fetch_one(
own_presence["multiplayer_match_id"]
)
assert not isinstance(match, ServiceError)

# fetch our slot
slot = await multiplayer_slots.fetch_one_by_session_id(
match["match_id"],
session["session_id"],
)
assert slot is not None

# remove us from the match
await multiplayer_slots.partial_update(
match["match_id"],
slot["slot_id"],
account_id=-1,
session_id=UUID(int=0),
status=SlotStatus.OPEN,
team=MatchTeams.NEUTRAL,
mods=0,
loaded=False,
skipped=False,
)

match_channel = await channels.fetch_one_by_name(f"#mp_{match['match_id']}")
assert match_channel is not None

if match["host_account_id"] == session["account_id"]:
# if the host left, pick a new host
slots = await multiplayer_slots.fetch_all(match["match_id"])

new_host_slot = None
for slot in slots:
# slot doesn't have a user
if slot["account_id"] == -1:
continue

new_host_slot = slot
break

# no one is left in the match, close it
if new_host_slot is None:
await _broadcast_to_lobby(
packets.write_dispose_match_packet(match["match_id"])
)

await channel_members.remove(
match_channel["channel_id"],
session["session_id"],
)
await packet_bundles.enqueue(
session["session_id"],
data=(
packets.write_dispose_match_packet(match["match_id"])
+ packets.write_channel_kick_packet("#multiplayer")
),
)

# delete the multiplayer match (and it's slots)
match = await multiplayer_matches.delete(match["match_id"])
assert not isinstance(match, ServiceError)

# delete the #multiplayer channel
match_channel = await channels.delete(match_channel["channel_id"])
assert match_channel is not None

logger.info(
"Match closed due to no members",
match_id=match["match_id"],
)

# handle the player being in any chat channels
for channel in await channels.fetch_many():
# ask for foregiveness; not permission
left_channel = await channel_members.remove(
channel["channel_id"],
session["session_id"],
)
if left_channel is not None:
# update the channel info for everyone else
current_channel_members = await channel_members.members(
channel["channel_id"]
)
for session_id in current_channel_members:
await packet_bundles.enqueue(
session_id,
packets.write_channel_info_packet(
channel["name"],
channel["topic"],
len(current_channel_members),
),
)

# tell everyone else we logged out
if not own_presence["privileges"] & ServerPrivileges.UNRESTRICTED:
if own_presence["privileges"] & ServerPrivileges.UNRESTRICTED:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

funny bug

logout_packet_data = packets.write_logout_packet(session["account_id"])
for other_session in await sessions.fetch_all():
await packet_bundles.enqueue(
Expand Down
7 changes: 5 additions & 2 deletions app/repositories/packet_bundles.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from app import json
from app import logger

WARNING_QUEUE_SIZE_THRESHOLD = 100


def make_key(session_id: UUID | Literal["*"]) -> str:
return f"server:packet-bundles:{session_id}"
Expand Down Expand Up @@ -49,9 +51,10 @@ async def enqueue(
)

# XXX: warn developers if a queue's size becomes very large
if queue_size > 50:
if queue_size > WARNING_QUEUE_SIZE_THRESHOLD:
logger.warning(
"Packet bundle size exceeded 20 items",
f"Packet bundle size exceeded warning threshold",
warning_threshold=WARNING_QUEUE_SIZE_THRESHOLD,
queue_size=queue_size,
session_id=session_id,
)
Expand Down