Track client platform version

Client platform version can be retrieved live with /ids and is also
recorded in the playerdetail module.
This commit is contained in:
Nathan Fulton 2016-08-31 23:54:02 -04:00
parent 87e212c2c9
commit d6b301c3e7
35 changed files with 288 additions and 40 deletions

View File

@ -17,15 +17,16 @@ public:
if (idc == kidcLeaderboard) {
LoginHandler handler;
std::string d = base::StringEncoder::QueryEncode(gszDeviceId);
std::string o(base::StringEncoder::QueryEncode(HostGetPlatformString()));
const char *url;
if (strlen(handler.StatsUsername()) == 0) {
url = base::Format::ToString("%s?d=%s", kszLeaderboardUrl,
d.c_str());
url = base::Format::ToString("%s?d=%s&o=%s", kszLeaderboardUrl,
d.c_str(), o.c_str());
} else {
std::string q = base::StringEncoder::QueryEncode(
handler.StatsUsername());
url = base::Format::ToString("%s?p=%s&d=%s", kszLeaderboardUrl,
q.c_str(), d.c_str());
url = base::Format::ToString("%s?p=%s&d=%s&o=%s", kszLeaderboardUrl,
q.c_str(), d.c_str(), o.c_str());
}
HostInitiateWebView("Hostile Takeover Statistics", url);
return;

View File

@ -370,17 +370,18 @@ std::string ChooseServerForm::GetServiceUrl() {
// protocol version
std::string deviceid(base::StringEncoder::QueryEncode(gszDeviceId));
std::string os(base::StringEncoder::QueryEncode(HostGetPlatformString()));
const char *url;
LoginHandler handler;
if (handler.anonymous()) {
url = base::Format::ToString("%s?x=%d&d=%s", kszServerInfoUrl,
kdwProtocolCurrent, deviceid.c_str());
url = base::Format::ToString("%s?x=%d&d=%s&o=%s", kszServerInfoUrl,
kdwProtocolCurrent, deviceid.c_str(), os.c_str());
} else {
std::string player(base::StringEncoder::QueryEncode(
handler.username()));
url = base::Format::ToString("%s?x=%d&p=%s&d=%s", kszServerInfoUrl,
kdwProtocolCurrent, player.c_str(), deviceid.c_str());
url = base::Format::ToString("%s?x=%d&p=%s&d=%s&o=%s", kszServerInfoUrl,
kdwProtocolCurrent, player.c_str(), deviceid.c_str(), os.c_str());
}
return url;

View File

@ -8822,6 +8822,7 @@ IChatController *HostGetChatController();
void HostInitiateWebView(const char *title, const char *url);
const char *HostGenerateDeviceId();
const char *HostGetPlatformString();
// Date

View File

@ -54,6 +54,10 @@ const char *HostGenerateDeviceId() {
return base::Format::ToHex(hash, 16);
}
const char *HostGetPlatformString() {
return IPhone::GetPlatformString();
}
void HostInitiateWebView(const char *title, const char *url) {
return IPhone::InitiateWebView(title, url);
}

View File

@ -46,6 +46,7 @@ public:
static const char *GetTempDir();
static const char *GetCompletesDir();
static const char *GetStaticUUID();
static const char *GetPlatformString();
static void InitiateAsk(const char *title, int max, const char *def,
int keyboard, bool secure);
static void GetAskString(char *psz, int cb);

View File

@ -34,6 +34,7 @@
char *m_pszTempDir;
char *m_pszCompletesDir;
char *m_pszUUID;
char *m_pszPlatform;
}
@end
@ -193,6 +194,15 @@ IPhoneAppDelegate *g_appDelegate;
strcpy(m_pszUUID, pszUUID);
CFRelease(strUUID);
// Get the device platform string
NSString *strPlatform = [NSString stringWithFormat:@"iOS %@",
[[UIDevice currentDevice] systemVersion]];
const char *pszPlatform = [strPlatform cStringUsingEncoding:
[NSString defaultCStringEncoding]];
m_pszPlatform = (char *)malloc(strlen(pszPlatform) + 1);
strcpy(m_pszPlatform, pszPlatform);
CFRelease(strPlatform);
// Set up a notification to restore sound when interrupted
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(handleAudioInterruption:)
@ -255,6 +265,11 @@ IPhoneAppDelegate *g_appDelegate;
return m_pszUUID;
}
- (const char *)platformString
{
return m_pszPlatform;
}
- (bool)isExiting
{
return m_fExiting;
@ -463,6 +478,11 @@ const char *IPhone::GetStaticUUID()
return [g_appDelegate staticUUID];
}
const char *IPhone::GetPlatformString()
{
return [g_appDelegate platformString];
}
bool IPhone::IsExiting()
{
return [g_appDelegate isExiting];

View File

@ -178,7 +178,10 @@ dword LoginHandler::GetToken() {
// Submit a simple blocking request to get the token
SimpleRequest req(gphttp);
const char *url = base::Format::ToString("%s?a=%s&d=%s", kszAuthUrl, output, gszDeviceId);
std::string d = base::StringEncoder::QueryEncode(gszDeviceId);
std::string o(base::StringEncoder::QueryEncode(HostGetPlatformString()));
const char *url = base::Format::ToString("%s?a=%s&d=%s&o=%s", kszAuthUrl, output,
d.c_str(), o.c_str());
req.SetTimeout(60);
char result[kcbTokenMax];

View File

@ -23,6 +23,7 @@ static jmethodID getAndroidIDMethod;
static jmethodID initiateAskMethod;
static jmethodID initiateWebViewMethod;
static jmethodID getAskStringMethod;
static jmethodID getPlatformStringMethod;
namespace wi {
@ -71,6 +72,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
initiateWebViewMethod = env->GetStaticMethodID(NativeLibClass, "initiateWebView", "(Ljava/lang/String;)V");
getAskStringMethod = env->GetStaticMethodID(NativeLibClass, "getAskString", "()Ljava/lang/String;");
initiateAskMethod = env->GetStaticMethodID(NativeLibClass, "initiateAsk", "(Ljava/lang/String;ILjava/lang/String;II)V");
getPlatformStringMethod = env->GetStaticMethodID(NativeLibClass, "getPlatformString", "()Ljava/lang/String;");
return JNI_VERSION_1_4;
}
@ -335,6 +337,11 @@ void HostHelpers::InitiateWebView(const char *title, const char *url) {
g_env->CallStaticVoidMethod(NativeLibClass, initiateWebViewMethod, jstrUrl);
}
const char *HostHelpers::GetPlatformString() {
jobject jstr = g_env->CallStaticObjectMethod(NativeLibClass, getPlatformStringMethod);
return g_env->GetStringUTFChars((jstring)jstr, NULL);
}
/*
void HostHelpers::GameThreadStart(void *pv) {
Log("Starting game...");

View File

@ -118,4 +118,8 @@ public class NativeLib {
static String getAskString() {
return askString;
}
static String getPlatformString() {
return "Android " + android.os.Build.VERSION.RELEASE;
}
}

View File

@ -67,6 +67,10 @@ const char *HostGenerateDeviceId() {
return base::Format::ToHex(hash, 16);
}
const char *HostGetPlatformString() {
return HostHelpers::GetPlatformString();
}
void HostInitiateWebView(const char *title, const char *url) {
return HostHelpers::InitiateWebView(title, url);
}

View File

@ -51,6 +51,7 @@ public:
static bool IsExiting();
static void GameThreadStart(void *pv);
static void DisplayInitComplete();
static const char *GetPlatformString();
// TODO(darrinm): unused?
static int main(int argc, char **argv);

View File

@ -261,6 +261,10 @@ void HostHelpers::InitiateWebView(const char *title, const char *url) {
[iphone initiateWebView:[NSString stringWithUTF8String:url] title:[NSString stringWithUTF8String:title]];
}
const char *HostHelpers::GetPlatformString() {
return [[iphone getPlatformString] cStringUsingEncoding:NSASCIIStringEncoding];
}
void HostHelpers::GameThreadStart(void *pv) {
Log("Starting game...");
wi::GameMain((char *)"");

View File

@ -20,4 +20,5 @@
+ (void)presentView:(UIView *)view;
- (void)forceDeviceIntoLandscape;
- (int)deviceOS;
- (NSString *)getPlatformString;
@end

View File

@ -146,4 +146,9 @@
- (int)deviceOS {
return DEVICE_OS;
}
- (NSString *)getPlatformString {
return [NSString stringWithFormat:@"iOS %@", [[UIDevice currentDevice] systemVersion]];
}
@end

View File

@ -246,6 +246,14 @@ void HostHelpers::InitiateWebView(const char *title, const char *url) {
HostHelpers::OpenUrl(url);
}
const char *HostHelpers::GetPlatformString() {
NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
NSString *versionString = [NSString stringWithFormat:@"MacOS %ld.%ld.%ld",
version.majorVersion, version.minorVersion, version.patchVersion];
return [versionString cStringUsingEncoding:NSASCIIStringEncoding];
}
void HostHelpers::GameThreadStart(void *pv) {
Log("Starting game...");
wi::GameMain((char *)"");

View File

@ -268,7 +268,7 @@ dword XTransport::Login(const char *username, const char *token) {
// Login and wait synchronously for reply.
SetState(XTS_LOGGINGIN);
xpump_.Send(XMsgLogin::ToBuffer(username, token, gszDeviceId));
xpump_.Send(XMsgLogin::ToBuffer(username, token, gszDeviceId, HostGetPlatformString()));
if (!WaitForStateChange()) {
LOG() << "Timed out waiting for Login";

View File

@ -148,7 +148,7 @@ typedef XMsg2<XMSG_HANDSHAKE> XMsgHandshake;
typedef XMsg2<XMSG_HANDSHAKERESULT> XMsgHandshakeResult;
typedef XMsg0<XMSG_ECHO> XMsgEcho;
typedef XMsg1<XMSG_PROTOCOLERROR> XMsgProtocolError;
typedef XMsgS3<XMSG_LOGIN, kcbUsernameMax, kcbTokenMax, kcbDidMax> XMsgLogin;
typedef XMsgS4<XMSG_LOGIN, kcbUsernameMax, kcbTokenMax, kcbDidMax, kcbPlatformMax> XMsgLogin;
typedef XMsg1<XMSG_LOGINRESULT> XMsgLoginResult;
typedef XMsg0<XMSG_SIGNOUT> XMsgSignOut;
typedef XMsg1<XMSG_SIGNOUTRESULT> XMsgSignOutResult;

View File

@ -50,6 +50,7 @@ const int kcbTokenMax = 256;
const int kcbDidMax = 34;
const int kcbChatMax = 1024;
const int kcbShowMessageMax = 512;
const int kcbPlatformMax = 32;
// Side masks

View File

@ -610,6 +610,150 @@ struct XMsgS3 : public XMsg
}
};
const dword XMSGSIZE_STRING4_FIXED = XMSGSIZE_FIXED + sizeof(word) * 4;
template <dword ID, int SIZE0, int SIZE1, int SIZE2, int SIZE3>
struct XMsgS4 : public XMsg
{
XMsgS4(const char *s0, const char *s1, const char *s2, const char *s3) : XMsg(ID) {
strncpyz(s0_, s0, sizeof(s0_));
strncpyz(s1_, s1, sizeof(s1_));
strncpyz(s2_, s2, sizeof(s2_));
strncpyz(s3_, s3, sizeof(s3_));
}
char s0_[SIZE0];
char s1_[SIZE1];
char s2_[SIZE2];
char s3_[SIZE3];
#ifdef LOGGING
virtual std::string ToString() {
std::ostringstream ss;
ss << XMsgLabels.Find(id_) << ", s0: " << s0_ << ", s1: " << s1_ <<
", s2: " << s2_ << ", s3: " << s3_ << std::endl;
return ss.str();
}
#endif
static base::ByteBuffer *ToBuffer(const char *s0, const char *s1,
const char *s2, const char *s3) {
word cbS0 = strlen(s0) + 1;
char s0T[SIZE0];
if (cbS0 > sizeof(s0T)) {
strncpyz(s0T, s0, sizeof(s0T));
s0 = s0T;
cbS0 = sizeof(s0T);
}
word cbS1 = strlen(s1) + 1;
char s1T[SIZE1];
if (cbS1 > sizeof(s1T)) {
strncpyz(s1T, s1, sizeof(s1T));
s1 = s1T;
cbS1 = sizeof(s1T);
}
word cbS2 = strlen(s2) + 1;
char s2T[SIZE2];
if (cbS2 > sizeof(s2T)) {
strncpyz(s2T, s2, sizeof(s2T));
s2 = s2T;
cbS2 = sizeof(s2T);
}
word cbS3 = strlen(s3) + 1;
char s3T[SIZE3];
if (cbS3 > sizeof(s3T)) {
strncpyz(s3T, s3, sizeof(s3T));
s3 = s3T;
cbS3 = sizeof(s3T);
}
dword cb = XMSGSIZE_STRING4_FIXED + cbS0 + cbS1 + cbS2 + cbS3;
base::ByteBuffer *bb = new base::ByteBuffer(cb);
bb->WriteWord(cb);
bb->WriteByte(ID);
bb->WriteWord(cbS0);
bb->WriteBytes((const byte *)s0, cbS0);
bb->WriteWord(cbS1);
bb->WriteBytes((const byte *)s1, cbS1);
bb->WriteWord(cbS2);
bb->WriteBytes((const byte *)s2, cbS2);
bb->WriteWord(cbS3);
bb->WriteBytes((const byte *)s3, cbS3);
if (bb->Length() != cb) {
delete bb;
return NULL;
}
return bb;
}
static XMsgS4<ID, SIZE0, SIZE1, SIZE2, SIZE3> *FromBuffer(base::ByteBuffer& bb,
dword cb) {
dword cbSav = bb.Length();
word w;
if (!bb.ReadWord(&w)) {
return NULL;
}
if (cb != (dword)w || w < XMSGSIZE_STRING4_FIXED ||
w > XMSGSIZE_STRING4_FIXED + SIZE0 + SIZE1 + SIZE2 + SIZE3) {
Assert();
return NULL;
}
if (w - sizeof(word) > bb.Length()) {
return NULL;
}
byte id = 0;
bb.ReadByte(&id);
if (id != ID) {
return NULL;
}
word cbS0 = 0;
bb.ReadWord(&cbS0);
if (cbS0 > SIZE0) {
Assert();
return NULL;
}
char s0[SIZE0];
bb.ReadBytes((byte *)s0, cbS0);
s0[cbS0 - 1] = 0;
word cbS1 = 0;
bb.ReadWord(&cbS1);
if (cbS1 > SIZE1) {
Assert();
return NULL;
}
char s1[SIZE1];
bb.ReadBytes((byte *)s1, cbS1);
s1[cbS1 - 1] = 0;
word cbS2 = 0;
bb.ReadWord(&cbS2);
if (cbS2 > SIZE2) {
Assert();
return NULL;
}
char s2[SIZE2];
bb.ReadBytes((byte *)s2, cbS2);
s2[cbS2 - 1] = 0;
word cbS3 = 0;
bb.ReadWord(&cbS3);
if (cbS3 > SIZE3) {
Assert();
return NULL;
}
char s3[SIZE3];
bb.ReadBytes((byte *)s3, cbS3);
s3[cbS3 - 1] = 0;
if (cbSav - bb.Length() != w) {
Assert();
return NULL;
}
return new XMsgS4<ID, SIZE0, SIZE1, SIZE2, SIZE3>(s0, s1, s2, s3);
}
};
const dword XMSGSIZE_DWORDSTRING_FIXED = XMSGSIZE_FIXED + sizeof(dword) +
sizeof(word);

View File

@ -378,8 +378,8 @@ void XPump::DispatchXMsg(XMsg *pmsg) {
case XMSG_LOGIN:
{
XMsgLogin *pmsgT = (XMsgLogin *)pmsg;
// username, token, did
notify_->OnLogin(pmsgT->s0_, pmsgT->s1_, pmsgT->s2_);
// username, token, did, platform
notify_->OnLogin(pmsgT->s0_, pmsgT->s1_, pmsgT->s2_, pmsgT->s3_);
}
break;

View File

@ -20,7 +20,8 @@ public:
bool disconnect) { Assert(); }
virtual void OnEcho() { Assert(); }
virtual void OnProtocolError(dword error) { Assert(); }
virtual void OnLogin(const char *username, const char *token, const char *did) { Assert(); }
virtual void OnLogin(const char *username, const char *token, const char *did,
const char *platform) { Assert(); }
virtual void OnLoginResult(dword loginResult) { Assert(); }
virtual void OnSignOut() { Assert(); }
virtual void OnSignOutResult(dword result) { Assert(); }

View File

@ -103,8 +103,10 @@ void Endpoint::OnHandshake(dword clientid, dword protocolid) {
okecho_ = true;
}
void Endpoint::OnLogin(const char *username, const char *token, const char *did) {
LOG() << "username: " << username << " token: " << token << " did: " << did;
void Endpoint::OnLogin(const char *username, const char *token,
const char *did, const char *platform) {
LOG() << "username: " << username << " token: " << token << " did: " << did
<< " platform: " << platform;
if (!CheckState(ES_HANDSHAKESUCCESS)) {
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultFail));
@ -130,6 +132,7 @@ void Endpoint::OnLogin(const char *username, const char *token, const char *did)
UpdateDid(did);
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultAnonymousSuccess));
anonymous_ = true;
strncpyz(platform_, platform, sizeof(platform_));
SetState(ES_READY);
return;
}
@ -172,6 +175,7 @@ void Endpoint::OnLogin(const char *username, const char *token, const char *did)
// Success. Transition to ES_READY
xpump_.Send(XMsgLoginResult::ToBuffer(knLoginResultSuccess));
anonymous_ = false;
strncpyz(platform_, platform, sizeof(platform_));
SetState(ES_READY);
// Moderators see unfiltered chat by default

View File

@ -68,6 +68,7 @@ public:
const char *did() { return did_; }
dword roomid() { return roomid_; }
dword gameid();
const char *platform() { return platform_; }
std::vector<std::string>& old_names() { return old_names_; }
@ -88,7 +89,8 @@ private:
// XPumpNotify overrides
virtual void OnHandshake(dword clientid, dword protocolid);
virtual void OnEcho();
virtual void OnLogin(const char *username, const char *token, const char *did);
virtual void OnLogin(const char *username, const char *token,
const char *did, const char *platform);
virtual void OnSignOut();
virtual void OnLobbyJoin();
virtual void OnLobbyCreateRoom(const char *name, const char *password);
@ -134,6 +136,7 @@ private:
std::string chat_fragment_;
TokenBucket roomlimiter_;
char did_[64];
char platform_[32];
};
STARTLABEL(EsLabels)

View File

@ -891,8 +891,9 @@ std::vector<std::string> Game::GetIdsString(Endpoint *endpoint) {
char ip[32];
(*it)->xpump().socket()->GetRemoteAddress().IPAsString(ip,
sizeof(ip));
responses.push_back(base::Format::ToString("%s: id %d ip %s",
(*it)->name(), server_.GetChatterId(endpoint, *it), ip));
responses.push_back(base::Format::ToString("%s: id %d ip %s os %s",
(*it)->name(), server_.GetChatterId(endpoint, *it), ip,
(*it)->platform()));
} else {
responses.push_back(base::Format::ToString("%s: id %d",
(*it)->name(), server_.GetChatterId(endpoint, *it)));

View File

@ -242,10 +242,10 @@ std::vector<std::string> Lobby::GetIdsString(Endpoint *endpoint) {
char ip[32];
it->second->xpump().socket()->
GetRemoteAddress().IPAsString(ip, sizeof(ip));
responses.push_back(base::Format::ToString("%s: id %d ip %s",
responses.push_back(base::Format::ToString("%s: id %d ip %s os %s",
it->second->name(),
it->second->server().GetChatterId(endpoint, it->second),
ip));
ip, it->second->platform()));
} else {
responses.push_back(base::Format::ToString("%s: id %d",
it->second->name(),

View File

@ -33,6 +33,7 @@ void Player::Init(Pid pid) {
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) {
@ -44,6 +45,7 @@ void Player::SetEndpoint(Endpoint *endpoint) {
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;
@ -184,6 +186,7 @@ void Player::GetPlayerStats(PlayerStats *ps) {
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)) {

View File

@ -15,6 +15,7 @@ struct PlayerStats {
Pid pid;
char ip[32];
char did[64];
char platform[32];
WinStats ws;
};
@ -53,6 +54,7 @@ public:
void ClearLatencyRecords() { clatr_ = 0; }
const LatencyRecord *GetLatencyRecord(int i);
void SetDid(const char *did) { strncpyz(did_, did, sizeof(did_)); }
void SetPlatform(const char *platform) { strncpyz(platform_, platform, sizeof(platform_)); }
long updates() { return updates_; }
bool havestats() { return havestats_; }
@ -83,6 +85,7 @@ private:
bool anonymous_;
char did_[64];
SideMask allies_;
char platform_[32];
};
} // namespace wi

View File

@ -81,10 +81,10 @@ std::vector<std::string> Room::GetIdsString(Endpoint *endpoint) {
char ip[32];
it->second->xpump().socket()->
GetRemoteAddress().IPAsString(ip, sizeof(ip));
responses.push_back(base::Format::ToString("%s: id %d ip %s",
responses.push_back(base::Format::ToString("%s: id %d ip %s os %s",
it->second->name(),
it->second->server().GetChatterId(endpoint, it->second),
ip));
ip, it->second->platform()));
} else {
responses.push_back(base::Format::ToString("%s: id %d",
it->second->name(),

View File

@ -180,6 +180,7 @@ std::string StatsPoster::ToJson(const GameStats& s) {
GenNum(g, "pid", s.player_stats[i].pid);
GenString(g, "ip", s.player_stats[i].ip);
GenString(g, "did", s.player_stats[i].did);
GenString(g, "platform", s.player_stats[i].platform);
str = "winstats";
yajl_gen_string(g, (const unsigned char *)str, strlen(str));

View File

@ -23,22 +23,23 @@ class AuthUser(webapp.RequestHandler):
password = ''
t = None
try:
username,password,did = self.get_username_password_did()
username,password,did,platform = self.get_username_password_did_platform()
if self.authenticate(username, password, did):
t = self.generate_token(username, config.AUTH_GOOD_SECRET)
self.save_action(username, did)
self.save_action(username, did, platform)
else:
t = self.generate_token(username, config.AUTH_BAD_SECRET)
except:
t = self.generate_token(username, config.AUTH_BAD_SECRET)
self.response.out.write(t)
def get_username_password_did(self):
def get_username_password_did_platform(self):
d = base64.b64decode(self.request.get('a'))
username = d[0:d.find('\0')]
password = d[d.find('\0')+1:]
did = self.request.get('d')
return username,password,did
platform = self.request.get('o')
return username,password,did,platform
def generate_token(self, username, secret):
a = {}
@ -55,12 +56,12 @@ class AuthUser(webapp.RequestHandler):
return False
return m.password.lower() == md5(password).hexdigest().lower()
def save_action(self, username, did):
def save_action(self, username, did, platform):
try:
player_name = username
anonymous = False
ip = self.request.remote_addr
action = dict(action='auth')
playerdetail.save(player_name, anonymous, did, ip, action)
playerdetail.save(player_name, anonymous, did, ip, action, platform)
except:
pass

View File

@ -33,6 +33,9 @@ AddGameStat json:
{
"name": "<string>",
"pid": <integer>,
"ip": "<string>",
"did": "<string>",
"platform": "<string>",
"winstats": {
"side_mask": <integer>,
"side_mask_allies": <integer>,
@ -418,7 +421,16 @@ class GameStats(wrap.DictWrap):
dids[player_stat.name] = ''
return dids
def save_actions(self, dids):
def lookup_platforms(self):
platforms = {}
for player_stat in self.player_stats:
if 'platform' in player_stat.__dict__ and len(player_stat.platform) != 0:
platforms[player_stat.name] = player_stat.platform
continue
platforms[player_stat.name] = ''
return platforms
def save_actions(self, dids, platforms):
# For every legit player, add a player action
for player_stat in self.player_stats:
if player_stat.winstats.ff & kfwsComputer:
@ -428,12 +440,16 @@ class GameStats(wrap.DictWrap):
did = dids[player_name] if player_name in dids else ''
ip = player_stat.ip if 'ip' in player_stat.__dict__ else ''
action = dict(action='game', key=self.get_game_key_name())
playerdetail.save(player_name, anonymous, did, ip, action)
platform = platforms[player_name] if player_name in platforms else ''
playerdetail.save(player_name, anonymous, did, ip, action, platform)
def save(self, update_player_stats=True, lookup_dids=True, save_actions=True):
try:
# Lookup dids if asked
dids = self.lookup_dids() if lookup_dids else {}
# Lookup platforms
platforms = self.lookup_platforms()
# If not saved, it's already in the db.
if not db.run_in_transaction(self.add_txn, dids):
@ -441,7 +457,7 @@ class GameStats(wrap.DictWrap):
if update_player_stats:
self.update_player_stats()
if save_actions:
self.save_actions(dids)
self.save_actions(dids, platforms)
return True
except:
# Save game in a special place for later analysis

View File

@ -102,6 +102,7 @@ class Leaderboard(basehandler.BaseHandler):
anon = False if p else True
did = self.request.get('d')
ip = self.request.remote_addr
playerdetail.save(p, anon, did, ip, dict(action='leaderboard'))
platform = self.request.get('o')
playerdetail.save(p, anon, did, ip, dict(action='leaderboard'), platform)
except:
pass

View File

@ -106,6 +106,7 @@ class PlayerActionModel(db.Model):
ip_address = db.StringProperty()
action = db.StringProperty()
time_utc = db.IntegerProperty()
platform = db.StringProperty()
# +++

View File

@ -199,7 +199,7 @@ class PlayerDetail(basehandler.BaseHandler):
history_table = {}
history_table['title'] = 'History Table'
history_table['columns'] = ['Time', 'Player', 'Did', 'Ip', 'Action']
history_table['columns'] = ['Time', 'Player', 'Did', 'Ip', 'Platform', 'Action']
history_table['rows'] = []
for r in results:
row = []
@ -207,6 +207,7 @@ class PlayerDetail(basehandler.BaseHandler):
row.append([(r.player_name, self.get_url(r.player_name))])
row.append([(r.did, self.get_url(r.did))])
row.append([(r.ip_address, self.get_url(r.ip_address))])
row.append([(r.platform, '')])
row.append([self.get_action_field(r)])
history_table['rows'].append(row)
tables.append(history_table)
@ -268,7 +269,7 @@ class PlayerDetail(basehandler.BaseHandler):
url = '%s?g=%s' % (config.GAMEDETAIL_URL, a['key'])
return a['action'], url
def save(player_name, anonymous, did, ip, action):
def save(player_name, anonymous, did, ip, action, platform):
try:
a = models.PlayerActionModel()
a.player_name = player_name.lower()
@ -277,6 +278,7 @@ def save(player_name, anonymous, did, ip, action):
a.ip_address = ip
a.action = json.dumps(action)
a.time_utc = int(time.time())
a.platform = platform
a.put()
except:
pass

View File

@ -39,17 +39,18 @@ class ServerInfo(webapp.RequestHandler):
# Get did, check for banned dids
ip = self.request.remote_addr
did = self.request.get('d')
platform = self.request.get('o')
if did and self.is_banned_did(did):
self.save_action(did, ip, True)
self.save_action(did, ip, True, platform)
return
# Check for banned ips
if ip and self.is_banned_ip(ip):
self.save_action(did, ip, True)
self.save_action(did, ip, True, platform)
return
# Not banned
self.save_action(did, ip, False)
self.save_action(did, ip, False, platform)
# Attempt to retrieve serverinfo from cache
j = memcache.get(SERVERINFO_KEY)
@ -120,12 +121,12 @@ class ServerInfo(webapp.RequestHandler):
memcache.set(SERVERINFO_KEY, j, info['expires_utc'])
return j
def save_action(self, did, ip, banned):
def save_action(self, did, ip, banned, platform):
try:
p = self.request.get('p')
anon = False if p else True
d = dict(action='serverinfo', banned=banned)
playerdetail.save(p, anon, did, ip, d)
playerdetail.save(p, anon, did, ip, d, platform)
except:
pass