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 committed Oct 27, 2024
1 parent 645d001 commit 426efa9
Show file tree
Hide file tree
Showing 2 changed files with 270 additions and 4 deletions.
60 changes: 57 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
# https://github.com/FAForever/server/issues/1002
subject = self.player_service.get_player(subject_id)
visibility_context_manager = self._write_visibility_change_update(
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,49 @@ 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
# https://github.com/FAForever/server/issues/1002
subject = self.player_service.get_player(subject_id)
visibility_context_manager = self._write_visibility_change_update(
game,
subject,
)

with visibility_context_manager:
player_attr.add(subject_id)

@contextlib.contextmanager
def _write_visibility_change_update(
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 was_visible and 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):
test_id, _, proto1 = await connect_and_sign_in(

Check warning on line 1124 in tests/integration_tests/test_server.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/integration_tests/test_server.py#L1124

Unused variable 'test_id'
("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):
test_id, _, 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 426efa9

Please sign in to comment.