hostile-takeover/server/player.cpp
Nathan Fulton d6b301c3e7 Track client platform version
Client platform version can be retrieved live with /ids and is also
recorded in the playerdetail module.
2016-08-31 23:54:48 -04:00

234 lines
5.9 KiB
C++

#include "server/player.h"
#include "base/tick.h"
namespace wi {
// lag time period to be declared laggy player
#define kctLagGrace 200
// to become non-laggy, the player must be not laggy for this period
#define kctLagRedemption 0
// if still laggy after this period, recommend kill
#define kctLagKill 3000
Player::Player() {
Init(0);
}
void Player::Init(Pid pid) {
anonymous_ = false;
havestats_ = false;
cur_ = 0;
wf_ = 0;
pid_ = pid;
side_ = ksideNeutral;
endpoint_ = NULL;
lag_ = knLagNone;
nLagState_ = knLagNone;
tLagStart_ = 0;
tLastLag_ = 0;
updates_ = -1;
strncpyz(name_, "Unfulfilled", sizeof(name_));
memset(alatr_, 0, sizeof(alatr_));
memset(&ws_, 0, sizeof(ws_));
memset(did_, 0, sizeof(did_));
memset(platform_, 0, sizeof(platform_));
}
void Player::SetEndpoint(Endpoint *endpoint) {
endpoint_ = endpoint;
if (endpoint == NULL) {
return;
}
strncpyz(name_, endpoint->name(), sizeof(name_));
anonymous_ = endpoint->anonymous();
address_ = endpoint->xpump().socket()->GetRemoteAddress();
strncpyz(did_, endpoint->did(), sizeof(did_));
strncpyz(platform_, endpoint->platform(), sizeof(platform_));
lag_ = knLagNone;
nLagState_ = knLagNone;
tLagStart_ = 0;
tLastLag_ = 0;
}
int Player::GetLagTimeout() {
// Return seconds until this player gets to the kill state
long64 ctElapsed = base::GetTickCount() - tLagStart_;
if (ctElapsed > kctLagKill) {
return 0;
}
return (int)((kctLagKill - ctElapsed + 50) / 100);
}
void Player::SetLagState(int nLagState) {
nLagState_ = nLagState;
tLastLag_ = base::GetTickCount();
tLagStart_ = tLastLag_;
}
int Player::UpdateLagState(long cUpdates) {
if (wf_ & kfPlrDisconnectBroadcasted) {
SetLagState(knLagNone);
return knLagNone;
}
if (updates_ < cUpdates) {
// Player is behind
switch (nLagState_) {
case knLagNone:
// Remember that pplr is lagging and when this started
tLagStart_ = base::GetTickCount();
nLagState_ = knLagGrace;
break;
case knLagGrace:
// This state is effectively a grace period. If the player remains
// laggy through this period, it becomes guilty of lag
if (base::GetTickCount() - tLagStart_ >= kctLagGrace) {
nLagState_ = knLagGuilty;
tLastLag_ = base::GetTickCount();
}
break;
case knLagGuilty:
case knLagKill:
// This player has lagged long enough to be guilty as charged. Now
// the player needs to be *not* laggy over a fixed period to get
// out of this state
tLastLag_ = base::GetTickCount();
if (base::GetTickCount() - tLagStart_ >= kctLagKill) {
nLagState_ = knLagKill;
}
break;
}
} else {
// Player is not behind
switch (nLagState_) {
case knLagNone:
// All is ok
break;
case knLagGrace:
// This player has "caught up" during the grace period. Assume
// there is no lag now.
nLagState_ = knLagNone;
break;
case knLagGuilty:
case knLagKill:
// Guilty of lag and yet the player has caught up. If the player
// can keep this state it will be declared not laggy
if (base::GetTickCount() - tLastLag_ >= kctLagRedemption) {
nLagState_ = knLagNone;
break;
}
}
}
return nLagState_;
}
void Player::AddLatencyRecord(LatencyRecord *platr) {
memmove(&alatr_[1], &alatr_[0], sizeof(alatr_) - sizeof(alatr_[0]));
alatr_[0] = *platr;
clatr_++;
if (clatr_ > ARRAYSIZE(alatr_)) {
clatr_ = ARRAYSIZE(alatr_);
}
updates_ = platr->cUpdatesBlock;
}
int Player::GetLatencyRecordCount() {
return clatr_;
}
const LatencyRecord *Player::GetLatencyRecord(int i) {
if (i < 0 || i >= clatr_) {
return NULL;
}
return &alatr_[i];
}
void Player::SaveWinStats(const WinStats& ws, bool lock) {
// Ignore if already locked
if (ws_.ff & kfwsLocked) {
return;
}
// Otherwise keep as most recent. Filter out flags from these win stats,
// since these came from the client. Flags will be added in later, when
// these results get posted.
ws_ = ws;
ws_.ff &= (kfwsWinner | kfwsLoser);
if (lock) {
ws_.ff |= kfwsLocked;
}
havestats_ = true;
}
void Player::GetPlayerStats(PlayerStats *ps) {
// Players can be human or computer. Humans can be in a game, and leave
// before the game ends. Losers are given a chance to dispute winners.
// Clients may or may not have sent win stats.
memset(ps, 0, sizeof(*ps));
strncpyz(ps->name, name_, sizeof(ps->name));
ps->pid = pid_;
address_.IPAsString(ps->ip, sizeof(ps->ip));
strncpyz(ps->did, did_, sizeof(ps->did));
strncpyz(ps->platform, platform_, sizeof(ps->platform));
// If this player was never used, then there are no stats for it
if (!(wf_ & kfPlrInUse)) {
return;
}
// Return stats that exist, if any
if (havestats_) {
ps->ws = ws_;
ps->ws.ff |= kfwsReceivedStats;
}
// Humans can join, and then leave. When they leave, the player turns
// into a computer, with kfPlrComputer set. kfPlrHumanJoined means this
// player started out human (even if the player didn't stay till the end
// of the game).
if (wf_ & kfPlrHumanJoined) {
ps->ws.ff |= kfwsHuman;
} else {
ps->ws.ff |= kfwsComputer;
}
// Remember if the player is anonymous.
if (anonymous_) {
ps->ws.ff |= kfwsAnonymous;
}
#if 0
// not implemented
// Winner/Loser is already encoded in WinStats.
// If this player challenged the winner, note it
if (wf_ & kfPlrChallengeWinner) {
ps->ws.ff |= kfwsWinChallenger;
}
#endif
// If this player was removed at game start, remember that since the
// stats are invalid.
if (wf_ & kfPlrRemovedAtGameStart) {
ps->ws.ff |= kfwsRemovedAtGameStart;
}
}
} // namespace wi