Skip to content

Commit

Permalink
Issue/#931 Violation tracking fix (#932)
Browse files Browse the repository at this point in the history
* Move violation service test to a separate file

* Track violations by player id

* Add test for party members with violations
  • Loading branch information
Askaholic authored Dec 10, 2022
1 parent b6bbdcc commit 35a23b5
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 117 deletions.
22 changes: 15 additions & 7 deletions server/ladder_service/violation_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,25 @@ class ViolationService(Service):
"""

def __init__(self):
self.violations: dict[Player, Violation] = {}
# We store a reference to the original `Player` object for logging only
self._violations: dict[int, tuple[Player, Violation]] = {}

async def initialize(self):
self._cleanup_task = at_interval(5, func=self.clear_expired)

def clear_expired(self):
now = datetime_now()
for player, violation in list(self.violations.items()):
for player, violation in list(self._violations.values()):
if violation.is_expired(now):
self._clear_violation(player)

def register_violations(self, players: list[Player]):
now = datetime_now()
for player in players:
violation = self.violations.get(player)
violation = self.get_violation(player)
if violation is None or violation.is_expired(now):
violation = Violation(time=now)
self.violations[player] = violation
self.set_violation(player, violation)
else:
violation.register()

Expand Down Expand Up @@ -109,7 +110,7 @@ def get_violations(self, players: list[Player]) -> dict[Player, Violation]:
now = datetime_now()
result = {}
for player in players:
violation = self.violations.get(player)
violation = self.get_violation(player)
if not violation:
continue
elif violation.get_ban_expiration() > now:
Expand All @@ -119,11 +120,18 @@ def get_violations(self, players: list[Player]) -> dict[Player, Violation]:

return result

def get_violation(self, player: Player) -> Optional[Violation]:
_, violation = self._violations.get(player.id, (None, None))
return violation

def set_violation(self, player: Player, violation: Violation):
self._violations[player.id] = (player, violation)

def _clear_violation(self, player: Player):
violation = self.violations.get(player)
violation = self.get_violation(player)
self._logger.debug(
"Cleared violation for player %s: %s",
player.login,
violation
)
del self.violations[player]
del self._violations[player.id]
106 changes: 1 addition & 105 deletions tests/integration_tests/test_matchmaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import math
import re
from collections import deque
from datetime import datetime, timezone

import pytest
from sqlalchemy import select
Expand All @@ -24,8 +23,7 @@
queue_players_for_matchmaking,
queue_temp_players_for_matchmaking,
read_until_launched,
send_player_options,
start_search
send_player_options
)


Expand Down Expand Up @@ -568,105 +566,3 @@ async def test_search_info_messages(lobby_server):

with pytest.raises(asyncio.TimeoutError):
await read_until_command(proto, "search_info", timeout=5)


@fast_forward(360)
async def test_failed_start_ban_guest(mocker, lobby_server):
mock_now = mocker.patch(
"server.ladder_service.violation_service.datetime_now",
return_value=datetime(2022, 2, 5, tzinfo=timezone.utc)
)
_, host, guest_id, guest = await queue_players_for_matchmaking(lobby_server)

# The player that queued last will be the host
async def launch_game_and_timeout_guest():
await read_until_command(host, "game_launch")
await open_fa(host)
await read_until_command(host, "game_info")

await read_until_command(guest, "game_launch")
await read_until_command(guest, "match_cancelled", timeout=120)
await read_until_command(host, "match_cancelled")
await host.send_message({
"command": "GameState",
"target": "game",
"args": ["Ended"]
})

await launch_game_and_timeout_guest()

# Second time searching there is no ban
await start_search(host)
await start_search(guest)
await launch_game_and_timeout_guest()

# Third time searching there is a short ban
await guest.send_message({
"command": "game_matchmaking",
"state": "start",
"queue_name": "ladder1v1"
})

msg = await read_until_command(guest, "search_timeout")
assert msg == {
"command": "search_timeout",
"timeouts": [{
"player": guest_id,
"expires_at": "2022-02-05T00:10:00+00:00"
}]
}

mock_now.return_value = datetime(2022, 2, 5, 0, 10, tzinfo=timezone.utc)
await asyncio.sleep(1)

# Third successful search
await start_search(host)
await start_search(guest)
await launch_game_and_timeout_guest()

# Fourth time searching there is a long ban
await guest.send_message({
"command": "game_matchmaking",
"state": "start",
"queue_name": "ladder1v1"
})

msg = await read_until_command(guest, "search_timeout")
assert msg == {
"command": "search_timeout",
"timeouts": [{
"player": guest_id,
"expires_at": "2022-02-05T00:40:00+00:00"
}]
}

mock_now.return_value = datetime(2022, 2, 5, 0, 40, tzinfo=timezone.utc)
await asyncio.sleep(1)

# Fourth successful search
await start_search(host)
await start_search(guest)
await launch_game_and_timeout_guest()

# Fifth time searching there is a long ban
await guest.send_message({
"command": "game_matchmaking",
"state": "start",
"queue_name": "ladder1v1"
})

msg = await read_until_command(guest, "search_timeout")
assert msg == {
"command": "search_timeout",
"timeouts": [{
"player": guest_id,
"expires_at": "2022-02-05T01:10:00+00:00"
}]
}

msg = await read_until_command(guest, "notice")
assert msg == {
"command": "notice",
"style": "info",
"text": "Player ladder2 is timed out for 30 minutes"
}
Loading

0 comments on commit 35a23b5

Please sign in to comment.