From d6b301c3e7cc43a61a6b3e66327df3a0db2db1b5 Mon Sep 17 00:00:00 2001 From: Nathan Fulton Date: Wed, 31 Aug 2016 23:54:02 -0400 Subject: [PATCH] Track client platform version Client platform version can be retrieved live with /ids and is also recorded in the playerdetail module. --- game/Shell.cpp | 9 +- game/chooseserverform.cpp | 9 +- game/ht.h | 1 + game/iphone/host.cpp | 4 + game/iphone/iphone.h | 1 + game/iphone/iphone.mm | 20 +++ game/loginhandler.cpp | 5 +- game/sdl/android/jni/ht/hosthelpers.cpp | 7 + .../src/com/spiffcode/ht/NativeLib.java | 4 + game/sdl/host.cpp | 4 + game/sdl/hosthelpers.h | 1 + game/sdl/ios/hosthelpers.mm | 4 + game/sdl/ios/iphone.h | 1 + game/sdl/ios/iphone.mm | 5 + game/sdl/mac/hosthelpers.mm | 8 + game/xtransport.cpp | 2 +- mpshared/messages.h | 2 +- mpshared/mpht.h | 1 + mpshared/xmsg.h | 144 ++++++++++++++++++ mpshared/xpump.cpp | 4 +- mpshared/xpump.h | 3 +- server/endpoint.cpp | 8 +- server/endpoint.h | 5 +- server/game.cpp | 5 +- server/lobby.cpp | 4 +- server/player.cpp | 3 + server/player.h | 3 + server/room.cpp | 4 +- server/statsposter.cpp | 1 + stats/auth.py | 13 +- stats/gamestats.py | 22 ++- stats/leaderboard.py | 3 +- stats/models.py | 1 + stats/playerdetail.py | 6 +- stats/serverinfo.py | 11 +- 35 files changed, 288 insertions(+), 40 deletions(-) diff --git a/game/Shell.cpp b/game/Shell.cpp index d417055..36336f6 100644 --- a/game/Shell.cpp +++ b/game/Shell.cpp @@ -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; diff --git a/game/chooseserverform.cpp b/game/chooseserverform.cpp index f395d3d..4ef78c2 100644 --- a/game/chooseserverform.cpp +++ b/game/chooseserverform.cpp @@ -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; diff --git a/game/ht.h b/game/ht.h index 0a58ce8..502e40b 100644 --- a/game/ht.h +++ b/game/ht.h @@ -8822,6 +8822,7 @@ IChatController *HostGetChatController(); void HostInitiateWebView(const char *title, const char *url); const char *HostGenerateDeviceId(); +const char *HostGetPlatformString(); // Date diff --git a/game/iphone/host.cpp b/game/iphone/host.cpp index 3de6f4c..0e1af92 100644 --- a/game/iphone/host.cpp +++ b/game/iphone/host.cpp @@ -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); } diff --git a/game/iphone/iphone.h b/game/iphone/iphone.h index 2fd4a97..789776b 100644 --- a/game/iphone/iphone.h +++ b/game/iphone/iphone.h @@ -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); diff --git a/game/iphone/iphone.mm b/game/iphone/iphone.mm index 9a9400c..3069d21 100644 --- a/game/iphone/iphone.mm +++ b/game/iphone/iphone.mm @@ -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]; diff --git a/game/loginhandler.cpp b/game/loginhandler.cpp index f41d7e2..93322cc 100644 --- a/game/loginhandler.cpp +++ b/game/loginhandler.cpp @@ -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]; diff --git a/game/sdl/android/jni/ht/hosthelpers.cpp b/game/sdl/android/jni/ht/hosthelpers.cpp index 7d338f9..ba04143 100644 --- a/game/sdl/android/jni/ht/hosthelpers.cpp +++ b/game/sdl/android/jni/ht/hosthelpers.cpp @@ -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..."); diff --git a/game/sdl/android/src/com/spiffcode/ht/NativeLib.java b/game/sdl/android/src/com/spiffcode/ht/NativeLib.java index 6168692..528c545 100644 --- a/game/sdl/android/src/com/spiffcode/ht/NativeLib.java +++ b/game/sdl/android/src/com/spiffcode/ht/NativeLib.java @@ -118,4 +118,8 @@ public class NativeLib { static String getAskString() { return askString; } + + static String getPlatformString() { + return "Android " + android.os.Build.VERSION.RELEASE; + } } diff --git a/game/sdl/host.cpp b/game/sdl/host.cpp index 29904b9..d26b534 100644 --- a/game/sdl/host.cpp +++ b/game/sdl/host.cpp @@ -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); } diff --git a/game/sdl/hosthelpers.h b/game/sdl/hosthelpers.h index 20d2d2a..b0b36d9 100644 --- a/game/sdl/hosthelpers.h +++ b/game/sdl/hosthelpers.h @@ -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); diff --git a/game/sdl/ios/hosthelpers.mm b/game/sdl/ios/hosthelpers.mm index cf446cc..15782ce 100644 --- a/game/sdl/ios/hosthelpers.mm +++ b/game/sdl/ios/hosthelpers.mm @@ -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 *)""); diff --git a/game/sdl/ios/iphone.h b/game/sdl/ios/iphone.h index 7268dd2..1400bc5 100644 --- a/game/sdl/ios/iphone.h +++ b/game/sdl/ios/iphone.h @@ -20,4 +20,5 @@ + (void)presentView:(UIView *)view; - (void)forceDeviceIntoLandscape; - (int)deviceOS; +- (NSString *)getPlatformString; @end diff --git a/game/sdl/ios/iphone.mm b/game/sdl/ios/iphone.mm index f7b0268..2b69ad5 100644 --- a/game/sdl/ios/iphone.mm +++ b/game/sdl/ios/iphone.mm @@ -146,4 +146,9 @@ - (int)deviceOS { return DEVICE_OS; } + +- (NSString *)getPlatformString { + return [NSString stringWithFormat:@"iOS %@", [[UIDevice currentDevice] systemVersion]]; +} + @end diff --git a/game/sdl/mac/hosthelpers.mm b/game/sdl/mac/hosthelpers.mm index 72b3308..bc5f7ae 100644 --- a/game/sdl/mac/hosthelpers.mm +++ b/game/sdl/mac/hosthelpers.mm @@ -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 *)""); diff --git a/game/xtransport.cpp b/game/xtransport.cpp index 0fa543f..93d59f9 100644 --- a/game/xtransport.cpp +++ b/game/xtransport.cpp @@ -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"; diff --git a/mpshared/messages.h b/mpshared/messages.h index 215d43e..02a62b6 100644 --- a/mpshared/messages.h +++ b/mpshared/messages.h @@ -148,7 +148,7 @@ typedef XMsg2 XMsgHandshake; typedef XMsg2 XMsgHandshakeResult; typedef XMsg0 XMsgEcho; typedef XMsg1 XMsgProtocolError; -typedef XMsgS3 XMsgLogin; +typedef XMsgS4 XMsgLogin; typedef XMsg1 XMsgLoginResult; typedef XMsg0 XMsgSignOut; typedef XMsg1 XMsgSignOutResult; diff --git a/mpshared/mpht.h b/mpshared/mpht.h index 1bf87d2..9aabacb 100644 --- a/mpshared/mpht.h +++ b/mpshared/mpht.h @@ -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 diff --git a/mpshared/xmsg.h b/mpshared/xmsg.h index a1c8ca5..cd53049 100644 --- a/mpshared/xmsg.h +++ b/mpshared/xmsg.h @@ -610,6 +610,150 @@ struct XMsgS3 : public XMsg } }; +const dword XMSGSIZE_STRING4_FIXED = XMSGSIZE_FIXED + sizeof(word) * 4; + +template +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 *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(s0, s1, s2, s3); + } +}; + const dword XMSGSIZE_DWORDSTRING_FIXED = XMSGSIZE_FIXED + sizeof(dword) + sizeof(word); diff --git a/mpshared/xpump.cpp b/mpshared/xpump.cpp index 1ae547e..792b778 100644 --- a/mpshared/xpump.cpp +++ b/mpshared/xpump.cpp @@ -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; diff --git a/mpshared/xpump.h b/mpshared/xpump.h index 2c4dc50..3a83fdf 100644 --- a/mpshared/xpump.h +++ b/mpshared/xpump.h @@ -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(); } diff --git a/server/endpoint.cpp b/server/endpoint.cpp index 6a2384e..4579b68 100644 --- a/server/endpoint.cpp +++ b/server/endpoint.cpp @@ -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 diff --git a/server/endpoint.h b/server/endpoint.h index f6a0ad4..aee9110 100644 --- a/server/endpoint.h +++ b/server/endpoint.h @@ -68,6 +68,7 @@ public: const char *did() { return did_; } dword roomid() { return roomid_; } dword gameid(); + const char *platform() { return platform_; } std::vector& 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) diff --git a/server/game.cpp b/server/game.cpp index a516d4b..3bdc280 100644 --- a/server/game.cpp +++ b/server/game.cpp @@ -891,8 +891,9 @@ std::vector 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))); diff --git a/server/lobby.cpp b/server/lobby.cpp index b99a584..b886cab 100644 --- a/server/lobby.cpp +++ b/server/lobby.cpp @@ -242,10 +242,10 @@ std::vector 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(), diff --git a/server/player.cpp b/server/player.cpp index 997c546..e80f104 100644 --- a/server/player.cpp +++ b/server/player.cpp @@ -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)) { diff --git a/server/player.h b/server/player.h index 62cdd4f..6b6529e 100644 --- a/server/player.h +++ b/server/player.h @@ -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 diff --git a/server/room.cpp b/server/room.cpp index 7cce060..bce50cc 100644 --- a/server/room.cpp +++ b/server/room.cpp @@ -81,10 +81,10 @@ std::vector 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(), diff --git a/server/statsposter.cpp b/server/statsposter.cpp index 9aeec52..15d934f 100644 --- a/server/statsposter.cpp +++ b/server/statsposter.cpp @@ -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)); diff --git a/stats/auth.py b/stats/auth.py index c9c0052..460e387 100644 --- a/stats/auth.py +++ b/stats/auth.py @@ -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 diff --git a/stats/gamestats.py b/stats/gamestats.py index c0d6c9f..7029610 100644 --- a/stats/gamestats.py +++ b/stats/gamestats.py @@ -33,6 +33,9 @@ AddGameStat json: { "name": "", "pid": , + "ip": "", + "did": "", + "platform": "", "winstats": { "side_mask": , "side_mask_allies": , @@ -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 diff --git a/stats/leaderboard.py b/stats/leaderboard.py index 09ab115..73d4eec 100644 --- a/stats/leaderboard.py +++ b/stats/leaderboard.py @@ -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 diff --git a/stats/models.py b/stats/models.py index ec00b52..ac8b373 100644 --- a/stats/models.py +++ b/stats/models.py @@ -106,6 +106,7 @@ class PlayerActionModel(db.Model): ip_address = db.StringProperty() action = db.StringProperty() time_utc = db.IntegerProperty() + platform = db.StringProperty() # +++ diff --git a/stats/playerdetail.py b/stats/playerdetail.py index 253483d..4ef1371 100644 --- a/stats/playerdetail.py +++ b/stats/playerdetail.py @@ -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 diff --git a/stats/serverinfo.py b/stats/serverinfo.py index 162f344..76b6de8 100644 --- a/stats/serverinfo.py +++ b/stats/serverinfo.py @@ -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