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

Issue/#687 Fix coop leaderboards again #718

Merged
merged 4 commits into from
Feb 21, 2021
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
15 changes: 11 additions & 4 deletions server/games/coop.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from server.abc.base_game import InitMode

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


class CoopGame(Game):
Expand All @@ -18,7 +19,7 @@ def __init__(self, *args, **kwargs):
"TeamSpawn": "fixed",
"RevealedCivilians": "No",
"Difficulty": 3,
"Expansion": 1
"Expansion": "true"
})

async def validate_game_mode_settings(self):
Expand All @@ -29,8 +30,14 @@ async def validate_game_mode_settings(self):
valid_options = {
"Victory": (Victory.SANDBOX, ValidityState.WRONG_VICTORY_CONDITION),
"TeamSpawn": ("fixed", ValidityState.SPAWN_NOT_FIXED),
"RevealedCivilians": ("No", ValidityState.CIVILIANS_REVEALED),
"RevealedCivilians": (FA.FALSE, ValidityState.CIVILIANS_REVEALED),
"Difficulty": (3, ValidityState.WRONG_DIFFICULTY),
"Expansion": (1, ValidityState.EXPANSION_DISABLED),
"Expansion": (FA.TRUE, ValidityState.EXPANSION_DISABLED),
}
await self._validate_game_options(valid_options)

async def process_game_results(self):
"""
When a coop game ends, we don't expect there to be any game results.
"""
pass
28 changes: 16 additions & 12 deletions server/games/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from ..abc.base_game import GameConnectionState, InitMode
from ..players import Player, PlayerState
from .typedefs import (
FA,
BasicGameInfo,
EndedGameInfo,
FeaturedModType,
Expand Down Expand Up @@ -418,14 +419,7 @@ async def on_game_end(self):
await self.mark_invalid(ValidityState.MUTUAL_DRAW)
return

if not self._results:
await self.mark_invalid(ValidityState.UNKNOWN_RESULT)
return

await self.persist_results()

game_results = await self.resolve_game_results()
await self.game_service.publish_game_results(game_results)
await self.process_game_results()

self._process_pending_army_stats()
except Exception: # pragma: no cover
Expand All @@ -438,6 +432,16 @@ async def on_game_end(self):
async def _run_pre_rate_validity_checks(self):
pass

async def process_game_results(self):
if not self._results:
await self.mark_invalid(ValidityState.UNKNOWN_RESULT)
return

await self.persist_results()

game_results = await self.resolve_game_results()
await self.game_service.publish_game_results(game_results)

async def resolve_game_results(self) -> EndedGameInfo:
if self.state not in (GameState.LIVE, GameState.ENDED):
raise GameError("Cannot rate game that has not been launched.")
Expand Down Expand Up @@ -614,11 +618,11 @@ async def validate_game_settings(self):
await self.mark_invalid(ValidityState.FFA_NOT_RANKED)
return
valid_options = {
"AIReplacement": ("Off", ValidityState.HAS_AI_PLAYERS),
Copy link
Member

Choose a reason for hiding this comment

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

Oh wow I didn't realize that there were even more words for true/false used already 😅

"AIReplacement": (FA.FALSE, ValidityState.HAS_AI_PLAYERS),
"FogOfWar": ("explored", ValidityState.NO_FOG_OF_WAR),
"CheatsEnabled": ("false", ValidityState.CHEATS_ENABLED),
"PrebuiltUnits": ("Off", ValidityState.PREBUILT_ENABLED),
"NoRushOption": ("Off", ValidityState.NORUSH_ENABLED),
"CheatsEnabled": (FA.FALSE, ValidityState.CHEATS_ENABLED),
"PrebuiltUnits": (FA.FALSE, ValidityState.PREBUILT_ENABLED),
"NoRushOption": (FA.FALSE, ValidityState.NORUSH_ENABLED),
"RestrictedCategories": (0, ValidityState.BAD_UNIT_RESTRICTIONS),
"TeamLock": ("locked", ValidityState.UNLOCKED_TEAMS)
}
Expand Down
29 changes: 28 additions & 1 deletion server/games/typedefs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from enum import Enum, unique
from typing import Dict, List, NamedTuple, Optional, Set
from typing import Any, Dict, List, NamedTuple, Optional, Set

from server.games.game_results import GameOutcome
from server.players import Player
Expand Down Expand Up @@ -190,3 +190,30 @@ def to_dict(self):
for team_summary in self.team_summaries
],
}


