Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix v1.14.0 prod errors #999

Merged
merged 4 commits into from
Jan 10, 2024
Merged
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
29 changes: 17 additions & 12 deletions server/gameconnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,11 @@ async def handle_game_result(self, army: Any, result: Any):
self._logger.warning("Invalid result for %s reported: %s", army, result)
else:
await self.game.add_result(
self.player.id, army, result_type, int(score), frozenset(metadata)
self.player.id,
army,
result_type,
int(score),
frozenset(metadata),
)

async def handle_operation_complete(
Expand Down Expand Up @@ -332,25 +336,26 @@ async def handle_operation_complete(

# Each player in a co-op game will send the OperationComplete
# message but we only need to perform this insert once
if not self.game.leaderboard_saved:
await conn.execute(
coop_leaderboard.insert().values(
mission=mission,
gameuid=self.game.id,
secondary=secondary,
time=delta,
player_count=len(self.game.players),
async with self.game.leaderboard_lock:
if not self.game.leaderboard_saved:
await conn.execute(
coop_leaderboard.insert().values(
mission=mission,
gameuid=self.game.id,
secondary=secondary,
time=delta,
player_count=len(self.game.players),
)
)
)
self.game.leaderboard_saved = True
self.game.leaderboard_saved = True

async def handle_json_stats(self, stats: str):
try:
self.game.report_army_stats(stats)
except json.JSONDecodeError as e:
self._logger.warning(
"Malformed game stats reported by %s: '...%s...'",
self._player.login,
self.player.login,
stats[e.pos-20:e.pos+20]
)

Expand Down
3 changes: 3 additions & 0 deletions server/games/coop.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import asyncio

from .game import Game
from .typedefs import FA, GameType, InitMode, ValidityState, Victory

Expand All @@ -19,6 +21,7 @@ def __init__(self, *args, **kwargs):
"Difficulty": 3,
"Expansion": "true"
})
self.leaderboard_lock = asyncio.Lock()
self.leaderboard_saved = False

async def validate_game_mode_settings(self):
Expand Down
9 changes: 8 additions & 1 deletion server/lobbyconnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,11 @@ async def on_message_received(self, message):
})
await self.abort(e.message())
except ClientError as e:
self._logger.warning("Client error: %s", e.message)
self._logger.warning(
"ClientError[%s]: %s",
self.user_agent,
e.message,
)
await self.send({
"command": "notice",
"style": "error",
Expand Down Expand Up @@ -333,6 +337,9 @@ async def command_social_add(self, message):
else:
return

if subject_id in player_attr:
return

async with self._db.acquire() as conn:
await conn.execute(friends_and_foes.insert().values(
user_id=self.player.id,
Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def opt(val):
user=user,
password=pw or "",
port=port,
db=name
db=name,
)


Expand All @@ -183,7 +183,7 @@ def opt(val):
user=user,
password=pw or "",
port=port,
db=name
db=name,
)
await db.connect()

Expand Down Expand Up @@ -211,7 +211,7 @@ async def game(database, players):


GAME_UID = 1
COOP_GAME_UID = 1
COOP_GAME_UID = 2


@pytest.fixture
Expand Down
23 changes: 14 additions & 9 deletions tests/unit_tests/test_gameconnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,14 @@ async def test_json_stats_malformed(
await game_connection.handle_action("JsonStats", ['{"stats": {}'])


async def test_handle_json_stats_malformed(
real_game: Game,
game_connection: GameConnection,
):
game_connection.game = real_game
await game_connection.handle_json_stats('{"stats": {}')


async def test_handle_action_EnforceRating(
game: Game,
game_connection: GameConnection
Expand Down Expand Up @@ -519,9 +527,7 @@ async def test_handle_action_TeamkillHappened(


async def test_handle_action_TeamkillHappened_AI(
game: Game,
game_connection: GameConnection,
database
):
# Should fail with a sql constraint error if this isn't handled correctly
game_connection.abort = mock.AsyncMock()
Expand Down Expand Up @@ -667,13 +673,12 @@ async def test_handle_action_OperationComplete_duplicate(
)

with caplog.at_level(logging.ERROR):
await game_connection.handle_action(
"OperationComplete", [1, 1, time_taken]
)
caplog.clear()
await game_connection.handle_action(
"OperationComplete", [1, 1, time_taken]
)
await asyncio.gather(*(
game_connection.handle_action(
"OperationComplete", [1, 1, time_taken]
)
for _ in range(10)
))

assert not any(
record.exc_info
Expand Down
38 changes: 32 additions & 6 deletions tests/unit_tests/test_lobbyconnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ async def test_command_avatar_list(mocker, lobbyconnection: LobbyConnection):
})


async def test_command_avatar_select(mocker, database, lobbyconnection: LobbyConnection):
async def test_command_avatar_select(database, lobbyconnection: LobbyConnection):
lobbyconnection.player.id = 2 # Dostya test user

await lobbyconnection.on_message_received({
Expand Down Expand Up @@ -656,6 +656,24 @@ async def test_command_social_add_friend(lobbyconnection, database):
assert lobbyconnection.player.friends == {2}


async def test_command_social_add_friend_idempotent(lobbyconnection, database):
lobbyconnection.player.id = 1

friends = await get_friends(lobbyconnection.player.id, database)
assert friends == []
assert lobbyconnection.player.friends == set()

for _ in range(5):
await lobbyconnection.command_social_add({
"command": "social_add",
"friend": 2
})

friends = await get_friends(lobbyconnection.player.id, database)
assert friends == [2]
assert lobbyconnection.player.friends == {2}


async def test_command_social_remove_friend(lobbyconnection, database):
lobbyconnection.player.id = 2

Expand All @@ -672,11 +690,19 @@ async def test_command_social_remove_friend(lobbyconnection, database):
assert friends == []
assert lobbyconnection.player.friends == set()

# Removing twice does nothing
await lobbyconnection.on_message_received({
"command": "social_remove",
"friend": 1
})

async def test_command_social_remove_friend_idempotent(lobbyconnection, database):
lobbyconnection.player.id = 2

friends = await get_friends(lobbyconnection.player.id, database)
assert friends == [1]
lobbyconnection.player.friends = {1}

for _ in range(5):
await lobbyconnection.command_social_remove({
"command": "social_remove",
"friend": 1
})

friends = await get_friends(lobbyconnection.player.id, database)
assert friends == []
Expand Down