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

Support for multiple player ratings #486

Merged
merged 15 commits into from
Sep 6, 2019

Conversation

Wesmania
Copy link

This PR will eventually add support for more player ratings in preparation for team matchmaking. For now though, it's a few misc cleanups.

if global_rating is not None:
self.global_rating = global_rating
if ladder_rating is not None:
self.ladder_rating = ladder_rating
Copy link
Collaborator

Choose a reason for hiding this comment

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

Bad naming for global_rating, self.global_rating and self._global_rating, how about to create and use set_global_rating()?

Copy link
Author

Choose a reason for hiding this comment

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

This commit does not change existent naming, and the name property backed by a _name attribute is pretty common practice. The property already acts like a setter and has no side effects beyond setting the backing value anyway.

@@ -76,6 +79,39 @@ def __init__(
self._game = lambda: None
self._game_connection = lambda: None

@property
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't use @property, it doesn't give you anything, only hiding call functions + add wrappers, without proper IDE impossible to detect with naked eye.
Use property only if you're trying to not to break same API during refactoring.
Better to use set_ methods. It's more explicit.

Copy link
Author

@Wesmania Wesmania Aug 21, 2019

Choose a reason for hiding this comment

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

It has no side effects beyond setting the backing value. Sounds like a perfectly fine usage of @property to me.

Copy link
Collaborator

Choose a reason for hiding this comment

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

In fact it has. If you see below, you'll find condition that decides what to do with Rating instance. It is hiding behavior.

Copy link
Author

Choose a reason for hiding this comment

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

I was talking about side effects. The only thing setting this value ever affects is the value returned by the getter. This is a valid use case for a property, what you're describing restricts @property to trivial cases.

@@ -35,18 +35,21 @@ def __init__(
permission_group: int = 0,
lobby_connection: "LobbyConnection" = None
):
super().__init__(player_id, login)
self._faction = 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

typing for local variables + typing for incoming variables like clan, global_rating, ladder_rating.

Copy link
Author

Choose a reason for hiding this comment

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

I had no reason to touch this code beyond copying it. Faction could use an enum anyway rather than just an obvious typing hint.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You can make it better ;-) you're refactoring, isn't it?

Copy link
Author

Choose a reason for hiding this comment

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

Whenever possible, one commit is one feature.

return self._global_rating

@global_rating.setter
def global_rating(self, value: Rating):
Copy link
Collaborator

Choose a reason for hiding this comment

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

wrong typing, use Union[Tuple[int, int], Rating] https://docs.python.org/3/library/typing.html#typing.Union

Copy link
Author

Choose a reason for hiding this comment

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

This is copied from BasePlayer, so I didn't bother touching it. Frankly you know my opinion on type hints, in trivial cases like this one they bring exactly zero benefit.

self._global_rating = value

@property
def ladder_rating(self):
Copy link
Collaborator

Choose a reason for hiding this comment

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

missing return typing, if it would be just variable, not property, code would be much easier to read, and for outside world it behaves the same

Copy link
Author

Choose a reason for hiding this comment

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

As above, didn't bother changing copied code. It deserves to be a named tuple anyway, we can change it then.

tests/conftest.py Outdated Show resolved Hide resolved
if isinstance(value, Rating):
self._global_rating = (value.mu, value.sigma)
else:
self._global_rating = value
Copy link
Collaborator

Choose a reason for hiding this comment

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

if value is None, you may achieve bad behavior.

Copy link
Author

Choose a reason for hiding this comment

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

This is the original behaviour and it was not my intention to change it. It's not set to None internally and if there's an inconsistency with how other code uses it, then you can show and fix it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe it would be a good opportunity to add assert value is not None

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see benefit from moving code from one place to another, if we can make it better.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@norraxx Because it's a lot of extra work and a lot higher chance that you mess something up on accident. I'd be happy if you wanted to make a PR though :)

@@ -35,18 +35,21 @@ def __init__(
permission_group: int = 0,
lobby_connection: "LobbyConnection" = None
):
super().__init__(player_id, login)
self._faction = 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wouldn't use fraction 0, it doesn't looks like it's undefined. Maybe use None?

Copy link
Author

Choose a reason for hiding this comment

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

Commit has no intention to change original behaviour.

@Wesmania Wesmania force-pushed the multiple-ratings branch 3 times, most recently from 42213ff to 6ac9a67 Compare August 23, 2019 19:49
@@ -1,6 +1,8 @@
import time