class _FATrue(object):
__slots__ = ()

def __eq__(self, other: Any) -> bool:
if isinstance(other, str):
other = other.lower()

return other in (True, "true", "on", "yes", 1)


class _FAFalse(object):
__slots__ = ()

def __eq__(self, other: Any) -> bool:
if isinstance(other, str):
other = other.lower()

return other in (False, "false", "off", "no", 0)


class FA(object):
__slots__ = ()

TRUE = _FATrue()
FALSE = _FAFalse()
101 changes: 101 additions & 0 deletions tests/integration_tests/test_coop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import datetime

import pytest
from sqlalchemy import select

from server.db.models import coop_leaderboard
from tests.utils import fast_forward

from .conftest import connect_and_sign_in, read_until, read_until_command
from .test_game import host_game, send_player_options

# All test coroutines will be treated as marked.
pytestmark = pytest.mark.asyncio


@fast_forward(5)
async def test_host_coop_game(lobby_server):
_, _, proto = await connect_and_sign_in(
("test", "test_password"),
lobby_server
)

await read_until_command(proto, "game_info")

await host_game(proto, mod="coop", title="")

msg = await read_until_command(proto, "game_info")

assert msg["title"] == "test's game"
assert msg["mapname"] == "scmp_007"
assert msg["map_file_path"] == "maps/scmp_007.zip"
assert msg["featured_mod"] == "coop"
assert msg["game_type"] == "coop"


@fast_forward(30)
async def test_single_player_game_recorded(lobby_server, database):
test_id, _, proto = await connect_and_sign_in(
("test", "test_password"), lobby_server
)
await read_until_command(proto, "game_info")

# Set up the game
game_id = await host_game(proto, mod="coop", mapname="scmp_coop_123.v0002")
# Set player options
await send_player_options(
proto,
[test_id, "Army", 1],
[test_id, "Team", 1],
[test_id, "StartSpot", 1],
[test_id, "Faction", 1],
[test_id, "Color", 1],
)

# Launch game
await proto.send_message({
"target": "game",
"command": "GameState",
"args": ["Launching"]
})

await read_until(
proto,
lambda cmd: cmd["command"] == "game_info" and cmd["launched_at"]
)

# End the game
# Single player coop won't report any results

await proto.send_message({
"target": "game",
"command": "GameEnded",
"args": []
})

await proto.send_message({
"target": "game",
"command": "OperationComplete",
"args": [1, 0, "00:11:50"]
})

# Now disconnect
await proto.send_message({
"target": "game",
"command": "GameState",
"args": ["Ended"]
})

await read_until_command(proto, "game_info", uid=game_id, state="closed")

async with database.acquire() as conn:
result = await conn.execute(
select([coop_leaderboard]).where(
coop_leaderboard.c.gameuid == game_id
)
)
row = await result.fetchone()
assert row is not None
assert row.secondary == 0
assert row.time == datetime.time(0, 11, 50)
assert row.player_count == 1
13 changes: 10 additions & 3 deletions tests/integration_tests/test_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,18 @@
pytestmark = pytest.mark.asyncio


async def host_game(proto: Protocol) -> int:
async def host_game(
proto: Protocol,
*,
mod: str = "faf",
visibility: str = "public",
**kwargs
) -> int:
await proto.send_message({
"command": "game_host",
"mod": "faf",
"visibility": "public"
"mod": mod,
"visibility": visibility,
**kwargs
})
msg = await read_until_command(proto, "game_launch")
game_id = int(msg["uid"])
Expand Down
25 changes: 0 additions & 25 deletions tests/integration_tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,31 +334,6 @@ async def test_host_missing_fields(event_loop, lobby_server, player_service):
assert msg["featured_mod"] == "faf"


@fast_forward(5)
async def test_host_coop_game(lobby_server):
player_id, session, proto = await connect_and_sign_in(
("test", "test_password"),
lobby_server
)

await read_until_command(proto, "game_info")

await proto.send_message({
"command": "game_host",
"mod": "coop",
"visibility": "public",
"title": ""
})

msg = await read_until_command(proto, "game_info")

assert msg["title"] == "test's game"
assert msg["mapname"] == "scmp_007"
assert msg["map_file_path"] == "maps/scmp_007.zip"
assert msg["featured_mod"] == "coop"
assert msg["game_type"] == "coop"


async def test_play_game_while_queueing(lobby_server):
player_id, session, proto = await connect_and_sign_in(
("test", "test_password"),
Expand Down