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

Add outcome override for ladder 1v1 games #669

Conversation

cleborys
Copy link
Member

Gives the possibility to decide the outcome of 1v1 ladder games by score instead of by reported result, as it was a year ago.
I added a config variable, so that this can be turned on and off dynamically (e.g. for testing new game patches on the test server).

I hope that the game still reports scores as it did and that that didn't change with recent patches?

Could make this override for all 1v1 games, but I thought I'd rather have as little scope as possible.

Addresses issue #580 but should be regarded a temporary fix.

@@ -34,3 +36,18 @@ def get_army_score(self, army: int) -> int:
as 1 for win and 0 for anything else.
"""
return self._results.victory_only_score(army)

def _outcome_override_hook(self) -> Optional[List[GameOutcome]]:
if not config.LADDER_1V1_OUTCOME_OVERRIDE or len(self.players) > 2:
Copy link
Member Author

Choose a reason for hiding this comment

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

Checking the player count isn't actually necessary since max_players is set to two, but with tmm coming up I wanted to be sure, if it adapts this class.

return None
team_sets = self.get_team_sets()
army_scores = [
self._results.score(self.get_player_option(team_set.pop().id, "Army"))
Copy link
Member Author

Choose a reason for hiding this comment

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

Cannot use self.get_army_score(...), since that was overridden to use the reported result instead.
I thought the better way was

Suggested change
self._results.score(self.get_player_option(team_set.pop().id, "Army"))
super().get_army_score(self.get_player_option(team_set.pop().id, "Army"))

but for some reason the super() call fails, saying that the types aren't related?

super(type, obj): obj must be an instance or subtype of type

Forcing it's hand with super(Game, self) says that it has no method get_army_score, I was very confused and still don't know what was going on.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the right way to explicitly call it would be super(LadderGame, self), the docstring for super says:

super() -> same as super(__class__, <first argument>)

But I'm not gonna worry about investigating it right now :P

@Askaholic
Copy link
Collaborator

Ok, I managed to run some test games. Here is the log for a game where I left early and the other player stayed until the end:

TRACE    Sep 23  19:20:53 LobbyConnection                << Askaholic: {'command': 'GameResult', 'args': [1, 'defeat -10'], 'target': 'game'}
INFO     Sep 23  19:20:53 LadderGame.11623726            166198 reported result for army 1: defeat -10
TRACE    Sep 23  19:20:53 LobbyConnection                << yenon: {'command': 'GameResult', 'args': [2, 'defeat -10'], 'target': 'game'}
INFO     Sep 23  19:20:53 LadderGame.11623726            102144 reported result for army 2: defeat -10
TRACE    Sep 23  19:20:54 LobbyConnection                << Askaholic: {'command': 'GameState', 'args': ['Ended'], 'target': 'game'}
INFO     Sep 23  19:20:54 LadderGame.11623726            Removed game connection GameConnection(Player(Askaholic, 166198, (1495.86, 98.7115), (821.2538981582424, 100.1683523272519)), Game(11623726,yenon,maps/canis3v3.v0001.zip,2))
DEBUG    Sep 23  19:20:54 GameConnection                 GameConnection(Player(Askaholic, 166198, (1495.86, 98.7115), (821.2538981582424, 100.1683523272519)), Game(11623726,yenon,maps/canis3v3.v0001.zip,2)).abort()
TRACE    Sep 23  19:21:01 LobbyConnection                << yenon: {'command': 'GameResult', 'args': [1, 'victory 10'], 'target': 'game'}
INFO     Sep 23  19:21:01 LadderGame.11623726            102144 reported result for army 1: victory 10
TRACE    Sep 23  19:21:04 LobbyConnection                << yenon: {'command': 'GameEnded', 'args': [], 'target': 'game'}
INFO     Sep 23  19:21:04 LadderGame.11623726            Game finished normally
DEBUG    Sep 23  19:21:04 LadderGame.11623726            Saving scores from game 11623726
INFO     Sep 23  19:21:04 GameResultReports              Multiple outcomes for game 11623726 army 1 resolved to GameOutcome.CONFLICTING. Reports are: defaultdict(<class 'set'>, {<GameOutcome.DEFEAT: 'DEFEAT'>: {166198}, <GameOutcome.VICTORY: 'VICTORY'>: {102144}})
INFO     Sep 23  19:21:04 LadderGame.11623726            Result for army 1, player: Player(yenon, 102144, (1276.94, 90.329), (644.928857132299, 137.90429402402538)): score 1, outcome GameOutcome.CONFLICTING
INFO     Sep 23  19:21:04 LadderGame.11623726            Result for army 2, player: Player(Askaholic, 166198, (1495.86, 98.7115), (821.2538981582424, 100.1683523272519)): score 0, outcome GameOutcome.DEFEAT
INFO     Sep 23  19:21:04 LadderGame.11623726            Score for player Player(yenon, 102144, (1276.94, 90.329), (644.928857132299, 137.90429402402538)): score 1, outcome GameOutcome.CONFLICTING
INFO     Sep 23  19:21:04 LadderGame.11623726            Score for player Player(Askaholic, 166198, (1495.86, 98.7115), (821.2538981582424, 100.1683523272519)): score 0, outcome GameOutcome.DEFEAT
INFO     Sep 23  19:21:04 GameResultReports              Multiple outcomes for game 11623726 army 1 resolved to GameOutcome.CONFLICTING. Reports are: defaultdict(<class 'set'>, {<GameOutcome.DEFEAT: 'DEFEAT'>: {166198}, <GameOutcome.VICTORY: 'VICTORY'>: {102144}})
INFO     Sep 23  19:21:04 GameResultReports              Conflicting scores (Counter({-10: 1, 10: 1})) reported for game 11623726
WARNING  Sep 23  19:21:04 MessageQueueService            Not connected to RabbitMQ, unable to publish message.
DEBUG    Sep 23  19:21:04 RatingService                  Queued up rating request GameRatingSummary(game_id=11623726, rating_type='ladder_1v1', teams=[TeamRatingSummary(outcome=<GameOutcome.VICTORY: 'VICTORY'>, player_ids={102144}), TeamRatingSummary(outcome=<GameOutcome.DEFEAT: 'DEFEAT'>, player_ids={166198})])
DEBUG    Sep 23  19:21:04 RatingService                  Now rating request GameRatingSummary(game_id=11623726, rating_type='ladder_1v1', teams=[TeamRatingSummary(outcome=<GameOutcome.VICTORY: 'VICTORY'>, player_ids={102144}), TeamRatingSummary(outcome=<GameOutcome.DEFEAT: 'DEFEAT'>, player_ids={166198})])
INFO     Sep 23  19:21:04 GameResultReports              Multiple outcomes for game 11623726 army 1 resolved to GameOutcome.CONFLICTING. Reports are: defaultdict(<class 'set'>, {<GameOutcome.DEFEAT: 'DEFEAT'>: {166198}, <GameOutcome.VICTORY: 'VICTORY'>: {102144}})
DEBUG    Sep 23  19:21:04 GameStatsService               Processing game stats for player: yenon
DEBUG    Sep 23  19:21:04 GameStatsService               Army result for Player(yenon, 102144, (1276.94, 90.329), (644.928857132299, 137.90429402402538)) => GameOutcome.CONFLICTING 
DEBUG    Sep 23  19:21:04 GameRater                      Rating groups: [{102144: trueskill.Rating(mu=644.929, sigma=137.904)}, {166198: trueskill.Rating(mu=821.254, sigma=100.168)}]
DEBUG    Sep 23  19:21:04 GameRater                      Ranks: [0, 1]
DEBUG    Sep 23  19:21:04 RatingService                  Saving rating change stats for game 11623726
DEBUG    Sep 23  19:21:04 RatingService                  New ladder_1v1 rating for player with id 102144: trueskill.Rating(mu=644.929, sigma=137.904) -> trueskill.Rating(mu=705.151, sigma=131.286)
DEBUG    Sep 23  19:21:04 RatingService                  Sending player rating update for player with id 102144
DEBUG    Sep 23  19:21:04 PlayerService                  Received rating change for player Player(yenon, 102144, (1276.94, 90.329), (644.928857132299, 137.90429402402538)).
DEBUG    Sep 23  19:21:04 RatingService                  New ladder_1v1 rating for player with id 166198: trueskill.Rating(mu=821.254, sigma=100.168) -> trueskill.Rating(mu=789.332, sigma=98.005)
DEBUG    Sep 23  19:21:04 RatingService                  Sending player rating update for player with id 166198
DEBUG    Sep 23  19:21:04 PlayerService                  Received rating change for player Player(Askaholic, 166198, (1495.86, 98.7115), (821.2538981582424, 100.1683523272519)).
DEBUG    Sep 23  19:21:04 RatingService                  Done rating request.

My client incorrectly reported that my opponent was defeated, but the game was still rated correctly because my opponent reported that he won.

It is still possible to have games incorrectly count as draws if the winner of the game also leaves early, since the victory result doesn't get reported until a few seconds after one player looses. But there's nothing we can do about that on the server side.

return None
team_sets = self.get_team_sets()
army_scores = [
self._results.score(self.get_player_option(team_set.pop().id, "Army"))
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the right way to explicitly call it would be super(LadderGame, self), the docstring for super says:

super() -> same as super(__class__, <first argument>)

But I'm not gonna worry about investigating it right now :P

@Askaholic Askaholic merged commit 1e72a29 into FAForever:develop Sep 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants