Revamped the rating manager delegate to support party games and to only read
and write ratings once regardless of how many times the game is played. git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@429 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
@@ -23,6 +23,9 @@ package com.threerings.parlor.rating.server;
|
|||||||
|
|
||||||
import static com.threerings.parlor.Log.log;
|
import static com.threerings.parlor.Log.log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
@@ -32,18 +35,23 @@ import com.samskivert.jdbc.RepositoryUnit;
|
|||||||
import com.samskivert.util.ArrayIntSet;
|
import com.samskivert.util.ArrayIntSet;
|
||||||
import com.samskivert.util.ArrayUtil;
|
import com.samskivert.util.ArrayUtil;
|
||||||
import com.samskivert.util.HashIntMap;
|
import com.samskivert.util.HashIntMap;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import com.threerings.util.Name;
|
||||||
|
|
||||||
|
import com.threerings.media.util.MathUtil;
|
||||||
|
|
||||||
|
import com.threerings.crowd.data.BodyObject;
|
||||||
import com.threerings.crowd.data.PlaceObject;
|
import com.threerings.crowd.data.PlaceObject;
|
||||||
import com.threerings.crowd.server.CrowdServer;
|
import com.threerings.crowd.server.CrowdServer;
|
||||||
|
|
||||||
import com.threerings.media.util.MathUtil;
|
import com.threerings.parlor.game.data.GameConfig;
|
||||||
import com.threerings.parlor.game.data.GameObject;
|
import com.threerings.parlor.game.data.GameObject;
|
||||||
import com.threerings.parlor.game.server.GameManager;
|
import com.threerings.parlor.game.server.GameManager;
|
||||||
import com.threerings.parlor.game.server.GameManagerDelegate;
|
import com.threerings.parlor.game.server.GameManagerDelegate;
|
||||||
|
|
||||||
import com.threerings.parlor.rating.server.persist.RatingRecord;
|
import com.threerings.parlor.rating.server.persist.RatingRecord;
|
||||||
import com.threerings.parlor.rating.server.persist.RatingRepository;
|
import com.threerings.parlor.rating.server.persist.RatingRepository;
|
||||||
import com.threerings.util.Name;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rates players after each game and handles persisting the results.
|
* Rates players after each game and handles persisting the results.
|
||||||
@@ -76,6 +84,35 @@ public abstract class RatingManagerDelegate extends GameManagerDelegate
|
|||||||
_gobj = (GameObject) plobj;
|
_gobj = (GameObject) plobj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override // from PlaceManagerDelegate
|
||||||
|
public void bodyEntered (int bodyOid)
|
||||||
|
{
|
||||||
|
super.bodyEntered(bodyOid);
|
||||||
|
|
||||||
|
// if the game is in play and this is a player, load their ratings
|
||||||
|
BodyObject occupant = (BodyObject)CrowdServer.omgr.getObject(bodyOid);
|
||||||
|
if (_gobj.isInPlay() && isPlayer(occupant)) {
|
||||||
|
Rating rating = maybeCreateRating(occupant);
|
||||||
|
if (rating != null) {
|
||||||
|
loadRatings(Collections.singleton(rating));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // from PlaceManagerDelegate
|
||||||
|
public void bodyLeft (int bodyOid)
|
||||||
|
{
|
||||||
|
super.bodyLeft(bodyOid);
|
||||||
|
|
||||||
|
// flush this players rating if its modified
|
||||||
|
for (Rating rating : _ratings.values()) {
|
||||||
|
if (rating.playerOid == bodyOid && rating.modified) {
|
||||||
|
saveRatings(Collections.singleton(rating));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override // from GameManagerDelegate
|
@Override // from GameManagerDelegate
|
||||||
public void gameWillStart ()
|
public void gameWillStart ()
|
||||||
{
|
{
|
||||||
@@ -84,44 +121,125 @@ public abstract class RatingManagerDelegate extends GameManagerDelegate
|
|||||||
// note the time at which we started
|
// note the time at which we started
|
||||||
_startStamp = (int) (System.currentTimeMillis() / 1000);
|
_startStamp = (int) (System.currentTimeMillis() / 1000);
|
||||||
|
|
||||||
final int gameId = _gmgr.getGameConfig().getGameId();
|
// this contains the persistent player id for each position in a seated table game
|
||||||
|
_playerIds = new int[_gmgr.getPlayerSlots()];
|
||||||
|
|
||||||
// enumerate our players
|
// load up the ratings for all players in this game; also make a note of the persistent
|
||||||
final Rating[] ratings = new Rating[_gmgr.getPlayerCount()];
|
// player id of each player position for seated table games
|
||||||
ArrayIntSet ratedSet = new ArrayIntSet();
|
ArrayList<Rating> toLoad = new ArrayList<Rating>();
|
||||||
for (int ii = 0; ii < ratings.length; ii ++) {
|
for (int ii = 0, ll = _gobj.occupants.size(); ii < ll; ii++) {
|
||||||
ratings[ii] = new Rating(_gmgr.getPlayerPersistentId(_gmgr.getPlayer(ii)));
|
BodyObject bobj = (BodyObject)CrowdServer.omgr.getObject(_gobj.occupants.get(ii));
|
||||||
if (ratings[ii].playerId != 0) {
|
int pidx = _gmgr.getPlayerIndex(bobj.getVisibleName());
|
||||||
// this is a player with a persistent id, a player we can rate
|
if (pidx != -1) {
|
||||||
ratedSet.add(ratings[ii].playerId);
|
_playerIds[pidx] = _gmgr.getPlayerPersistentId(bobj);
|
||||||
|
}
|
||||||
|
Rating rating = maybeCreateRating(bobj);
|
||||||
|
if (rating != null) {
|
||||||
|
toLoad.add(rating);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final Integer[] ratedPlayers = ratedSet.toArray(new Integer[ratedSet.size()]);
|
loadRatings(toLoad);
|
||||||
|
}
|
||||||
|
|
||||||
// read ratings from persistent store
|
@Override // from GameManagerDelegate
|
||||||
|
public void gameDidEnd ()
|
||||||
|
{
|
||||||
|
super.gameDidEnd();
|
||||||
|
|
||||||
|
// don't update ratings if the game did not run long enough
|
||||||
|
int gameSecs = (int) (System.currentTimeMillis()/1000 - _startStamp);
|
||||||
|
if (gameSecs < minimumRatedDuration()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute our updated ratings
|
||||||
|
updateRatings();
|
||||||
|
|
||||||
|
// any players who are no longer in the room need their ratings flushed immediately
|
||||||
|
ArrayList<Rating> flushes = new ArrayList<Rating>();
|
||||||
|
for (Rating rating : _ratings.values()) {
|
||||||
|
if (rating.modified && !_gobj.occupants.contains(rating.playerOid)) {
|
||||||
|
flushes.add(rating.cloneForSave());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveRatings(flushes);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Rating maybeCreateRating (BodyObject bobj)
|
||||||
|
{
|
||||||
|
// if this occupant is not a player (or not ratable), skip 'em
|
||||||
|
int playerId = _gmgr.getPlayerPersistentId(bobj);
|
||||||
|
if (playerId == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// if this player's ratings are already loaded and their oids match, no need to reload
|
||||||
|
Rating orating = _ratings.get(playerId);
|
||||||
|
if (orating != null && orating.playerOid == bobj.getOid()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Rating(bobj, playerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the supplied occupant is a player, false if not.
|
||||||
|
*/
|
||||||
|
protected boolean isPlayer (BodyObject occupant)
|
||||||
|
{
|
||||||
|
// avoid having to do this check all over the damned place
|
||||||
|
if (occupant == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is a seated table game, ask the game object
|
||||||
|
if (_gobj.getPlayerIndex(occupant.getVisibleName()) != -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is a party game, everyone's a player!
|
||||||
|
if (_gmgr.getGameConfig().getMatchType() == GameConfig.PARTY) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, sorry pardner
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads up rating information for the specified set of player ids and stores them in the
|
||||||
|
* {@link #_ratings} table.
|
||||||
|
*/
|
||||||
|
protected void loadRatings (final Collection<Rating> ratings)
|
||||||
|
{
|
||||||
|
if (ratings.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Loading " + ratings + "...");
|
||||||
|
final int gameId = _gmgr.getGameConfig().getGameId();
|
||||||
CrowdServer.invoker.postUnit(new RepositoryUnit("loadRatings") {
|
CrowdServer.invoker.postUnit(new RepositoryUnit("loadRatings") {
|
||||||
public void invokePersist () throws Exception {
|
public void invokePersist () throws Exception {
|
||||||
// fetch the previous ratings of our persistent users and map them by player id
|
// map the records by player id so that we can correlate with the db results
|
||||||
HashIntMap<RatingRecord> map = new HashIntMap<RatingRecord>();
|
HashIntMap<Rating> map = new HashIntMap<Rating>();
|
||||||
for (RatingRecord record : _repo.getRatings(gameId, ratedPlayers)) {
|
for (Rating rating : ratings) {
|
||||||
map.put(record.playerId, record);
|
map.put(rating.playerId, rating);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now build the array we keep around until the end of the game
|
// load up the ratings data from the database, update the records
|
||||||
for (Rating rating : ratings) {
|
Integer[] playerIds = map.keySet().toArray(new Integer[map.size()]);
|
||||||
if (rating.playerId == 0) {
|
for (RatingRecord record : _repo.getRatings(gameId, playerIds)) {
|
||||||
continue; // for guests we let the slot remain null
|
Rating rating = map.get(record.playerId);
|
||||||
}
|
if (rating != null) {
|
||||||
RatingRecord record = map.get(rating.playerId);
|
|
||||||
if (record != null) {
|
|
||||||
rating.rating = record.rating;
|
rating.rating = record.rating;
|
||||||
rating.experience = record.experience;
|
rating.experience = record.experience;
|
||||||
}
|
} // else { hell frozen over, pigs flying }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleSuccess () {
|
public void handleSuccess () {
|
||||||
_ratings = ratings; // stick our ratings into the manager
|
// stuff our populated records into the _ratings mapping
|
||||||
|
for (Rating rating : ratings) {
|
||||||
|
_ratings.put(rating.playerId, rating);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleFailure (Exception e) {
|
public void handleFailure (Exception e) {
|
||||||
@@ -131,59 +249,25 @@ public abstract class RatingManagerDelegate extends GameManagerDelegate
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override // from GameManagerDelegate
|
protected void saveRatings (final Collection<Rating> ratings)
|
||||||
public void gameDidEnd ()
|
|
||||||
{
|
{
|
||||||
super.gameDidEnd();
|
if (ratings.size() == 0) {
|
||||||
|
|
||||||
// note the duration of the game (in seconds)
|
|
||||||
int gameSecs = (int) (System.currentTimeMillis()/1000 - _startStamp);
|
|
||||||
|
|
||||||
// update ratings if the game ran long enough, and the persistence code finished reading
|
|
||||||
if (_ratings == null || gameSecs < minimumRatedDuration() || _gobj.getWinnerCount() == 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute the updated ratings
|
log.info("Saving " + ratings + "...");
|
||||||
int[] nratings = new int[_ratings.length];
|
|
||||||
for (int ii = 0; ii < _ratings.length; ii ++) {
|
|
||||||
nratings[ii] = _ratings[ii] != null ? computeRating(ii) : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// and store them
|
|
||||||
boolean modified = false;
|
|
||||||
for (int ii = 0; ii < _ratings.length; ii++) {
|
|
||||||
// skip this rating if it's a guest slot or we weren't able to compute a value
|
|
||||||
if (nratings[ii] < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
_ratings[ii].rating = nratings[ii];
|
|
||||||
_ratings[ii].experience ++;
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// bail if nothing changed
|
|
||||||
if (!modified) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// else persist the result
|
|
||||||
final int gameId = _gmgr.getGameConfig().getGameId();
|
final int gameId = _gmgr.getGameConfig().getGameId();
|
||||||
CrowdServer.invoker.postUnit(new RepositoryUnit("updateRatings") {
|
CrowdServer.invoker.postUnit(new RepositoryUnit("saveRatings") {
|
||||||
public void invokePersist () throws Exception {
|
public void invokePersist () throws Exception {
|
||||||
for (Rating rating : _ratings) {
|
for (Rating rating : ratings) {
|
||||||
if (rating != null) {
|
_repo.setRating(gameId, rating.playerId, rating.rating, rating.experience);
|
||||||
_repo.setRating(gameId, rating.playerId, rating.rating, rating.experience);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleSuccess () {
|
public void handleSuccess () {
|
||||||
for (int ii = 0; ii < _ratings.length; ii++) {
|
// let subclasses publish the new ratings if they so desire
|
||||||
if (_ratings[ii] != null) {
|
for (Rating rating : ratings) {
|
||||||
// let subclasses publish the new ratings if they so desire
|
updateRatingInMemory(gameId, rating);
|
||||||
updateRatingInMemory(gameId, _gmgr.getPlayerName(ii), _ratings[ii]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,46 +278,76 @@ public abstract class RatingManagerDelegate extends GameManagerDelegate
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes updated ratings for the players of this game. The default implementation uses the
|
||||||
|
* {@link GameObject#winners} field to determine winners and losers and uses a FIDE/ELO
|
||||||
|
* algorithm to comptue updated ratings.
|
||||||
|
*/
|
||||||
|
protected void updateRatings ()
|
||||||
|
{
|
||||||
|
// if no one won (or we're not a seated game), no ratings need be computed
|
||||||
|
if (_gobj.getWinnerCount() == 0 || _playerIds.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute the update ratings for all players
|
||||||
|
int[] nratings = new int[_playerIds.length];
|
||||||
|
for (int ii = 0; ii < nratings.length; ii ++) {
|
||||||
|
nratings[ii] = computeRating(ii);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and write them back to their rating records
|
||||||
|
for (int ii = 0; ii < nratings.length; ii++) {
|
||||||
|
Rating rating = _ratings.get(_playerIds[ii]);
|
||||||
|
if (rating != null && nratings[ii] > 0) {
|
||||||
|
rating.rating = nratings[ii];
|
||||||
|
rating.experience++;
|
||||||
|
rating.modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes a player's updated rating using a modified version of the FIDE/ELO system.
|
* Computes a player's updated rating using a modified version of the FIDE/ELO system.
|
||||||
* The rating adjustment is computed for the player versus each opponent individually and
|
|
||||||
* these adjustments are summed and scaled by one over the number of opponents to create
|
|
||||||
* the final rating adjustment, which is then added to the player's previous rating and
|
|
||||||
* bounded to the rating range. <em>Note:</em> provisional players (those with experience of
|
|
||||||
* less than 20) will be treated as having, at most, the default rating when used as an
|
|
||||||
* opponent in calculatons for a non-provisional player.
|
|
||||||
*
|
*
|
||||||
* This method may be overriden to change the rating algorithm.
|
* <p> The rating adjustment is computed for the player versus each opponent individually and
|
||||||
|
* these adjustments are summed and scaled by one over the number of opponents to create the
|
||||||
|
* final rating adjustment, which is then added to the player's previous rating and bounded to
|
||||||
|
* the rating range. <em>Note:</em> provisional players (those with experience of less than 20)
|
||||||
|
* will be treated as having, at most, the default rating when used as an opponent in
|
||||||
|
* calculatons for a non-provisional player.
|
||||||
*
|
*
|
||||||
* @return the player's updated rating or -1 if none of the opponents could be applicably
|
* @return the player's updated rating or -1 if none of the opponents could be applicably rated
|
||||||
* rated against this player.
|
* against this player.
|
||||||
*/
|
*/
|
||||||
protected int computeRating (int pidx)
|
protected int computeRating (int pidx)
|
||||||
{
|
{
|
||||||
float totDeltaR = 0; // the total delta rating
|
float totDeltaR = 0; // the total delta rating
|
||||||
int opponents = 0;
|
int opponents = 0;
|
||||||
|
Rating prating = _ratings.get(_playerIds[pidx]);
|
||||||
|
|
||||||
for (int ii = 0; ii < _ratings.length; ii++) {
|
for (int ii = 0; ii < _playerIds.length; ii++) {
|
||||||
|
Rating orating = _ratings.get(_playerIds[ii]);
|
||||||
// we don't care how we did against ourselves, or against guests...
|
// we don't care how we did against ourselves, or against guests...
|
||||||
if (pidx == ii || _ratings[ii] == null) {
|
if (pidx == ii || orating == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we are non-provisional, and the opponent is provisional, we
|
// if we are non-provisional, and the opponent is provisional, we max the opponent out
|
||||||
// max the opponent out at the default rating to avoid potentially
|
// at the default rating to avoid potentially inflating a real rating with one that has
|
||||||
// inflating a real rating with one that has a lot of uncertainty
|
// a lot of uncertainty
|
||||||
int opprat = _ratings[ii].rating;
|
int opprat = orating.rating;
|
||||||
if (_ratings[pidx].experience >= 20 && _ratings[ii].experience < 20) {
|
if (prating.experience >= 20 && orating.experience < 20) {
|
||||||
opprat = Math.min(opprat, DEFAULT_RATING);
|
opprat = Math.min(opprat, DEFAULT_RATING);
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate K, the score multiplier constant
|
// calculate K, the score multiplier constant
|
||||||
int K;
|
int K;
|
||||||
if (_ratings[pidx].experience < 20) {
|
if (prating.experience < 20) {
|
||||||
K = 64;
|
K = 64;
|
||||||
} else if (_ratings[pidx].rating < 2100) {
|
} else if (prating.rating < 2100) {
|
||||||
K = 32; // experience >= 20
|
K = 32; // experience >= 20
|
||||||
} else if (_ratings[pidx].rating < 2400) {
|
} else if (prating.rating < 2400) {
|
||||||
K = 24; // experience >= 20 && rating >= 2100
|
K = 24; // experience >= 20 && rating >= 2100
|
||||||
} else {
|
} else {
|
||||||
K = 16; // experience >= 20 && rating >= 2400
|
K = 16; // experience >= 20 && rating >= 2400
|
||||||
@@ -242,7 +356,7 @@ public abstract class RatingManagerDelegate extends GameManagerDelegate
|
|||||||
// calculate W, the win value representing the actual result of the game
|
// calculate W, the win value representing the actual result of the game
|
||||||
float W = _gobj.isDraw() ? 0.5f : _gobj.isWinner(pidx) ? 1f : 0f;
|
float W = _gobj.isDraw() ? 0.5f : _gobj.isWinner(pidx) ? 1f : 0f;
|
||||||
// calculate We, the expected win value given the players' respective ratings
|
// calculate We, the expected win value given the players' respective ratings
|
||||||
float dR = opprat - _ratings[pidx].rating;
|
float dR = opprat - prating.rating;
|
||||||
float We = 1.0f / (float)(Math.pow(10.0f, (dR / 400.0f)) + 1);
|
float We = 1.0f / (float)(Math.pow(10.0f, (dR / 400.0f)) + 1);
|
||||||
|
|
||||||
// update the total rating adjustment with the value for this opponent
|
// update the total rating adjustment with the value for this opponent
|
||||||
@@ -256,7 +370,7 @@ public abstract class RatingManagerDelegate extends GameManagerDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return the updated and clamped rating
|
// return the updated and clamped rating
|
||||||
int nrat = Math.round(_ratings[pidx].rating + totDeltaR/opponents);
|
int nrat = Math.round(prating.rating + totDeltaR/opponents);
|
||||||
return MathUtil.bound(MINIMUM_RATING, nrat, MAXIMUM_RATING);
|
return MathUtil.bound(MINIMUM_RATING, nrat, MAXIMUM_RATING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,13 +389,20 @@ public abstract class RatingManagerDelegate extends GameManagerDelegate
|
|||||||
*
|
*
|
||||||
* This method is called on the dobj thread.
|
* This method is called on the dobj thread.
|
||||||
*/
|
*/
|
||||||
protected abstract void updateRatingInMemory (int gameId, Name playerName, Rating rating);
|
protected abstract void updateRatingInMemory (int gameId, Rating rating);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simply encapsulates the rating/experience tuple representing a player's rating for a game.
|
* Encapsulates the rating/experience tuple representing a player's rating for a game.
|
||||||
*/
|
*/
|
||||||
protected static class Rating
|
protected static class Rating
|
||||||
|
implements Cloneable
|
||||||
{
|
{
|
||||||
|
/** The oid of the rated player. */
|
||||||
|
public int playerOid;
|
||||||
|
|
||||||
|
/** The name of the rated player. */
|
||||||
|
public Name playerName;
|
||||||
|
|
||||||
/** The id of the rated player. */
|
/** The id of the rated player. */
|
||||||
public int playerId;
|
public int playerId;
|
||||||
|
|
||||||
@@ -291,15 +412,40 @@ public abstract class RatingManagerDelegate extends GameManagerDelegate
|
|||||||
/** The number of times the player's played our game. */
|
/** The number of times the player's played our game. */
|
||||||
public int experience;
|
public int experience;
|
||||||
|
|
||||||
|
/** Whether or not this rating needs to be written back to the database. */
|
||||||
|
public boolean modified;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up a new {@link Rating} object with default values.
|
* Sets up a new {@link Rating} object with default values.
|
||||||
*/
|
*/
|
||||||
public Rating (int playerId)
|
public Rating (BodyObject player, int playerId)
|
||||||
{
|
{
|
||||||
|
this.playerOid = player.getOid();
|
||||||
|
this.playerName = player.getVisibleName();
|
||||||
this.playerId = playerId;
|
this.playerId = playerId;
|
||||||
this.rating = DEFAULT_RATING;
|
this.rating = DEFAULT_RATING;
|
||||||
this.experience = 0;
|
this.experience = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicates this rating and clears its modified status.
|
||||||
|
*/
|
||||||
|
public Rating cloneForSave ()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Rating rating = (Rating)this.clone();
|
||||||
|
modified = false;
|
||||||
|
return rating;
|
||||||
|
} catch (CloneNotSupportedException cnse) {
|
||||||
|
throw new RuntimeException(cnse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // from Object
|
||||||
|
public String toString ()
|
||||||
|
{
|
||||||
|
return StringUtil.fieldsToString(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An appropriately casted reference to our GameManager. */
|
/** An appropriately casted reference to our GameManager. */
|
||||||
@@ -311,8 +457,11 @@ public abstract class RatingManagerDelegate extends GameManagerDelegate
|
|||||||
/** The RatingRepository that holds our data. */
|
/** The RatingRepository that holds our data. */
|
||||||
protected RatingRepository _repo;
|
protected RatingRepository _repo;
|
||||||
|
|
||||||
|
/** Contains the persistent id of the players in this game. */
|
||||||
|
protected int[] _playerIds;
|
||||||
|
|
||||||
/** The ratings for each player as they were at the beginning of the game. */
|
/** The ratings for each player as they were at the beginning of the game. */
|
||||||
protected Rating[] _ratings;
|
protected HashIntMap<Rating> _ratings = new HashIntMap<Rating>();
|
||||||
|
|
||||||
/** A timestamp set at the beginning of the game, used to calculate its duration. */
|
/** A timestamp set at the beginning of the game, used to calculate its duration. */
|
||||||
protected int _startStamp;
|
protected int _startStamp;
|
||||||
|
|||||||
Reference in New Issue
Block a user