#include #pragma comment(lib, "ws2_32.lib") #include #include #include #include #include #include #include #include #include #include #include "leaderboards.h" struct Scores { long long id; int score; int rank; Scores(const long long id, const int score, const int rank) : id(id), score(score), rank(rank) {} }; struct Users { std::string name; std::string url; std::string avatar; Users() : name(""), url(""), avatar("") {} Users(const std::string& name, const std::string& url, const std::string& avatar) : name(name), url(url), avatar(avatar) {} }; std::map users; class Connection { std::string hostname; const hostent* const hoste; const unsigned int* const* addrptr; SOCKET sock; bool keeping; bool addr; public: Connection(const std::string& host) : hostname(host), hoste(gethostbyname(host.c_str())), keeping(false), addr(false) { if (!hoste) throw WSAGetLastError() == WSAHOST_NOT_FOUND ? hostname + " not found" : std::string("gethostbyname failed"); addrptr = reinterpret_cast(hoste->h_addr_list); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) throw std::string("socket failed\n"); setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&[](){return TRUE;}), sizeof(BOOL)); } ~Connection() { if (keeping) close(); closesocket(sock); } void open() { sockaddr_in server = {AF_INET, htons(80)}; if (addr) { server.sin_addr.S_un.S_addr = **addrptr; if (connect(sock, reinterpret_cast(&server), sizeof(server))) throw std::string("connect failed\n"); } else { for (; *addrptr; ++addrptr) { server.sin_addr.S_un.S_addr = **addrptr; if (!connect(sock, reinterpret_cast(&server), sizeof(server))) break; } if (!*addrptr) throw std::string("connect failed\n"); addr = true; } } void close() { shutdown(sock, SD_BOTH); } void req(const std::string& request, const bool keep = false) { if (!keeping) open(); const std::string req(std::string("GET ") + request + " HTTP/1.1\r\nHost: " + hostname + "\r\nConnection: " + (keep ? "Keep-alive" : "Close") + "\r\n\r\n"); int n = send(sock, req.c_str(), req.length(), 0); if (n < 0) throw std::string("send to ") + hostname + " failed\n"; } std::string get() { int n = 1; int length = 0; std::string res; while (n > 0) { char buf[257] = {}; n = recv(sock, buf, length ? min(sizeof(buf) - 1, length) : sizeof(buf) - 1, 0); if (n < 0) throw std::string("recv from ") + hostname + " failed\n"; res += buf; if (length) { length -= n; if (length <= 0) { break; } } else { auto end = res.find("\r\n\r\n"); if (end != std::string::npos) { auto pos = res.find("Content-Length:"); length = std::atoi(res.c_str() + pos + 16); length -= res.length() - end - 4; } } } for (auto i = res.find('\r'); i != std::string::npos; i = res.find('\r', i)) res.erase(i, 1); auto pos = res.find("Connection:"); keeping = res[pos + 12] == 'K' || res[pos + 12] == 'k'; if (!keeping) close(); return res; } }; int main(int argc, const char** argv) { const char* const querry = getenv("QUERY_STRING"); std::map querrys; if (querry) { size_t idx = 0; std::string q(querry); do { size_t key = q.find('=', idx); size_t val = q.find('&', idx); querrys.insert(std::make_pair( q.substr(idx, key == std::string::npos ? val == std::string::npos ? val : val - idx : key - idx), key == std::string::npos ? "" : q.substr(key + 1, val == std::string::npos ? val : val - key - 1))); } while(idx = q.find('&', idx) + 1); } if (querrys.find("id") != querrys.end() && querrys["id"].find_first_not_of("0123456789") != std::string::npos) { unsigned long long id = 0; WSADATA wsaData; if (WSAStartup(MAKEWORD(2,0), &wsaData)) throw std::string("WSAStartup failed\n"); try { Connection con("steamcommunity.com"); con.req((boost::format("/id/%s/?xml=1") % querrys["id"]).str()); std::istringstream res(con.get()); while (!res.eof()) if (res.get() == '\n') if (res.get() == '\n') break; boost::property_tree::ptree t; read_xml(res, t); id = t.get("profile.steamID64"); } catch(const std::string& e) { std::cout << e << std::endl; } catch(const std::exception& e) { std::cout << e.what() << std::endl; } WSACleanup(); if (id) std::cout << "Location: portal2_lb.cgi?id=" << id; else std::cout << "Location: portal2_lb.cgi"; bool f = id != 0; std::for_each(querrys.begin(), querrys.end(), [&](const std::pair& q) { if (q.first == "id") return; std::cout << (f ? '&' : '?') << q.first << '=' << q.second; if (f) f = true; }); std::cout << std::endl << std::endl; return 0; } unsigned long long userid = 0; if (querrys.find("id") != querrys.end()) userid = _atoi64(querrys["id"].c_str()); unsigned int page = 0; if (querrys.find("page") != querrys.end()) page = static_cast(atoi(querrys["page"].c_str())); if (page > sizeof(chapters) / sizeof(Chapters)) page = 0; std::cout << "Content-Type: text/html; charset=UTF-8" << std::endl << std::endl; std::cout << "\n" "\n" "\n" "\t\n" "\tPortal 2 Leaderboards\n" "\t\n" "\n" "\n" "
" << std::endl; std::cout << "
Portal 2 Leaderboards - " << chapters[page].name << "
\n" << std::endl; try { WSADATA wsaData; if (WSAStartup(MAKEWORD(2,0), &wsaData)) throw std::string("WSAStartup failed\n"); try { Connection api("api.steampowered.com"); if (userid) { api.req((boost::format("/ISteamUser/GetPlayerSummaries/v0002/?key=8A703C801079AD7771F83BB371538F3C&steamids=%d&format=json") % userid).str(), true); std::istringstream res(api.get()); while (!res.eof()) if (res.get() == '\n') if (res.get() == '\n') break; boost::property_tree::ptree t; read_json(res, t); const auto& players = t.get_child("response.players"); if (players.size()) { const auto& player = players.front().second; users.insert(std::make_pair(player.get("steamid"), Users(player.get("personaname"), player.get("profileurl"), player.get("avatar")))); }else { userid = 0; } } if (userid) { std::cout << "
" << std::endl; std::cout << "
" << users[userid].name << " and friends' scores
" << std::endl; } std::cout << "
" << std::endl; std::cout << "
" << std::endl; std::cout << "" << std::endl; if (userid) std::cout << "" << std::endl; else std::cout << "" << std::endl; std::for_each(querrys.begin(), querrys.end(), [&](const std::pair& q) { if (q.first == "id") return; std::cout << "" << std::endl; }); std::cout << "" << std::endl; std::cout << "" << std::endl; std::cout << "
" << std::endl; if (page) { std::cout << "" << std::endl; } else std::cout << "
<< Prev.
" << std::endl; if (page != sizeof(chapters) / sizeof(Chapters) - 1) { std::cout << "" << std::endl; } else std::cout << "
Next >>
" << std::endl; std::cout << "
" << std::endl; std::cout << "Single:" << std::endl; for (int i = 0; i < sizeof(chapters) / sizeof(Chapters); ++i) { if (i == 9) std::cout << " Coop:" << std::endl; std::cout << ' ' << std::endl; if (i != page) { std::cout << "& q) { if (q.first == "page") return; std::cout << '&' << q.first << '=' << q.second; }); std::cout << "\""; } else std::cout << "[" << (i < 9 ? i + 1 : i - 8) << (i == page ? "]" : "]") << std::endl; } std::cout << "
" << std::endl; std::cout << "
" << std::endl; std::string querryf; std::for_each(querrys.begin(), querrys.end(), [&](const std::pair& q) { if (q.first == "id") return; querryf += '&'; querryf += q.first; querryf += '='; querryf += q.second; }); std::unique_ptr com(userid ? new Connection("steamcommunity.com") : nullptr); api.req((boost::format("/IPortal2Leaderboards_620/GetBucketizedData/v0001/?key=8A703C801079AD7771F83BB371538F3C&leaderboardName=challenge_%s_%s&format=json&appid=620") % "portals" % leaderboards[chapters[page].begin].map).str(), true); if (com) com->req((boost::format("/stats/Portal2/leaderboards/%d/?xml=1&steamid=%d") % leaderboards[chapters[page].begin].portals % userid).str(), true); for (unsigned int i = chapters[page].begin; i < chapters[page].end; ++i) { for (unsigned int j = 0; j < 2u; ++j) { std::istringstream res(api.get()); while (!res.eof()) if (res.get() == '\n') if (res.get() == '\n') break; boost::property_tree::ptree t; read_json(res, t); std::cout << "
" << std::endl; std::cout << "
" << leaderboards[i].name << ": " << (j ? "Time" : "Portals") << "
\n" << std::endl; const int num = t.get("bucketizedData.bucketCount"); const int ent = t.get("bucketizedData.numEntries"); std::cout << "
" << std::endl; const auto& buckets = t.get_child("bucketizedData.buckets"); std::vector val; const auto min = t.get("bucketizedData.min"); const auto max = t.get("bucketizedData.max"); int peak = 1; int avg = (j ? max / 100 : max * 10); val.reserve(num); std::for_each(buckets.begin(), buckets.end(), [&](const boost::property_tree::ptree::value_type& v) { const auto n = v.second.get("playerCount"); val.push_back(n); if (n > peak) peak = n; }); int total = 0; for (unsigned int k = 0; k < val.size(); ++k) { total += val[k]; if (avg == (j ? max / 100 : max * 10)) if (total > ent / 2) { if (j) avg = k * 10 - (total - ent / 2) * 10 / val[k] + 10; else avg = k * 9 - (total - ent / 2) * 9 / val[k] + 9; } if (j) std::cout << "
(val[k]) * 100.0f / static_cast(ent)) % (static_cast(total) * 100.0f / static_cast(ent)) << "\">
" << std::endl; else std::cout << "
(val[k]) * 100.0f / static_cast(ent)) % (static_cast(total) * 100.0f / static_cast(ent)) << "\">
" << std::endl; } std::cout << "
" << std::endl; if (com) { std::istringstream res(com->get()); if (i + j != chapters[page].end) com->req((boost::format("/stats/Portal2/leaderboards/%d/?xml=1&steamid=%d") % (j ? leaderboards[i + 1].portals : leaderboards[i].time) % userid).str(), i != chapters[page].end - 1); while (!res.eof()) if (res.get() == '\n') if (res.get() == '\n') break; boost::property_tree::ptree t; read_xml(res, t); const auto& entries = t.get_child("response.entries"); const int usernum = t.get("response.resultCount"); const int total = t.get("response.totalLeaderboardEntries"); std::vector scores; std::vector req; scores.reserve(usernum); std::for_each(entries.begin(), entries.end(), [&](const boost::property_tree::ptree::value_type& v) { const unsigned long long id = v.second.get("steamid"); if (users.find(id) == users.end()) req.push_back(id); scores.push_back(Scores(id, v.second.get("score"), v.second.get("rank"))); }); if (req.size()) { api.req((boost::format("/ISteamUser/GetPlayerSummaries/v0002/?key=8A703C801079AD7771F83BB371538F3C&steamids=%s&format=json") % [&]()->std::string { std::string str; std::for_each(req.begin(), req.end(), [&](const unsigned long long v){str += (boost::format(",%d") % v).str();}); str.erase(0, 1); return str; }()).str(), true); std::istringstream res(api.get()); while (!res.eof()) if (res.get() == '\n') if (res.get() == '\n') break; read_json(res, t); const auto& players = t.get_child("response.players"); std::for_each(players.begin(), players.end(), [&](const boost::property_tree::ptree::value_type& v) { users.insert(std::make_pair(v.second.get("steamid"), Users(v.second.get("personaname"), v.second.get("profileurl"), v.second.get("avatar")))); }); } std::cout << "
" << std::endl; int slide = 0; for (unsigned int k = scores.size(); k--;) { if (scores[k].score > max) continue; if (scores[k].score < min) continue; const Users& user = users[scores[k].id]; const unsigned int l = scores.size() - k - 1; if (j) std::cout << "
" << std::endl; else std::cout << "
" << std::endl; slide += 160 + k * 40; } std::cout << "
" << std::endl; std::cout << "
" << std::endl; if (j) { std::cout << "
" << boost::format("%d:%02d") % (min / 6000) % (min % 6000 / 100) << "
" << std::endl; std::cout << "
" << boost::format("%d:%02d") % (max / 6000) % (max % 6000 / 100) << "
" << std::endl; std::cout << "
" << ent << " players / avg. " << boost::format("%d:%02d") % (avg / 60) % (avg % 60) << "
" << std::endl; } else { std::cout << "
" << min << "
" << std::endl; std::cout << "
" << max << "
" << std::endl; std::cout << "
" << ent << " players / avg. " << avg / 10 << "." << avg % 10 << " portals
" << std::endl; } std::cout << "
" << std::endl; std::cout << "
" << std::endl; for (unsigned int k = 0; k < scores.size(); ++k) { const Users& user = users[scores[k].id]; std::cout << (scores[k].id == userid ? "
" : "
") << std::endl; if (scores[k].rank <= 200) std::cout << "" << std::endl; else std::cout << "
#" << scores[k].rank << "
" << std::endl; std::cout << "
" << std::endl; if (scores[k].id == userid) std::cout << "
" << user.name << "
" << std::endl; else std::cout << "" << std::endl; if (j) std::cout << "
" << boost::format("%d:%02d.%02d") % (scores[k].score / 6000) % (scores[k].score % 6000 / 100) % (scores[k].score % 100) << "
" << std::endl; else std::cout << "
" << scores[k].score << "
" << std::endl; std::cout << "
" << std::endl; } std::cout << "
" << std::endl; } else { std::cout << "
" << std::endl; if (j) { std::cout << "
" << boost::format("%d:%02d") % (min / 6000) % (min % 6000 / 100) << "
" << std::endl; std::cout << "
" << boost::format("%d:%02d") % (max / 6000) % (max % 6000 / 100) << "
" << std::endl; std::cout << "
" << ent << " players / avg. " << boost::format("%d:%02d") % (avg / 60) % (avg % 60) << "
" << std::endl; } else { std::cout << "
" << min << "
" << std::endl; std::cout << "
" << max << "
" << std::endl; std::cout << "
" << ent << " players / avg. " << avg / 10 << "." << avg % 10 << " portals
" << std::endl; } std::cout << "
" << std::endl; } if (i + j != chapters[page].end) api.req((boost::format("/IPortal2Leaderboards_620/GetBucketizedData/v0001/?key=8A703C801079AD7771F83BB371538F3C&leaderboardName=challenge_%s_%s&format=json&appid=620") % (j ? "portals" : "besttime") % leaderboards[i + j].map).str(), i != sizeof(leaderboards) / sizeof(Leaderboards) - 1); std::cout << "
" << std::endl; } std::cout << "
" << std::endl; } } catch(const std::string& e) { std::cout << e << std::endl; } catch(const std::exception& e) { std::cout << e.what() << std::endl; } WSACleanup(); } catch(const std::string& e) { std::cout << "
Error: " << e << "
" << std::endl; } std::cout << "
" << std::endl; if (page) { std::cout << "" << std::endl; } else std::cout << "
<< Prev.
" << std::endl; if (page != sizeof(chapters) / sizeof(Chapters) - 1) { std::cout << "" << std::endl; } else std::cout << "
Next >>
" << std::endl; std::cout << "
" << std::endl; std::cout << "Single:" << std::endl; for (int i = 0; i < sizeof(chapters) / sizeof(Chapters); ++i) { if (i == 9) std::cout << " Coop:" << std::endl; std::cout << ' ' << std::endl; if (i != page) { std::cout << "& q) { if (q.first == "page") return; std::cout << '&' << q.first << '=' << q.second; }); std::cout << "\""; } else std::cout << "[" << (i < 9 ? i + 1 : i - 8) << (i == page ? "]" : "]") << std::endl; } std::cout << "
" << std::endl; std::cout << "
" << std::endl; std::cout << "
\n" "
2011 Ryosuke839
\n" "
\n" "\n" "" << std::endl; return 0; }