Skip to content

Commit

Permalink
Send game_info message if social add/remove changes game visibility (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Askaholic authored Nov 3, 2024
1 parent ede2a07 commit 0a667d2
Show file tree
Hide file tree
Showing 2 changed files with 269 additions and 4 deletions.
59 changes: 56 additions & 3 deletions server/lobbyconnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,20 @@ async def command_social_remove(self, message):
friends_and_foes.c.subject_id == subject_id
)))

with contextlib.suppress(KeyError):
player_attr.remove(subject_id)
game = self.player.game
visibility_context_manager = contextlib.nullcontext()

if game and game.host == self.player:
# If the player is currently hosting a game, we need to make sure
# that the visibility change is sent to the subject
subject = self.player_service.get_player(subject_id)
visibility_context_manager = self._write_visibility_change_context(
game,
subject,
)

with visibility_context_manager:
player_attr.discard(subject_id)

async def command_social_add(self, message):
if "friend" in message:
Expand All @@ -358,7 +370,48 @@ async def command_social_add(self, message):
subject_id=subject_id,
))

player_attr.add(subject_id)
game = self.player.game
visibility_context_manager = contextlib.nullcontext()

if game and game.host == self.player:
# If the player is currently hosting a game, we need to make sure
# that the visibility change is sent to the subject
subject = self.player_service.get_player(subject_id)
visibility_context_manager = self._write_visibility_change_context(
game,
subject,
)

with visibility_context_manager:
player_attr.add(subject_id)

@contextlib.contextmanager
def _write_visibility_change_context(
self,
game: Game,
player: Player,
):
# Check visibility before/after
was_visible = game.is_visible_to_player(player)
yield
is_visible = game.is_visible_to_player(player)

if was_visible is is_visible:
return

self._logger.debug(
"Visibility for %s changed for %s from %s -> %s",
game,
player.login,
was_visible,
is_visible,
)

msg = game.to_dict()
if not is_visible:
msg["state"] = "closed"

player.write_message(msg)

async def kick(self):
await self.send({
Expand Down
214 changes: 213 additions & 1 deletion tests/integration_tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sqlalchemy import and_, select

from server.config import config
from server.db.models import avatars, avatars_list, ban
from server.db.models import avatars, avatars_list, ban, friends_and_foes
from server.protocol import DisconnectedError
from tests.utils import fast_forward

Expand Down Expand Up @@ -996,3 +996,215 @@ async def test_avatar_select_not_owned(lobby_server, database):
async with database.acquire() as conn:
result = await get_player_selected_avatars(conn, player_id)
assert result.rowcount == 0


@fast_forward(30)
async def test_social_add_friend(lobby_server, database):
subject_id = 10
player_id, _, proto = await connect_and_sign_in(
("test", "test_password"),
lobby_server,
)
await read_until_command(proto, "game_info")

async with database.acquire() as conn:
result = await conn.execute(
select(friends_and_foes)
.where(
and_(
friends_and_foes.c.user_id == player_id,
friends_and_foes.c.subject_id == subject_id,
)
)
)
row = result.fetchone()
assert row is None

# Other player doesn't even need to be online
await proto.send_message({
"command": "social_add",
"friend": subject_id,
})
await asyncio.sleep(5)

async with database.acquire() as conn:
result = await conn.execute(
select(friends_and_foes)
.where(
and_(
friends_and_foes.c.user_id == player_id,
friends_and_foes.c.subject_id == subject_id,
)
)
)
row = result.fetchone()

assert row.subject_id == subject_id
assert row.status == "FRIEND"


@fast_forward(30)
async def test_social_add_foe(lobby_server, database):
subject_id = 10
player_id, _, proto = await connect_and_sign_in(
("test", "test_password"),
lobby_server,
)
await read_until_command(proto, "game_info")

async with database.acquire() as conn:
result = await conn.execute(
select(friends_and_foes)
.where(
and_(
friends_and_foes.c.user_id == player_id,
friends_and_foes.c.subject_id == subject_id,
)
)
)
row = result.fetchone()
assert row is None

# Other player doesn't even need to be online
await proto.send_message({
"command": "social_add",
"foe": subject_id,
})
await asyncio.sleep(5)

async with database.acquire() as conn:
result = await conn.execute(
select(friends_and_foes)
.where(
and_(
friends_and_foes.c.user_id == player_id,
friends_and_foes.c.subject_id == subject_id,
)
)
)
row = result.fetchone()

assert row.subject_id == subject_id
assert row.status == "FOE"


@fast_forward(30)
async def test_social_add_friend_while_hosting(lobby_server):
_, _, proto1 = await connect_and_sign_in(
("test", "test_password"),
lobby_server,
)
rhiza_id, _, proto2 = await connect_and_sign_in(
("Rhiza", "puff_the_magic_dragon"),
lobby_server,
)
await read_until_command(proto1, "game_info")
await read_until_command(proto2, "game_info")

game_id = await host_game(proto1, visibility="friends")
with pytest.raises(asyncio.TimeoutError):
await read_until_command(proto2, "game_info", timeout=10, uid=game_id)

await proto1.send_message({
"command": "social_add",
"friend": rhiza_id,
})

await read_until_command(
proto2,
"game_info",
timeout=10,
uid=game_id,
state="open",
)


@fast_forward(30)
async def test_social_add_foe_while_hosting(lobby_server):
_, _, proto1 = await connect_and_sign_in(
("test", "test_password"),
lobby_server,
)
rhiza_id, _, proto2 = await connect_and_sign_in(
("Rhiza", "puff_the_magic_dragon"),
lobby_server,
)
await read_until_command(proto1, "game_info")
await read_until_command(proto2, "game_info")

game_id = await host_game(proto1)
await read_until_command(proto2, "game_info", timeout=10, uid=game_id)

await proto1.send_message({
"command": "social_add",
"foe": rhiza_id,
})

await read_until_command(
proto2,
"game_info",
timeout=10,
uid=game_id,
state="closed",
)


@fast_forward(30)
async def test_social_remove_friend_while_hosting(lobby_server):
_, _, proto1 = await connect_and_sign_in(
("friends", "friends"),
lobby_server,
)
test_id, _, proto2 = await connect_and_sign_in(
("test", "test_password"),
lobby_server,
)
await read_until_command(proto1, "game_info")
await read_until_command(proto2, "game_info")

game_id = await host_game(proto1, visibility="friends")
await read_until_command(proto2, "game_info", timeout=5, uid=game_id)

await proto1.send_message({
"command": "social_remove",
"friend": test_id,
})

await read_until_command(
proto2,
"game_info",
timeout=10,
uid=game_id,
state="closed",
)


@fast_forward(30)
async def test_social_remove_foe_while_hosting(lobby_server):
_, _, proto1 = await connect_and_sign_in(
("test", "test_password"),
lobby_server,
)
rhiza_id, _, proto2 = await connect_and_sign_in(
("foed_by_test", "foe"),
lobby_server,
)
await read_until_command(proto1, "game_info")
await read_until_command(proto2, "game_info")

game_id = await host_game(proto1)
with pytest.raises(asyncio.TimeoutError):
await read_until_command(proto2, "game_info", timeout=5, uid=game_id)

await proto1.send_message({
"command": "social_remove",
"foe": rhiza_id,
})

await read_until_command(
proto2,
"game_info",
timeout=10,
uid=game_id,
state="open",
)

0 comments on commit 0a667d2

Please sign in to comment.