From 4dd858f8997488e2252f5a04df9df1654a70d67f Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Tue, 29 Jul 2014 17:45:55 +0200 Subject: Added a cMojangAPI class for PlayerName -> UUID lookups, with cache. The cache is persisted into a SQLite DB file on server shutdown. --- src/Protocol/MojangAPI.cpp | 446 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 src/Protocol/MojangAPI.cpp (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp new file mode 100644 index 000000000..05f9c09a7 --- /dev/null +++ b/src/Protocol/MojangAPI.cpp @@ -0,0 +1,446 @@ + +// MojangAPI.cpp + +// Implements the cMojangAPI class representing the various API points provided by Mojang's webservices, and a cache for their results + +#include "Globals.h" +#include "MojangAPI.h" +#include "SQLiteCpp/Database.h" +#include "SQLiteCpp/Statement.h" +#include "inifile/iniFile.h" +#include "json/json.h" +#include "PolarSSL++/BlockingSslClientSocket.h" + + + + + +/** The maximum age for items to be kept in the cache. Any item older than this will be removed. */ +const Int64 MAX_AGE = 7 * 24 * 60 * 60; // 7 days ago + + + + + +#define DEFAULT_NAME_TO_UUID_SERVER "api.mojang.com" +#define DEFAULT_NAME_TO_UUID_ADDRESS "/profiles/minecraft" + + + + + +/** This is the data of the root certs for Starfield Technologies, the CA that signed sessionserver.mojang.com's cert: +Downloaded from http://certs.starfieldtech.com/repository/ */ +static const AString & StarfieldCACert(void) +{ + static const AString Cert( + // G2 cert + "-----BEGIN CERTIFICATE-----\n" + "MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx\n" + "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n" + "HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs\n" + "ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw\n" + "MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6\n" + "b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj\n" + "aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp\n" + "Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg\n" + "nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1\n" + "HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N\n" + "Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN\n" + "dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0\n" + "HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO\n" + "BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G\n" + "CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU\n" + "sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3\n" + "4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg\n" + "8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K\n" + "pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1\n" + "mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n" + "-----END CERTIFICATE-----\n\n" + // Original (G1) cert: + "-----BEGIN CERTIFICATE-----\n" + "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n" + "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n" + "U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw\n" + "NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE\n" + "ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp\n" + "ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3\n" + "DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf\n" + "8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN\n" + "+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0\n" + "X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa\n" + "K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA\n" + "1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G\n" + "A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR\n" + "zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0\n" + "YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD\n" + "bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w\n" + "DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3\n" + "L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D\n" + "eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl\n" + "xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp\n" + "VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY\n" + "WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=\n" + "-----END CERTIFICATE-----\n" + ); + + return Cert; +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cMojangAPI: + +cMojangAPI::cMojangAPI(void) : + m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER), + m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS) +{ +} + + + + + +cMojangAPI::~cMojangAPI() +{ + SaveCachesToDisk(); +} + + + + + +void cMojangAPI::Start(cIniFile & a_SettingsIni) +{ + m_NameToUUIDServer = a_SettingsIni.GetValueSet("Authentication", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); + m_NameToUUIDAddress = a_SettingsIni.GetValueSet("Authentication", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); + LoadCachesFromDisk(); +} + + + + + +AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_PlayerNames) +{ + // Convert all playernames to lowercase: + AStringVector PlayerNames; + for (AStringVector::const_iterator itr = a_PlayerNames.begin(), end = a_PlayerNames.end(); itr != end; ++itr) + { + AString Lower(*itr); + PlayerNames.push_back(StrToLower(Lower)); + } // for itr - a_PlayerNames[] + + // Request the cache to populate any names not yet contained: + CacheNamesToUUIDs(PlayerNames); + + // Retrieve from cache: + size_t idx = 0; + AStringVector res; + res.resize(PlayerNames.size()); + cCSLock Lock(m_CSNameToUUID); + for (AStringVector::const_iterator itr = PlayerNames.begin(), end = PlayerNames.end(); itr != end; ++itr, ++idx) + { + cNameToUUIDMap::const_iterator itrN = m_NameToUUID.find(*itr); + if (itrN != m_NameToUUID.end()) + { + res[idx] = itrN->second.m_UUID; + } + } // for itr - PlayerNames[] + return res; +} + + + + + +void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const AString & a_UUID) +{ + AString lcName(a_PlayerName); + Int64 Now = time(NULL); + cCSLock Lock(m_CSNameToUUID); + m_NameToUUID[StrToLower(lcName)] = sUUIDRecord(a_PlayerName, a_UUID, Now); +} + + + + + +bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_Request, AString & a_Response) +{ + // Connect the socket: + cBlockingSslClientSocket Socket; + Socket.SetTrustedRootCertsFromString(StarfieldCACert(), a_ServerName); + if (!Socket.Connect(a_ServerName, 443)) + { + LOGWARNING("%s: Can't connect to %s: %s", __FUNCTION__, a_ServerName.c_str(), Socket.GetLastErrorText().c_str()); + return false; + } + + if (!Socket.Send(a_Request.c_str(), a_Request.size())) + { + LOGWARNING("%s: Writing SSL data failed: %s", __FUNCTION__, Socket.GetLastErrorText().c_str()); + return false; + } + + // Read the HTTP response: + int ret; + unsigned char buf[1024]; + + for (;;) + { + ret = Socket.Receive(buf, sizeof(buf)); + + if ((ret == POLARSSL_ERR_NET_WANT_READ) || (ret == POLARSSL_ERR_NET_WANT_WRITE)) + { + // This value should never be returned, it is handled internally by cBlockingSslClientSocket + LOGWARNING("%s: SSL reading failed internally", __FUNCTION__); + return false; + } + if (ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) + { + break; + } + if (ret < 0) + { + LOGWARNING("%s: SSL reading failed: -0x%x", __FUNCTION__, -ret); + return false; + } + if (ret == 0) + { + break; + } + + a_Response.append((const char *)buf, (size_t)ret); + } + + Socket.Disconnect(); + return true; +} + + + + + +AString cMojangAPI::MakeUUIDShort(const AString & a_UUID) +{ + switch (a_UUID.size()) + { + case 32: return a_UUID; + + case 36: + { + AString res; + // TODO + return res; + } + } + LOGWARNING("%s: Not an UUID: \"%s\".", __FUNCTION__, a_UUID.c_str()); + return ""; +} + + + + + +AString cMojangAPI::MakeUUIDDashed(const AString & a_UUID) +{ + switch (a_UUID.size()) + { + case 36: return a_UUID; + + case 32: + { + AString res; + // TODO + return res; + } + } + LOGWARNING("%s: Not an UUID: \"%s\".", __FUNCTION__, a_UUID.c_str()); + return ""; +} + + + + + +void cMojangAPI::LoadCachesFromDisk(void) +{ + try + { + // Open up the SQLite DB: + SQLite::Database db("NameToUUID.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + db.exec("CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)"); + + // Clean up old entries: + { + SQLite::Statement stmt(db, "DELETE FROM PlayerNameToUUID WHERE DateTime < ?"); + Int64 LimitDateTime = time(NULL) - MAX_AGE; + stmt.bind(1, LimitDateTime); + stmt.exec(); + } + + // Retrieve all remaining entries:: + SQLite::Statement stmt(db, "SELECT PlayerName, UUID, DateTime FROM PlayerNameToUUID"); + while (stmt.executeStep()) + { + AString PlayerName = stmt.getColumn(0); + AString UUID = stmt.getColumn(1); + Int64 DateTime = stmt.getColumn(2); + AString lcPlayerName = PlayerName; + m_NameToUUID[StrToLower(lcPlayerName)] = sUUIDRecord(PlayerName, UUID, DateTime); + } + } + catch (const SQLite::Exception & ex) + { + LOGINFO("Loading MojangAPI cache failed: %s", ex.what()); + } +} + + + + + +void cMojangAPI::SaveCachesToDisk(void) +{ + try + { + // Open up the SQLite DB: + SQLite::Database db("NameToUUID.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + db.exec("CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)"); + + // Remove all entries: + db.exec("DELETE FROM PlayerNameToUUID"); + + // Save all cache entries: + SQLite::Statement stmt(db, "INSERT INTO PlayerNameToUUID(PlayerName, UUID, DateTime) VALUES (?, ?, ?)"); + Int64 LimitDateTime = time(NULL) - MAX_AGE; + cCSLock Lock(m_CSNameToUUID); + for (cNameToUUIDMap::const_iterator itr = m_NameToUUID.begin(), end = m_NameToUUID.end(); itr != end; ++itr) + { + if (itr->second.m_DateTime < LimitDateTime) + { + // This item is too old, do not save + continue; + } + stmt.bind(1, itr->second.m_PlayerName); + stmt.bind(2, itr->second.m_UUID); + stmt.bind(3, itr->second.m_DateTime); + stmt.exec(); + stmt.reset(); + } + } + catch (const SQLite::Exception & ex) + { + LOGINFO("Saving MojangAPI cache failed: %s", ex.what()); + } +} + + + + + +void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) +{ + // Create a list of names to query, by removing those that are already cached: + AStringVector NamesToQuery; + NamesToQuery.reserve(a_PlayerNames.size()); + { + cCSLock Lock(m_CSNameToUUID); + for (AStringVector::const_iterator itr = a_PlayerNames.begin(), end = a_PlayerNames.end(); itr != end; ++itr) + { + if (m_NameToUUID.find(*itr) == m_NameToUUID.end()) + { + NamesToQuery.push_back(*itr); + } + } // for itr - a_PlayerNames[] + } // Lock(m_CSNameToUUID) + + while (!NamesToQuery.empty()) + { + // Create the request body - a JSON containing up to 100 playernames: + Json::Value root; + int Count = 0; + AStringVector::iterator itr = NamesToQuery.begin(), end = NamesToQuery.end(); + for (; (itr != end) && (Count < 100); ++itr, ++Count) + { + Json::Value req(*itr); + root.append(req); + } // for itr - a_PlayerNames[] + NamesToQuery.erase(NamesToQuery.begin(), itr); + Json::FastWriter Writer; + AString RequestBody = Writer.write(root); + + // Create the HTTP request: + AString Request; + Request += "POST " + m_NameToUUIDAddress + " HTTP/1.0\r\n"; + Request += "Host: " + m_NameToUUIDServer + "\r\n"; + Request += "User-Agent: MCServer\r\n"; + Request += "Connection: close\r\n"; + Request += "Content-Type: application/json\r\n"; + Request += Printf("Content-Length: %u\r\n", (unsigned)RequestBody.length()); + Request += "\r\n"; + Request += RequestBody; + + // Get the response from the server: + AString Response; + if (!SecureRequest(m_NameToUUIDServer, Request, Response)) + { + continue; + } + + // Check the HTTP status line: + const AString Prefix("HTTP/1.1 200 OK"); + AString HexDump; + if (Response.compare(0, Prefix.size(), Prefix)) + { + LOGINFO("%s failed: bad HTTP status line received", __FUNCTION__); + LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); + continue; + } + + // Erase the HTTP headers from the response: + size_t idxHeadersEnd = Response.find("\r\n\r\n"); + if (idxHeadersEnd == AString::npos) + { + LOGINFO("%s failed: bad HTTP response header received", __FUNCTION__); + LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); + continue; + } + Response.erase(0, idxHeadersEnd + 4); + + // Parse the returned string into Json: + Json::Reader reader; + if (!reader.parse(Response, root, false) || !root.isArray()) + { + LOGWARNING("%s failed: Cannot parse received data (NameToUUID) to JSON!", __FUNCTION__); + LOGD("Response body:\n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); + continue; + } + + // Store the returned results into cache: + size_t JsonCount = root.size(); + Int64 Now = time(NULL); + cCSLock Lock(m_CSNameToUUID); + for (size_t idx = 0; idx < JsonCount; ++idx) + { + Json::Value & Val = root[idx]; + AString JsonName = Val.get("name", "").asString(); + AString JsonUUID = Val.get("id", "").asString(); + if (JsonUUID.empty()) + { + continue; + } + AString lcName = JsonName; + m_NameToUUID[StrToLower(lcName)] = sUUIDRecord(JsonName, JsonUUID, Now); + } // for idx - root[] + } // while (!NamesToQuery.empty()) +} + + + + -- cgit v1.2.3 From 17a94b16ea6207fd5f38fbd309b4db0d92de0d31 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 30 Jul 2014 13:52:51 +0200 Subject: MojangAPI: Implemented UUID shortening and dashing. --- src/Protocol/MojangAPI.cpp | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 05f9c09a7..52afd71da 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -18,6 +18,9 @@ /** The maximum age for items to be kept in the cache. Any item older than this will be removed. */ const Int64 MAX_AGE = 7 * 24 * 60 * 60; // 7 days ago +/** The maximum number of names to send in a single query */ +const int MAX_PER_QUERY = 100; + @@ -29,6 +32,7 @@ const Int64 MAX_AGE = 7 * 24 * 60 * 60; // 7 days ago + /** This is the data of the root certs for Starfield Technologies, the CA that signed sessionserver.mojang.com's cert: Downloaded from http://certs.starfieldtech.com/repository/ */ static const AString & StarfieldCACert(void) @@ -161,9 +165,10 @@ AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_Player void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const AString & a_UUID) { AString lcName(a_PlayerName); + AString UUID = MakeUUIDShort(a_UUID); Int64 Now = time(NULL); cCSLock Lock(m_CSNameToUUID); - m_NameToUUID[StrToLower(lcName)] = sUUIDRecord(a_PlayerName, a_UUID, Now); + m_NameToUUID[StrToLower(lcName)] = sUUIDRecord(a_PlayerName, UUID, Now); } @@ -234,8 +239,14 @@ AString cMojangAPI::MakeUUIDShort(const AString & a_UUID) case 36: { + // Remove the dashes from the string: AString res; - // TODO + res.reserve(32); + res.append(a_UUID, 0, 8); + res.append(a_UUID, 9, 4); + res.append(a_UUID, 14, 4); + res.append(a_UUID, 19, 4); + res.append(a_UUID, 24, 12); return res; } } @@ -256,7 +267,16 @@ AString cMojangAPI::MakeUUIDDashed(const AString & a_UUID) case 32: { AString res; - // TODO + res.reserve(36); + res.append(a_UUID, 0, 8); + res.push_back('-'); + res.append(a_UUID, 8, 4); + res.push_back('-'); + res.append(a_UUID, 12, 4); + res.push_back('-'); + res.append(a_UUID, 16, 4); + res.push_back('-'); + res.append(a_UUID, 20, 12); return res; } } @@ -362,11 +382,11 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) while (!NamesToQuery.empty()) { - // Create the request body - a JSON containing up to 100 playernames: + // Create the request body - a JSON containing up to MAX_PER_QUERY playernames: Json::Value root; int Count = 0; AStringVector::iterator itr = NamesToQuery.begin(), end = NamesToQuery.end(); - for (; (itr != end) && (Count < 100); ++itr, ++Count) + for (; (itr != end) && (Count < MAX_PER_QUERY); ++itr, ++Count) { Json::Value req(*itr); root.append(req); @@ -377,7 +397,7 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) // Create the HTTP request: AString Request; - Request += "POST " + m_NameToUUIDAddress + " HTTP/1.0\r\n"; + Request += "POST " + m_NameToUUIDAddress + " HTTP/1.0\r\n"; // We need to use HTTP 1.0 because we don't handle Chunked transfer encoding Request += "Host: " + m_NameToUUIDServer + "\r\n"; Request += "User-Agent: MCServer\r\n"; Request += "Connection: close\r\n"; @@ -430,7 +450,7 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) { Json::Value & Val = root[idx]; AString JsonName = Val.get("name", "").asString(); - AString JsonUUID = Val.get("id", "").asString(); + AString JsonUUID = MakeUUIDShort(Val.get("id", "").asString()); if (JsonUUID.empty()) { continue; -- cgit v1.2.3 From 85d64d291a6d11df6689190268c1f4c6b1c02cdd Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 30 Jul 2014 14:09:30 +0200 Subject: MojangAPI: Clarified the UUID conversion code. --- src/Protocol/MojangAPI.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 52afd71da..86ff134db 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -233,9 +233,14 @@ bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_R AString cMojangAPI::MakeUUIDShort(const AString & a_UUID) { + // Note: we only check the string's length, not the actual content switch (a_UUID.size()) { - case 32: return a_UUID; + case 32: + { + // Already is a short UUID + return a_UUID; + } case 36: { @@ -260,12 +265,18 @@ AString cMojangAPI::MakeUUIDShort(const AString & a_UUID) AString cMojangAPI::MakeUUIDDashed(const AString & a_UUID) { + // Note: we only check the string's length, not the actual content switch (a_UUID.size()) { - case 36: return a_UUID; + case 36: + { + // Already is a dashed UUID + return a_UUID; + } case 32: { + // Insert dashes at the proper positions: AString res; res.reserve(36); res.append(a_UUID, 0, 8); -- cgit v1.2.3 From 0336e12cee0a784b076b5f9423bce2b9f803d1c8 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 30 Jul 2014 17:09:41 +0200 Subject: MojangAPI: Renamed cache file to MojangAPI.sqlite. --- src/Protocol/MojangAPI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 86ff134db..5fbc5b476 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -304,7 +304,7 @@ void cMojangAPI::LoadCachesFromDisk(void) try { // Open up the SQLite DB: - SQLite::Database db("NameToUUID.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + SQLite::Database db("MojangAPI.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); db.exec("CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)"); // Clean up old entries: @@ -341,7 +341,7 @@ void cMojangAPI::SaveCachesToDisk(void) try { // Open up the SQLite DB: - SQLite::Database db("NameToUUID.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + SQLite::Database db("MojangAPI.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); db.exec("CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)"); // Remove all entries: -- cgit v1.2.3 From 8b519bf6e20a987c9544ef11f0df6467831cc069 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 31 Jul 2014 10:02:50 +0200 Subject: MojangAPI: Added a UseCachedOnly param to GetUUIDsFromPlayerNames(). --- src/Protocol/MojangAPI.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 5fbc5b476..71a5e53de 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -129,7 +129,7 @@ void cMojangAPI::Start(cIniFile & a_SettingsIni) -AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_PlayerNames) +AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_PlayerNames, bool a_UseOnlyCached) { // Convert all playernames to lowercase: AStringVector PlayerNames; @@ -140,7 +140,10 @@ AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_Player } // for itr - a_PlayerNames[] // Request the cache to populate any names not yet contained: - CacheNamesToUUIDs(PlayerNames); + if (!a_UseOnlyCached) + { + CacheNamesToUUIDs(PlayerNames); + } // Retrieve from cache: size_t idx = 0; -- cgit v1.2.3 From 59adf113f0ba6414edf061fc4ffbf2bb2b904883 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 31 Jul 2014 17:22:48 +0200 Subject: MojangAPI: Moved the settings to a separate ini section. --- src/Protocol/MojangAPI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 71a5e53de..fa99d63fd 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -120,8 +120,8 @@ cMojangAPI::~cMojangAPI() void cMojangAPI::Start(cIniFile & a_SettingsIni) { - m_NameToUUIDServer = a_SettingsIni.GetValueSet("Authentication", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); - m_NameToUUIDAddress = a_SettingsIni.GetValueSet("Authentication", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); + m_NameToUUIDServer = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); + m_NameToUUIDAddress = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); LoadCachesFromDisk(); } -- cgit v1.2.3 From 70fd7caf1f879b0588042d6542c27ef8f0909a43 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 31 Jul 2014 22:54:45 +0200 Subject: Removed trailing whitespace. --- src/Protocol/MojangAPI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index fa99d63fd..45baa5a4f 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -245,7 +245,7 @@ AString cMojangAPI::MakeUUIDShort(const AString & a_UUID) return a_UUID; } - case 36: + case 36: { // Remove the dashes from the string: AString res; @@ -277,7 +277,7 @@ AString cMojangAPI::MakeUUIDDashed(const AString & a_UUID) return a_UUID; } - case 32: + case 32: { // Insert dashes at the proper positions: AString res; -- cgit v1.2.3 From 003f18bd0f7593bddf5c6af89e3f6fc13632300d Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 3 Aug 2014 12:12:28 +0200 Subject: Added cMojangAPI:GetUUIDFromPlayerName(). This is a simpler way to ask for a single name -> uuid conversion. --- src/Protocol/MojangAPI.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 45baa5a4f..f53df1cba 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -129,6 +129,34 @@ void cMojangAPI::Start(cIniFile & a_SettingsIni) +AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_UseOnlyCached) +{ + // Convert the playername to lowercase: + AString lcPlayerName(a_PlayerName); + StrToLower(lcPlayerName); + + // Request the cache to populate any names not yet contained: + if (!a_UseOnlyCached) + { + AStringVector PlayerNames; + PlayerNames.push_back(lcPlayerName); + CacheNamesToUUIDs(PlayerNames); + } + + // Retrieve from cache: + cNameToUUIDMap::const_iterator itr = m_NameToUUID.find(lcPlayerName); + if (itr == m_NameToUUID.end()) + { + // No UUID found + return ""; + } + return itr->second.m_PlayerName; +} + + + + + AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_PlayerNames, bool a_UseOnlyCached) { // Convert all playernames to lowercase: -- cgit v1.2.3 From 21f52676f3848d58ff1e4eb511c691d4a4ed824b Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 3 Aug 2014 21:32:20 +0200 Subject: cMojangAPI: Added UUID-to-Name lookup. Also fixed the bindings, now all functions are static-like. --- src/Protocol/MojangAPI.cpp | 383 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 341 insertions(+), 42 deletions(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index f53df1cba..9fdf07380 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -25,8 +25,10 @@ const int MAX_PER_QUERY = 100; -#define DEFAULT_NAME_TO_UUID_SERVER "api.mojang.com" -#define DEFAULT_NAME_TO_UUID_ADDRESS "/profiles/minecraft" +#define DEFAULT_NAME_TO_UUID_SERVER "api.mojang.com" +#define DEFAULT_NAME_TO_UUID_ADDRESS "/profiles/minecraft" +#define DEFAULT_UUID_TO_PROFILE_SERVER "sessionserver.mojang.com" +#define DEFAULT_UUID_TO_PROFILE_ADDRESS "/session/minecraft/profile/%UUID%?unsigned=false" @@ -96,12 +98,66 @@ static const AString & StarfieldCACert(void) +//////////////////////////////////////////////////////////////////////////////// +// cMojangAPI::sProfile: + +cMojangAPI::sProfile::sProfile( + const AString & a_PlayerName, + const AString & a_UUID, + const Json::Value & a_Properties, + Int64 a_DateTime +) : + m_PlayerName(a_PlayerName), + m_UUID(a_UUID), + m_Textures(), + m_TexturesSignature(), + m_DateTime(a_DateTime) +{ + /* + Example a_Profile contents: + "properties": + [ + { + "name": "textures", + "value": "eyJ0aW1lc3RhbXAiOjE0MDcwNzAzMjEyNzEsInByb2ZpbGVJZCI6ImIxY2FmMjQyMDJhODQxYTc4MDU1YTA3OWM0NjBlZWU3IiwicHJvZmlsZU5hbWUiOiJ4b2Z0IiwiaXNQdWJsaWMiOnRydWUsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9iNzc5YmFiZjVhNTg3Zjk0OGFkNjc0N2VhOTEyNzU0MjliNjg4Mjk1YWUzYzA3YmQwZTJmNWJmNGQwNTIifX19", + "signature": "XCty+jGEF39hEPrPhYNnCX087kPaoCjYruzYI/DS4nkL5hbjnkSM5Rh15hnUyv/FHhC8OF5rif3D1tQjtMI19KSVaXoUFXpbJM8/+PB8GDgEbX8Fc3u9nYkzOcM/xfxdYsFAdFhLQMkvase/BZLSuPhdy9DdI+TCrO7xuSTZfYmmwVuWo3w5gCY+mSIAnqltnOzaOOTcly75xvO0WYpVk7nJdnR2tvSi0wfrQPDrIg/uzhX7p0SnDqijmBU4QaNez/TNKiFxy69dAzt0RSotlQzqkDbyVKhhv9a4eY8h3pXi4UMftKEj4FAKczxLImkukJXuOn5NN15/Q+le0rJVBC60/xjKIVzltEsMN6qjWD0lQjey7WEL+4pGhCVuWY5KzuZjFvgqszuJTFz7lo+bcHiceldJtea8/fa02eTRObZvdLxbWC9ZfFY0IhpOVKfcLdno/ddDMNMQMi5kMrJ8MZZ/PcW1w5n7MMGWPGCla1kOaC55AL0QYSMGRVEZqgU9wXI5M7sHGZKGM4mWxkbEJYBkpI/p3GyxWgV6v33ZWlsz65TqlNrR1gCLaoFCm7Sif8NqPBZUAONHYon0roXhin/DyEanS93WV6i6FC1Wisscjq2AcvnOlgTo/5nN/1QsMbjNumuMGo37sqjRqlXoPb8zEUbAhhztYuJjEfQ2Rd8=" + } + ] + */ + + // Parse the Textures and TexturesSignature from the Profile: + if (!a_Properties.isArray()) + { + // Properties is not a valid array, bail out + return; + } + Json::UInt Size = a_Properties.size(); + for (Json::UInt i = 0; i < Size; i++) + { + const Json::Value & Prop = a_Properties[i]; + AString PropName = Prop.get("name", "").asString(); + if (PropName != "textures") + { + continue; + } + m_Textures = Prop.get("value", "").asString(); + m_TexturesSignature = Prop.get("signature", "").asString(); + break; + } // for i - Properties[] +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cMojangAPI: cMojangAPI::cMojangAPI(void) : m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER), - m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS) + m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS), + m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER), + m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS) { } @@ -120,8 +176,10 @@ cMojangAPI::~cMojangAPI() void cMojangAPI::Start(cIniFile & a_SettingsIni) { - m_NameToUUIDServer = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); - m_NameToUUIDAddress = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); + m_NameToUUIDServer = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); + m_NameToUUIDAddress = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); + m_UUIDToProfileServer = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER); + m_UUIDToProfileAddress = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS); LoadCachesFromDisk(); } @@ -135,7 +193,7 @@ AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_U AString lcPlayerName(a_PlayerName); StrToLower(lcPlayerName); - // Request the cache to populate any names not yet contained: + // Request the cache to query the name if not yet cached: if (!a_UseOnlyCached) { AStringVector PlayerNames; @@ -144,7 +202,8 @@ AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_U } // Retrieve from cache: - cNameToUUIDMap::const_iterator itr = m_NameToUUID.find(lcPlayerName); + cCSLock Lock(m_CSNameToUUID); + cProfileMap::const_iterator itr = m_NameToUUID.find(lcPlayerName); if (itr == m_NameToUUID.end()) { // No UUID found @@ -157,6 +216,44 @@ AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_U +AString cMojangAPI::GetPlayerNameFromUUID(const AString & a_UUID, bool a_UseOnlyCached) +{ + // Normalize the UUID to lowercase short format that is used as the map key: + AString UUID = StrToLower(MakeUUIDShort(a_UUID)); + + // Retrieve from caches: + { + cCSLock Lock(m_CSUUIDToProfile); + cProfileMap::const_iterator itr = m_UUIDToProfile.find(UUID); + if (itr != m_UUIDToProfile.end()) + { + return itr->second.m_PlayerName; + } + } + { + cCSLock Lock(m_CSUUIDToName); + cProfileMap::const_iterator itr = m_UUIDToName.find(UUID); + if (itr != m_UUIDToName.end()) + { + return itr->second.m_PlayerName; + } + } + + // Name not yet cached, request cache and retry: + if (!a_UseOnlyCached) + { + CacheUUIDToProfile(UUID); + return GetPlayerNameFromUUID(a_UUID, true); + } + + // No value found, none queried. Return an error: + return ""; +} + + + + + AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_PlayerNames, bool a_UseOnlyCached) { // Convert all playernames to lowercase: @@ -180,7 +277,7 @@ AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_Player cCSLock Lock(m_CSNameToUUID); for (AStringVector::const_iterator itr = PlayerNames.begin(), end = PlayerNames.end(); itr != end; ++itr, ++idx) { - cNameToUUIDMap::const_iterator itrN = m_NameToUUID.find(*itr); + cProfileMap::const_iterator itrN = m_NameToUUID.find(*itr); if (itrN != m_NameToUUID.end()) { res[idx] = itrN->second.m_UUID; @@ -196,10 +293,39 @@ AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_Player void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const AString & a_UUID) { AString lcName(a_PlayerName); - AString UUID = MakeUUIDShort(a_UUID); + AString UUID = StrToLower(MakeUUIDShort(a_UUID)); Int64 Now = time(NULL); - cCSLock Lock(m_CSNameToUUID); - m_NameToUUID[StrToLower(lcName)] = sUUIDRecord(a_PlayerName, UUID, Now); + { + cCSLock Lock(m_CSNameToUUID); + m_NameToUUID[StrToLower(lcName)] = sProfile(a_PlayerName, UUID, "", "", Now); + } + { + cCSLock Lock(m_CSUUIDToName); + m_UUIDToName[UUID] = sProfile(a_PlayerName, UUID, "", "", Now); + } +} + + + + + +void cMojangAPI::AddPlayerProfile(const AString & a_PlayerName, const AString & a_UUID, const Json::Value & a_Properties) +{ + AString lcName(a_PlayerName); + AString UUID = StrToLower(MakeUUIDShort(a_UUID)); + Int64 Now = time(NULL); + { + cCSLock Lock(m_CSNameToUUID); + m_NameToUUID[StrToLower(lcName)] = sProfile(a_PlayerName, UUID, "", "", Now); + } + { + cCSLock Lock(m_CSUUIDToName); + m_UUIDToName[UUID] = sProfile(a_PlayerName, UUID, "", "", Now); + } + { + cCSLock Lock(m_CSUUIDToProfile); + m_UUIDToProfile[UUID] = sProfile(a_PlayerName, UUID, a_Properties, Now); + } } @@ -337,6 +463,7 @@ void cMojangAPI::LoadCachesFromDisk(void) // Open up the SQLite DB: SQLite::Database db("MojangAPI.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); db.exec("CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)"); + db.exec("CREATE TABLE IF NOT EXISTS UUIDToProfile (UUID, PlayerName, Textures, TexturesSignature, DateTime)"); // Clean up old entries: { @@ -345,16 +472,40 @@ void cMojangAPI::LoadCachesFromDisk(void) stmt.bind(1, LimitDateTime); stmt.exec(); } + { + SQLite::Statement stmt(db, "DELETE FROM UUIDToProfile WHERE DateTime < ?"); + Int64 LimitDateTime = time(NULL) - MAX_AGE; + stmt.bind(1, LimitDateTime); + stmt.exec(); + } - // Retrieve all remaining entries:: - SQLite::Statement stmt(db, "SELECT PlayerName, UUID, DateTime FROM PlayerNameToUUID"); - while (stmt.executeStep()) + // Retrieve all remaining entries: + { + SQLite::Statement stmt(db, "SELECT PlayerName, UUID, DateTime FROM PlayerNameToUUID"); + while (stmt.executeStep()) + { + AString PlayerName = stmt.getColumn(0); + AString UUID = stmt.getColumn(1); + Int64 DateTime = stmt.getColumn(2); + AString lcPlayerName = PlayerName; + UUID = StrToLower(MakeUUIDShort(UUID)); + m_NameToUUID[StrToLower(lcPlayerName)] = sProfile(PlayerName, UUID, "", "", DateTime); + m_UUIDToName[UUID] = sProfile(PlayerName, UUID, "", "", DateTime); + } + } { - AString PlayerName = stmt.getColumn(0); - AString UUID = stmt.getColumn(1); - Int64 DateTime = stmt.getColumn(2); - AString lcPlayerName = PlayerName; - m_NameToUUID[StrToLower(lcPlayerName)] = sUUIDRecord(PlayerName, UUID, DateTime); + SQLite::Statement stmt(db, "SELECT PlayerName, UUID, Textures, TexturesSignature, DateTime FROM UUIDToProfile"); + while (stmt.executeStep()) + { + AString PlayerName = stmt.getColumn(0); + AString UUID = stmt.getColumn(1); + AString Textures = stmt.getColumn(2); + AString TexturesSignature = stmt.getColumn(2); + Int64 DateTime = stmt.getColumn(4); + AString lcPlayerName = PlayerName; + UUID = StrToLower(MakeUUIDShort(UUID)); + m_UUIDToProfile[StrToLower(lcPlayerName)] = sProfile(PlayerName, UUID, Textures, TexturesSignature, DateTime); + } } } catch (const SQLite::Exception & ex) @@ -374,26 +525,51 @@ void cMojangAPI::SaveCachesToDisk(void) // Open up the SQLite DB: SQLite::Database db("MojangAPI.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); db.exec("CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)"); + db.exec("CREATE TABLE IF NOT EXISTS UUIDToProfile (UUID, PlayerName, Textures, TexturesSignature, DateTime)"); // Remove all entries: db.exec("DELETE FROM PlayerNameToUUID"); + db.exec("DELETE FROM UUIDToProfile"); - // Save all cache entries: - SQLite::Statement stmt(db, "INSERT INTO PlayerNameToUUID(PlayerName, UUID, DateTime) VALUES (?, ?, ?)"); + // Save all cache entries - m_PlayerNameToUUID: Int64 LimitDateTime = time(NULL) - MAX_AGE; - cCSLock Lock(m_CSNameToUUID); - for (cNameToUUIDMap::const_iterator itr = m_NameToUUID.begin(), end = m_NameToUUID.end(); itr != end; ++itr) { - if (itr->second.m_DateTime < LimitDateTime) + SQLite::Statement stmt(db, "INSERT INTO PlayerNameToUUID(PlayerName, UUID, DateTime) VALUES (?, ?, ?)"); + cCSLock Lock(m_CSNameToUUID); + for (cProfileMap::const_iterator itr = m_NameToUUID.begin(), end = m_NameToUUID.end(); itr != end; ++itr) { - // This item is too old, do not save - continue; + if (itr->second.m_DateTime < LimitDateTime) + { + // This item is too old, do not save + continue; + } + stmt.bind(1, itr->second.m_PlayerName); + stmt.bind(2, itr->second.m_UUID); + stmt.bind(3, itr->second.m_DateTime); + stmt.exec(); + stmt.reset(); + } + } + + // Save all cache entries - m_UUIDToProfile: + { + SQLite::Statement stmt(db, "INSERT INTO UUIDToProfile(UUID, PlayerName, Textures, TexturesSignature, DateTime) VALUES (?, ?, ?, ?, ?)"); + cCSLock Lock(m_CSUUIDToProfile); + for (cProfileMap::const_iterator itr = m_UUIDToProfile.begin(), end = m_UUIDToProfile.end(); itr != end; ++itr) + { + if (itr->second.m_DateTime < LimitDateTime) + { + // This item is too old, do not save + continue; + } + stmt.bind(1, itr->second.m_UUID); + stmt.bind(2, itr->second.m_PlayerName); + stmt.bind(3, itr->second.m_Textures); + stmt.bind(4, itr->second.m_TexturesSignature); + stmt.bind(5, itr->second.m_DateTime); + stmt.exec(); + stmt.reset(); } - stmt.bind(1, itr->second.m_PlayerName); - stmt.bind(2, itr->second.m_UUID); - stmt.bind(3, itr->second.m_DateTime); - stmt.exec(); - stmt.reset(); } } catch (const SQLite::Exception & ex) @@ -487,22 +663,145 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) // Store the returned results into cache: size_t JsonCount = root.size(); Int64 Now = time(NULL); - cCSLock Lock(m_CSNameToUUID); - for (size_t idx = 0; idx < JsonCount; ++idx) { - Json::Value & Val = root[idx]; - AString JsonName = Val.get("name", "").asString(); - AString JsonUUID = MakeUUIDShort(Val.get("id", "").asString()); - if (JsonUUID.empty()) + cCSLock Lock(m_CSNameToUUID); + for (size_t idx = 0; idx < JsonCount; ++idx) { - continue; - } - AString lcName = JsonName; - m_NameToUUID[StrToLower(lcName)] = sUUIDRecord(JsonName, JsonUUID, Now); - } // for idx - root[] + Json::Value & Val = root[idx]; + AString JsonName = Val.get("name", "").asString(); + AString JsonUUID = StrToLower(MakeUUIDShort(Val.get("id", "").asString())); + if (JsonUUID.empty()) + { + continue; + } + AString lcName = JsonName; + m_NameToUUID[StrToLower(lcName)] = sProfile(JsonName, JsonUUID, "", "", Now); + } // for idx - root[] + } // cCSLock (m_CSNameToUUID) + + // Also cache the UUIDToName: + { + cCSLock Lock(m_CSUUIDToName); + for (size_t idx = 0; idx < JsonCount; ++idx) + { + Json::Value & Val = root[idx]; + AString JsonName = Val.get("name", "").asString(); + AString JsonUUID = StrToLower(MakeUUIDShort(Val.get("id", "").asString())); + if (JsonUUID.empty()) + { + continue; + } + m_UUIDToName[JsonUUID] = sProfile(JsonName, JsonUUID, "", "", Now); + } // for idx - root[] + } } // while (!NamesToQuery.empty()) } + +void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID) +{ + ASSERT(a_UUID.size() == 32); + + // Check if already present: + { + if (m_UUIDToProfile.find(a_UUID) != m_UUIDToProfile.end()) + { + return; + } + } + + // Create the request address: + AString Address = m_UUIDToProfileAddress; + ReplaceString(Address, "%UUID%", a_UUID); + + // Create the HTTP request: + AString Request; + Request += "GET " + Address + " HTTP/1.0\r\n"; // We need to use HTTP 1.0 because we don't handle Chunked transfer encoding + Request += "Host: " + m_UUIDToProfileServer + "\r\n"; + Request += "User-Agent: MCServer\r\n"; + Request += "Connection: close\r\n"; + Request += "Content-Length: 0\r\n"; + Request += "\r\n"; + + // Get the response from the server: + AString Response; + if (!SecureRequest(m_UUIDToProfileServer, Request, Response)) + { + return; + } + + // Check the HTTP status line: + const AString Prefix("HTTP/1.1 200 OK"); + AString HexDump; + if (Response.compare(0, Prefix.size(), Prefix)) + { + LOGINFO("%s failed: bad HTTP status line received", __FUNCTION__); + LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); + return; + } + + // Erase the HTTP headers from the response: + size_t idxHeadersEnd = Response.find("\r\n\r\n"); + if (idxHeadersEnd == AString::npos) + { + LOGINFO("%s failed: bad HTTP response header received", __FUNCTION__); + LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); + return; + } + Response.erase(0, idxHeadersEnd + 4); + + // Parse the returned string into Json: + Json::Reader reader; + Json::Value root; + if (!reader.parse(Response, root, false) || !root.isObject()) + { + LOGWARNING("%s failed: Cannot parse received data (NameToUUID) to JSON!", __FUNCTION__); + LOGD("Response body:\n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); + return; + } + + /* Example response: + { + "id": "b1caf24202a841a78055a079c460eee7", + "name": "xoft", + "properties": + [ + { + "name": "textures", + "value": "eyJ0aW1lc3RhbXAiOjE0MDcwNzAzMjEyNzEsInByb2ZpbGVJZCI6ImIxY2FmMjQyMDJhODQxYTc4MDU1YTA3OWM0NjBlZWU3IiwicHJvZmlsZU5hbWUiOiJ4b2Z0IiwiaXNQdWJsaWMiOnRydWUsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9iNzc5YmFiZjVhNTg3Zjk0OGFkNjc0N2VhOTEyNzU0MjliNjg4Mjk1YWUzYzA3YmQwZTJmNWJmNGQwNTIifX19", + "signature": "XCty+jGEF39hEPrPhYNnCX087kPaoCjYruzYI/DS4nkL5hbjnkSM5Rh15hnUyv/FHhC8OF5rif3D1tQjtMI19KSVaXoUFXpbJM8/+PB8GDgEbX8Fc3u9nYkzOcM/xfxdYsFAdFhLQMkvase/BZLSuPhdy9DdI+TCrO7xuSTZfYmmwVuWo3w5gCY+mSIAnqltnOzaOOTcly75xvO0WYpVk7nJdnR2tvSi0wfrQPDrIg/uzhX7p0SnDqijmBU4QaNez/TNKiFxy69dAzt0RSotlQzqkDbyVKhhv9a4eY8h3pXi4UMftKEj4FAKczxLImkukJXuOn5NN15/Q+le0rJVBC60/xjKIVzltEsMN6qjWD0lQjey7WEL+4pGhCVuWY5KzuZjFvgqszuJTFz7lo+bcHiceldJtea8/fa02eTRObZvdLxbWC9ZfFY0IhpOVKfcLdno/ddDMNMQMi5kMrJ8MZZ/PcW1w5n7MMGWPGCla1kOaC55AL0QYSMGRVEZqgU9wXI5M7sHGZKGM4mWxkbEJYBkpI/p3GyxWgV6v33ZWlsz65TqlNrR1gCLaoFCm7Sif8NqPBZUAONHYon0roXhin/DyEanS93WV6i6FC1Wisscjq2AcvnOlgTo/5nN/1QsMbjNumuMGo37sqjRqlXoPb8zEUbAhhztYuJjEfQ2Rd8=" + } + ] + } + */ + + // Store the returned result into caches: + AString PlayerName = root.get("name", "").asString(); + if (PlayerName.empty()) + { + // No valid playername, bail out + return; + } + Json::Value Properties = root.get("properties", ""); + Int64 Now = time(NULL); + { + cCSLock Lock(m_CSUUIDToProfile); + m_UUIDToProfile[a_UUID] = sProfile(PlayerName, a_UUID, Properties, Now); + } + { + cCSLock Lock(m_CSUUIDToName); + m_UUIDToName[a_UUID] = sProfile(PlayerName, a_UUID, Properties, Now); + } + { + AString lcPlayerName(PlayerName); + cCSLock Lock(m_CSNameToUUID); + m_NameToUUID[StrToLower(lcPlayerName)] = sProfile(PlayerName, a_UUID, Properties, Now); + } +} + + + + -- cgit v1.2.3 From 1fa210c7f91030ac18fd880fcf131e8104c0b889 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Mon, 4 Aug 2014 11:16:19 +0200 Subject: Refactored case-conversion functions. StrToLower() returns a modified copy of the string, InPlaceLowercase() modifies the string in-place. --- src/Protocol/MojangAPI.cpp | 52 ++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 30 deletions(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 9fdf07380..a7aea5490 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -190,8 +190,7 @@ void cMojangAPI::Start(cIniFile & a_SettingsIni) AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_UseOnlyCached) { // Convert the playername to lowercase: - AString lcPlayerName(a_PlayerName); - StrToLower(lcPlayerName); + AString lcPlayerName = StrToLower(a_PlayerName); // Request the cache to query the name if not yet cached: if (!a_UseOnlyCached) @@ -219,7 +218,7 @@ AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_U AString cMojangAPI::GetPlayerNameFromUUID(const AString & a_UUID, bool a_UseOnlyCached) { // Normalize the UUID to lowercase short format that is used as the map key: - AString UUID = StrToLower(MakeUUIDShort(a_UUID)); + AString UUID = MakeUUIDShort(a_UUID); // Retrieve from caches: { @@ -260,8 +259,7 @@ AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_Player AStringVector PlayerNames; for (AStringVector::const_iterator itr = a_PlayerNames.begin(), end = a_PlayerNames.end(); itr != end; ++itr) { - AString Lower(*itr); - PlayerNames.push_back(StrToLower(Lower)); + PlayerNames.push_back(StrToLower(*itr)); } // for itr - a_PlayerNames[] // Request the cache to populate any names not yet contained: @@ -292,12 +290,11 @@ AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_Player void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const AString & a_UUID) { - AString lcName(a_PlayerName); - AString UUID = StrToLower(MakeUUIDShort(a_UUID)); + AString UUID = MakeUUIDShort(a_UUID); Int64 Now = time(NULL); { cCSLock Lock(m_CSNameToUUID); - m_NameToUUID[StrToLower(lcName)] = sProfile(a_PlayerName, UUID, "", "", Now); + m_NameToUUID[StrToLower(a_PlayerName)] = sProfile(a_PlayerName, UUID, "", "", Now); } { cCSLock Lock(m_CSUUIDToName); @@ -311,12 +308,11 @@ void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const void cMojangAPI::AddPlayerProfile(const AString & a_PlayerName, const AString & a_UUID, const Json::Value & a_Properties) { - AString lcName(a_PlayerName); - AString UUID = StrToLower(MakeUUIDShort(a_UUID)); + AString UUID = MakeUUIDShort(a_UUID); Int64 Now = time(NULL); { cCSLock Lock(m_CSNameToUUID); - m_NameToUUID[StrToLower(lcName)] = sProfile(a_PlayerName, UUID, "", "", Now); + m_NameToUUID[StrToLower(a_PlayerName)] = sProfile(a_PlayerName, UUID, "", "", Now); } { cCSLock Lock(m_CSUUIDToName); @@ -395,13 +391,13 @@ AString cMojangAPI::MakeUUIDShort(const AString & a_UUID) { case 32: { - // Already is a short UUID - return a_UUID; + // Already is a short UUID, only lowercase + return StrToLower(a_UUID); } case 36: { - // Remove the dashes from the string: + // Remove the dashes from the string by appending together the parts between them: AString res; res.reserve(32); res.append(a_UUID, 0, 8); @@ -409,7 +405,7 @@ AString cMojangAPI::MakeUUIDShort(const AString & a_UUID) res.append(a_UUID, 14, 4); res.append(a_UUID, 19, 4); res.append(a_UUID, 24, 12); - return res; + return StrToLower(res); } } LOGWARNING("%s: Not an UUID: \"%s\".", __FUNCTION__, a_UUID.c_str()); @@ -427,8 +423,8 @@ AString cMojangAPI::MakeUUIDDashed(const AString & a_UUID) { case 36: { - // Already is a dashed UUID - return a_UUID; + // Already is a dashed UUID, only lowercase + return StrToLower(a_UUID); } case 32: @@ -445,7 +441,7 @@ AString cMojangAPI::MakeUUIDDashed(const AString & a_UUID) res.append(a_UUID, 16, 4); res.push_back('-'); res.append(a_UUID, 20, 12); - return res; + return StrToLower(res); } } LOGWARNING("%s: Not an UUID: \"%s\".", __FUNCTION__, a_UUID.c_str()); @@ -487,9 +483,8 @@ void cMojangAPI::LoadCachesFromDisk(void) AString PlayerName = stmt.getColumn(0); AString UUID = stmt.getColumn(1); Int64 DateTime = stmt.getColumn(2); - AString lcPlayerName = PlayerName; - UUID = StrToLower(MakeUUIDShort(UUID)); - m_NameToUUID[StrToLower(lcPlayerName)] = sProfile(PlayerName, UUID, "", "", DateTime); + UUID = MakeUUIDShort(UUID); + m_NameToUUID[StrToLower(PlayerName)] = sProfile(PlayerName, UUID, "", "", DateTime); m_UUIDToName[UUID] = sProfile(PlayerName, UUID, "", "", DateTime); } } @@ -502,9 +497,8 @@ void cMojangAPI::LoadCachesFromDisk(void) AString Textures = stmt.getColumn(2); AString TexturesSignature = stmt.getColumn(2); Int64 DateTime = stmt.getColumn(4); - AString lcPlayerName = PlayerName; - UUID = StrToLower(MakeUUIDShort(UUID)); - m_UUIDToProfile[StrToLower(lcPlayerName)] = sProfile(PlayerName, UUID, Textures, TexturesSignature, DateTime); + UUID = MakeUUIDShort(UUID); + m_UUIDToProfile[UUID] = sProfile(PlayerName, UUID, Textures, TexturesSignature, DateTime); } } } @@ -669,13 +663,12 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) { Json::Value & Val = root[idx]; AString JsonName = Val.get("name", "").asString(); - AString JsonUUID = StrToLower(MakeUUIDShort(Val.get("id", "").asString())); + AString JsonUUID = MakeUUIDShort(Val.get("id", "").asString()); if (JsonUUID.empty()) { continue; } - AString lcName = JsonName; - m_NameToUUID[StrToLower(lcName)] = sProfile(JsonName, JsonUUID, "", "", Now); + m_NameToUUID[StrToLower(JsonName)] = sProfile(JsonName, JsonUUID, "", "", Now); } // for idx - root[] } // cCSLock (m_CSNameToUUID) @@ -686,7 +679,7 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) { Json::Value & Val = root[idx]; AString JsonName = Val.get("name", "").asString(); - AString JsonUUID = StrToLower(MakeUUIDShort(Val.get("id", "").asString())); + AString JsonUUID = MakeUUIDShort(Val.get("id", "").asString()); if (JsonUUID.empty()) { continue; @@ -796,9 +789,8 @@ void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID) m_UUIDToName[a_UUID] = sProfile(PlayerName, a_UUID, Properties, Now); } { - AString lcPlayerName(PlayerName); cCSLock Lock(m_CSNameToUUID); - m_NameToUUID[StrToLower(lcPlayerName)] = sProfile(PlayerName, a_UUID, Properties, Now); + m_NameToUUID[StrToLower(PlayerName)] = sProfile(PlayerName, a_UUID, Properties, Now); } } -- cgit v1.2.3 From 9113b45673bc9b0202ce821b8db2dbe6f4b425b9 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Mon, 4 Aug 2014 23:35:32 +0200 Subject: MojangAPI: Fixed PlayerNameToUUID(). --- src/Protocol/MojangAPI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index a7aea5490..823ff5469 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -208,7 +208,7 @@ AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_U // No UUID found return ""; } - return itr->second.m_PlayerName; + return itr->second.m_UUID; } -- cgit v1.2.3 From 0c04bf962ed025789c1979c6d4fb122735b1a46b Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 21 Aug 2014 20:47:52 +0200 Subject: cMojangAPI updates cRankManager's playernames. --- src/Protocol/MojangAPI.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 823ff5469..3786fecc9 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -10,6 +10,7 @@ #include "inifile/iniFile.h" #include "json/json.h" #include "PolarSSL++/BlockingSslClientSocket.h" +#include "../RankManager.h" @@ -300,6 +301,7 @@ void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const cCSLock Lock(m_CSUUIDToName); m_UUIDToName[UUID] = sProfile(a_PlayerName, UUID, "", "", Now); } + NotifyNameUUID(a_PlayerName, a_UUID); } @@ -322,6 +324,7 @@ void cMojangAPI::AddPlayerProfile(const AString & a_PlayerName, const AString & cCSLock Lock(m_CSUUIDToProfile); m_UUIDToProfile[UUID] = sProfile(a_PlayerName, UUID, a_Properties, Now); } + NotifyNameUUID(a_PlayerName, a_UUID); } @@ -669,6 +672,7 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) continue; } m_NameToUUID[StrToLower(JsonName)] = sProfile(JsonName, JsonUUID, "", "", Now); + NotifyNameUUID(JsonName, JsonUUID); } // for idx - root[] } // cCSLock (m_CSNameToUUID) @@ -792,6 +796,21 @@ void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID) cCSLock Lock(m_CSNameToUUID); m_NameToUUID[StrToLower(PlayerName)] = sProfile(PlayerName, a_UUID, Properties, Now); } + NotifyNameUUID(PlayerName, a_UUID); +} + + + + + +void cMojangAPI::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID) +{ + // Notify the rank manager: + cCSLock Lock(m_CSRankMgr); + if (m_RankMgr != NULL) + { + m_RankMgr->NotifyNameUUID(a_PlayerName, a_UUID); + } } -- cgit v1.2.3 From 8f20c359cd1f1086d4c312273fe052404755584f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 25 Aug 2014 07:28:45 +0200 Subject: Fixed a type warning. --- src/Protocol/MojangAPI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 823ff5469..d107a8470 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -655,11 +655,11 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) } // Store the returned results into cache: - size_t JsonCount = root.size(); + Json::Value::UInt JsonCount = root.size(); Int64 Now = time(NULL); { cCSLock Lock(m_CSNameToUUID); - for (size_t idx = 0; idx < JsonCount; ++idx) + for (Json::Value::UInt idx = 0; idx < JsonCount; ++idx) { Json::Value & Val = root[idx]; AString JsonName = Val.get("name", "").asString(); -- cgit v1.2.3 From bf16c5ed92c7d45599099c4c3d628cee1a60f35f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 25 Aug 2014 18:25:39 +0300 Subject: Type warning fixes. --- src/Protocol/MojangAPI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index d107a8470..83c2dc300 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -675,7 +675,7 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) // Also cache the UUIDToName: { cCSLock Lock(m_CSUUIDToName); - for (size_t idx = 0; idx < JsonCount; ++idx) + for (Json::Value::UInt idx = 0; idx < JsonCount; ++idx) { Json::Value & Val = root[idx]; AString JsonName = Val.get("name", "").asString(); -- cgit v1.2.3 From b6d77d9679b7b26f31d6249b66b49d52eabe5bb6 Mon Sep 17 00:00:00 2001 From: worktycho Date: Sun, 31 Aug 2014 20:26:08 +0100 Subject: Init RankMgr pointer to NULL --- src/Protocol/MojangAPI.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 4e5c41a8a..28da83c31 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -158,7 +158,8 @@ cMojangAPI::cMojangAPI(void) : m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER), m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS), m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER), - m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS) + m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS), + m_RankMgr(NULL) { } -- cgit v1.2.3 From 3d2d8a096b72a0af9d4e21198d112d97c9318410 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 5 Oct 2014 20:03:21 +0200 Subject: cMojangAPI: Added periodical refreshes. --- src/Protocol/MojangAPI.cpp | 130 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 19 deletions(-) (limited to 'src/Protocol/MojangAPI.cpp') diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 28da83c31..0a6716e6f 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -11,6 +11,8 @@ #include "json/json.h" #include "PolarSSL++/BlockingSslClientSocket.h" #include "../RankManager.h" +#include "../OSSupport/IsThread.h" +#include "../Root.h" @@ -151,6 +153,41 @@ cMojangAPI::sProfile::sProfile( +//////////////////////////////////////////////////////////////////////////////// +// cMojangAPI::cUpdateThread: + +class cMojangAPI::cUpdateThread : + public cIsThread +{ + typedef cIsThread super; +public: + cUpdateThread() : + super("cMojangAPI::cUpdateThread") + { + } + + ~cUpdateThread() + { + m_evtNotify.Set(); + Stop(); + } + +protected: + cEvent m_evtNotify; + + virtual void Execute(void) override + { + do + { + cRoot::Get()->GetMojangAPI().Update(); + } while (!m_evtNotify.Wait(60 * 60 * 1000)); // Repeat every 60 minutes + } +} ; + + + + + //////////////////////////////////////////////////////////////////////////////// // cMojangAPI: @@ -159,7 +196,8 @@ cMojangAPI::cMojangAPI(void) : m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS), m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER), m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS), - m_RankMgr(NULL) + m_RankMgr(NULL), + m_UpdateThread(new cUpdateThread()) { } @@ -183,6 +221,7 @@ void cMojangAPI::Start(cIniFile & a_SettingsIni) m_UUIDToProfileServer = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER); m_UUIDToProfileAddress = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS); LoadCachesFromDisk(); + m_UpdateThread->Start(); } @@ -465,21 +504,7 @@ void cMojangAPI::LoadCachesFromDisk(void) db.exec("CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)"); db.exec("CREATE TABLE IF NOT EXISTS UUIDToProfile (UUID, PlayerName, Textures, TexturesSignature, DateTime)"); - // Clean up old entries: - { - SQLite::Statement stmt(db, "DELETE FROM PlayerNameToUUID WHERE DateTime < ?"); - Int64 LimitDateTime = time(NULL) - MAX_AGE; - stmt.bind(1, LimitDateTime); - stmt.exec(); - } - { - SQLite::Statement stmt(db, "DELETE FROM UUIDToProfile WHERE DateTime < ?"); - Int64 LimitDateTime = time(NULL) - MAX_AGE; - stmt.bind(1, LimitDateTime); - stmt.exec(); - } - - // Retrieve all remaining entries: + // Retrieve all entries: { SQLite::Statement stmt(db, "SELECT PlayerName, UUID, DateTime FROM PlayerNameToUUID"); while (stmt.executeStep()) @@ -596,18 +621,27 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) } // for itr - a_PlayerNames[] } // Lock(m_CSNameToUUID) - while (!NamesToQuery.empty()) + QueryNamesToUUIDs(NamesToQuery); +} + + + + + +void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery) +{ + while (!a_NamesToQuery.empty()) { // Create the request body - a JSON containing up to MAX_PER_QUERY playernames: Json::Value root; int Count = 0; - AStringVector::iterator itr = NamesToQuery.begin(), end = NamesToQuery.end(); + AStringVector::iterator itr = a_NamesToQuery.begin(), end = a_NamesToQuery.end(); for (; (itr != end) && (Count < MAX_PER_QUERY); ++itr, ++Count) { Json::Value req(*itr); root.append(req); } // for itr - a_PlayerNames[] - NamesToQuery.erase(NamesToQuery.begin(), itr); + a_NamesToQuery.erase(a_NamesToQuery.begin(), itr); Json::FastWriter Writer; AString RequestBody = Writer.write(root); @@ -705,12 +739,22 @@ void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID) // Check if already present: { + cCSLock Lock(m_CSUUIDToProfile); if (m_UUIDToProfile.find(a_UUID) != m_UUIDToProfile.end()) { return; } } + QueryUUIDToProfile(a_UUID); +} + + + + + +void cMojangAPI::QueryUUIDToProfile(const AString & a_UUID) +{ // Create the request address: AString Address = m_UUIDToProfileAddress; ReplaceString(Address, "%UUID%", a_UUID); @@ -817,3 +861,51 @@ void cMojangAPI::NotifyNameUUID(const AString & a_PlayerName, const AString & a_ + +void cMojangAPI::Update(void) +{ + Int64 LimitDateTime = time(NULL) - MAX_AGE; + + // Re-query all playernames that are stale: + AStringVector PlayerNames; + { + cCSLock Lock(m_CSNameToUUID); + for (cProfileMap::const_iterator itr = m_NameToUUID.begin(), end = m_NameToUUID.end(); itr != end; ++itr) + { + if (itr->second.m_DateTime < LimitDateTime) + { + PlayerNames.push_back(itr->first); + } + } // for itr - m_NameToUUID[] + } + if (!PlayerNames.empty()) + { + LOG("cMojangAPI: Updating name-to-uuid cache for %u names", (unsigned)PlayerNames.size()); + QueryNamesToUUIDs(PlayerNames); + } + + // Re-query all profiles that are stale: + AStringVector ProfileUUIDs; + { + cCSLock Lock(m_CSUUIDToProfile); + for (cProfileMap::const_iterator itr = m_UUIDToProfile.begin(), end = m_UUIDToProfile.end(); itr != end; ++itr) + { + if (itr->second.m_DateTime < LimitDateTime) + { + ProfileUUIDs.push_back(itr->first); + } + } // for itr - m_UUIDToProfile[] + } + if (!ProfileUUIDs.empty()) + { + LOG("cMojangAPI: Updating uuid-to-profile cache for %u uuids", (unsigned)ProfileUUIDs.size()); + for (AStringVector::const_iterator itr = ProfileUUIDs.begin(), end = ProfileUUIDs.end(); itr != end; ++itr) + { + QueryUUIDToProfile(*itr); + } + } +} + + + + -- cgit v1.2.3