from .game import Game, ValidityState
from server.rating import RatingType
Copy link
Collaborator

Choose a reason for hiding this comment

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

For now this makes sense, but it won't anymore once you finish multiple rating support :P

Copy link
Author

Choose a reason for hiding this comment

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

You want something else than an enum? Is it supposed to be populated from the db?

@@ -242,6 +247,16 @@ def connections(self):

@property
def teams(self):
"""
A dictionary of lists representing teams
Copy link
Collaborator

Choose a reason for hiding this comment

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

It returns a set not a dict. It would look like {2, 3} assuming that there are 2 teams

Copy link
Author

Choose a reason for hiding this comment

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

Copypasted from BaseGame, didn't touch it yet.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you please fix or delete this comment, because it's wrong

server/matchmaker/search.py Outdated Show resolved Hide resolved
return rounded_mu - 100, rounded_mu + 100
@property
def boundary_75(self):
""" Achieves roughly 75% quality. FIXME - why is it MORE restrictive??? """
Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess my TODO comment was poorly worded. But how are these boundaries going to make sense in a team context?

Copy link
Author

Choose a reason for hiding this comment

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

Dunno, I didn't want to change behaviour here yet.

from trueskill import Rating


# Values correspond to legacy table names. This will be fixed when db gets
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

@Askaholic
Copy link
Collaborator

Popped this onto the test server real quick and got the following error:

INFO     Aug 30  07:29:05 root                           Using StatsD server: 
ERROR    Aug 30  07:29:05 root                           Failure booting server connect() got an unexpected keyword argument 'loop'
Traceback (most recent call last):
  File "server.py", line 68, in <module>
    loop=loop
TypeError: connect() got an unexpected keyword argument 'loop'

@Wesmania Wesmania force-pushed the multiple-ratings branch 3 times, most recently from 1e8903c to 4d44bb2 Compare August 31, 2019 10:37
@Wesmania
Copy link
Author

Server launch issue should be fixed now.

Igor Kotrasinski added 13 commits September 6, 2019 15:38
Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
The Player class is essentially plain old data right now. Remove all
mocking of it to make it easier to modify. If we ever bless Player with
non-trivial behaviour, we can make a mock or an abstract class for it.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
Preparations for adding new rating types.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
This will save us some manual mucking around with asyncio's sleep.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
Everything inherits from the Game class anyway, and all "abstract"
methods are already implemented there.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
This should make tests a bit more manageable.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
This replaces homegrown tools for coroutine testing with asynctest and
pytest-asyncio. One benefit is that each test gets its own fresh event
loop, another will come with proper tools for fast-forwarding event
loops.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
This removes the need for ugly asyncio.sleep monkeypatching. It also
exposes some issues with tests not closing the server after the test
ends; we ignore it for now, eventually we'll fix that too.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
Igor Kotrasinski added 2 commits September 6, 2019 17:48
Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
@Askaholic Askaholic merged commit d5934e9 into FAForever:develop Sep 6, 2019
Brutus5000 pushed a commit that referenced this pull request Jan 27, 2020
* Get rid of BasePlayer class

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Remove Player class mocking in tests

The Player class is essentially plain old data right now. Remove all
mocking of it to make it easier to modify. If we ever bless Player with
non-trivial behaviour, we can make a mock or an abstract class for it.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Improve game stats coverage

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Use a dict-like object for player ratings

Preparations for adding new rating types.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Add asynctest to dependencies

This will save us some manual mucking around with asyncio's sleep.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Replace CoroMock with asynctest's CoroutineMock

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Remove BaseGame class

Everything inherits from the Game class anyway, and all "abstract"
methods are already implemented there.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Fix ban test by formatting >100 year bans as 'forever'

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Make the DB non-global

This should make tests a bit more manageable.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Use asynctest/pytest-asyncio for tests

This replaces homegrown tools for coroutine testing with asynctest and
pytest-asyncio. One benefit is that each test gets its own fresh event
loop, another will come with proper tools for fast-forwarding event
loops.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Add an event loop fast-forwarding decorator

This removes the need for ugly asyncio.sleep monkeypatching. It also
exposes some issues with tests not closing the server after the test
ends; we ignore it for now, eventually we'll fix that too.

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Fix some sleep usage in tests

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* A bit more test coverage

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Remove no longer needed loop fixture

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>

* Update game docstring

Signed-off-by: Igor Kotrasinski <i.kotrasinsk@gmail.com>
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.

3 participants