00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <common/StatsLoggerDatabase.h>
00022 #include <common/OptionsScorched.h>
00023 #include <common/Defines.h>
00024 #include <common/Logger.h>
00025 #include <net/NetInterface.h>
00026 #include <server/ServerCommon.h>
00027 #include <weapons/AccessoryStore.h>
00028 #include <server/ScorchedServer.h>
00029 #include <tank/TankState.h>
00030 #include <tank/TankScore.h>
00031 #include <tank/TankAvatar.h>
00032 #include <XML/XMLFile.h>
00033 #include <stdlib.h>
00034 #include <time.h>
00035 #include <math.h>
00036
00037 enum EventType
00038 {
00039 EventKill = 1,
00040 EventTeamKill = 2,
00041 EventSelfKill = 3,
00042 EventResigned = 4,
00043 EventWon = 5,
00044 EventOverallWinner = 6,
00045 EventConnected = 7,
00046 EventDisconnected = 8,
00047 EventJoined = 9
00048 };
00049
00050 const char *StatsLoggerDatabase::RowResult::getValue(const char *name)
00051 {
00052 std::map<std::string, unsigned int>::iterator findItor =
00053 names.find(name);
00054 if (findItor == names.end()) return 0;
00055 unsigned int pos = findItor->second;
00056 return columns[pos].c_str();
00057 }
00058
00059 StatsLoggerDatabase::StatsLoggerDatabase() :
00060 success_(false),
00061 serverid_(0), seriesid_(0), prefixid_(0),
00062 updateTime_(0)
00063 {
00064 }
00065
00066 StatsLoggerDatabase::~StatsLoggerDatabase()
00067 {
00068 }
00069
00070 void StatsLoggerDatabase::createLogger()
00071 {
00072 if (success_) return;
00073
00074 XMLFile file;
00075 std::string fileName = S3D::getSettingsFile(S3D::formatStringBuffer("mysql-%i.xml",
00076 ScorchedServer::instance()->getOptionsGame().getPortNo()));
00077
00078 std::string host, port, user, passwd, db, prefix;
00079 if (!file.readFile(fileName) ||
00080 !file.getRootNode())
00081 {
00082 S3D::dialogExit("Stats Logging",
00083 S3D::formatStringBuffer(
00084 "Failed to parse %s settings file. Error: %s",
00085 fileName.c_str(),
00086 file.getParserError()));
00087 return;
00088 }
00089
00090 if (!file.getRootNode()->getNamedChild("host", host) ||
00091 !file.getRootNode()->getNamedChild("port", port) ||
00092 !file.getRootNode()->getNamedChild("user", user) ||
00093 !file.getRootNode()->getNamedChild("passwd", passwd) ||
00094 !file.getRootNode()->getNamedChild("db", db) ||
00095 !file.getRootNode()->getNamedChild("prefix", prefix))
00096 {
00097 S3D::dialogExit("Stats Logging",
00098 S3D::formatStringBuffer(
00099 "Failed to parse %s settings file.", fileName.c_str()));
00100 return;
00101 }
00102
00103
00104 success_ = false;
00105 if (!connectDatabase(host.c_str(), port.c_str(),
00106 user.c_str(), passwd.c_str(),
00107 db.c_str()))
00108 {
00109 S3D::dialogExit("Stats Logging",
00110 "Failed to connect to stats database");
00111 return;
00112 }
00113 success_ = true;
00114
00115
00116 runQuery("INSERT INTO scorched3d_eventtypes "
00117 "(eventtype, name) VALUES "
00118 "(%i, \"KILL\"), "
00119 "(%i, \"TEAMKILL\"), "
00120 "(%i, \"SELFKILL\"), "
00121 "(%i, \"WON\"), "
00122 "(%i, \"OVERALLWINNER\"), "
00123 "(%i, \"CONNECTED\"), "
00124 "(%i, \"DISCONNECTED\"), "
00125 "(%i, \"JOINED\"), "
00126 "(%i, \"RESIGNED\"); ",
00127 EventKill, EventTeamKill, EventSelfKill,
00128 EventWon, EventOverallWinner,
00129 EventConnected, EventDisconnected,
00130 EventJoined, EventResigned);
00131
00132
00133 std::list<StatsLoggerDatabase::RowResult> prefixRows =
00134 runSelectQuery("SELECT prefixid FROM scorched3d_prefixs "
00135 "WHERE prefix = \"%s\";",
00136 prefix.c_str());
00137 if (!prefixRows.empty())
00138 {
00139 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00140 for (itor = prefixRows.begin();
00141 itor != prefixRows.end();
00142 itor++)
00143 {
00144 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00145 prefixid_ = atoi(rowResult.columns[0].c_str());
00146 }
00147 }
00148 if (prefixid_ == 0)
00149 {
00150 if (runQuery("INSERT INTO scorched3d_prefixs "
00151 "(prefix) VALUES(\"%s\");",
00152 prefix.c_str()))
00153 {
00154 prefixid_ = getLastInsertId();
00155 }
00156 }
00157
00158
00159 std::list<StatsLoggerDatabase::RowResult> serverIdRows =
00160 runSelectQuery("SELECT serverid, displaystats FROM scorched3d_servers "
00161 "WHERE name = \"%s\";",
00162 ScorchedServer::instance()->getOptionsGame().getServerName());
00163 if (!serverIdRows.empty())
00164 {
00165 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00166 for (itor = serverIdRows.begin();
00167 itor != serverIdRows.end();
00168 itor++)
00169 {
00170 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00171 serverid_ = atoi(rowResult.columns[0].c_str());
00172 displayStats_ = (atoi(rowResult.columns[1].c_str()) != 0);
00173 }
00174 }
00175 if (serverid_ == 0)
00176 {
00177 if (runQuery("INSERT INTO scorched3d_servers "
00178 "(name, published) VALUES(\"%s\", \"%s\");",
00179 ScorchedServer::instance()->getOptionsGame().getServerName(),
00180 ScorchedServer::instance()->getOptionsGame().getPublishAddress()))
00181 {
00182 serverid_ = getLastInsertId();
00183 displayStats_ = true;
00184 }
00185 }
00186
00187
00188 std::list<StatsLoggerDatabase::RowResult> seriesIdRows =
00189 runSelectQuery("SELECT seriesid FROM scorched3d_series "
00190 "WHERE type = 0;");
00191 if (!seriesIdRows.empty())
00192 {
00193 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00194 for (itor = seriesIdRows.begin();
00195 itor != seriesIdRows.end();
00196 itor++)
00197 {
00198 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00199 seriesid_ = atoi(rowResult.columns[0].c_str());
00200 }
00201 }
00202 if (seriesid_ == 0)
00203 {
00204 if (runQuery("INSERT INTO scorched3d_series "
00205 "(started, ended) VALUES(NOW(), NOW());"))
00206 {
00207 seriesid_ = getLastInsertId();
00208 }
00209 }
00210
00211
00212 runQuery("INSERT INTO scorched3d_statssource "
00213 "(serverid, prefixid, seriesid) VALUES "
00214 "(%i, %i, %i);",
00215 serverid_, prefixid_, seriesid_);
00216
00217
00218
00219 std::list<Accessory *> weapons =
00220 ScorchedServer::instance()->getAccessoryStore().getAllAccessories();
00221 std::list<Accessory *>::iterator itor;
00222 for (itor = weapons.begin();
00223 itor != weapons.end();
00224 itor++)
00225 {
00226 Accessory *accessory = *itor;
00227
00228 int weaponId = 0;
00229 std::list<StatsLoggerDatabase::RowResult> weaponIdRows =
00230 runSelectQuery("SELECT weaponid FROM scorched3d_weapons "
00231 "WHERE name = \"%s\" AND seriesid = %i AND prefixid = %i;",
00232 accessory->getName(),
00233 seriesid_,
00234 prefixid_);
00235 if (!weaponIdRows.empty())
00236 {
00237 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00238 for (itor = weaponIdRows.begin();
00239 itor != weaponIdRows.end();
00240 itor++)
00241 {
00242 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00243 weaponId = atoi(rowResult.columns[0].c_str());
00244 }
00245 }
00246
00247 if (weaponId == 0)
00248 {
00249 if (runQuery("INSERT INTO scorched3d_weapons "
00250 "(seriesid, prefixid, name, description, armslevel, cost, bundlesize, icon) "
00251 "VALUES(%i, %i, \"%s\", \"%s\", %i, %i, %i, \"%s\");",
00252 seriesid_,
00253 prefixid_,
00254 accessory->getName(),
00255 accessory->getDescription(),
00256 accessory->getArmsLevel(),
00257 accessory->getOriginalPrice(),
00258 accessory->getBundle(),
00259 accessory->getIconName()))
00260 {
00261 weaponId = getLastInsertId();
00262 }
00263 }
00264 else
00265 {
00266 runQuery("UPDATE scorched3d_weapons SET "
00267 "description = \"%s\", "
00268 "armslevel = %i, "
00269 "cost = %i, "
00270 "bundlesize = %i, "
00271 "icon = \"%s\" "
00272 "WHERE name = \"%s\" AND seriesid = %i AND prefixid = %i;",
00273 accessory->getDescription(),
00274 accessory->getArmsLevel(),
00275 accessory->getOriginalPrice(),
00276 accessory->getBundle(),
00277 accessory->getIconName(),
00278 accessory->getName(),
00279 seriesid_,
00280 prefixid_);
00281 }
00282
00283 weaponId_[accessory->getName()] = weaponId;
00284 }
00285
00286 Logger::log(S3D::formatStringBuffer("database stats logger started, prefix=%i, server=%i, series=%i",
00287 prefixid_, serverid_, seriesid_));
00288 periodicUpdate();
00289 }
00290
00291 void StatsLoggerDatabase::addIpAliases(int playerId,
00292 std::set<int> ¤tPlayers, std::list<std::string> &results)
00293 {
00294 currentPlayers.insert(playerId);
00295 addAliases(playerId, results);
00296
00297 std::list<std::string> ipaddresses;
00298 std::list<StatsLoggerDatabase::RowResult> ipaddressesRows =
00299 runSelectQuery("SELECT ipaddress FROM scorched3d_ipaddress "
00300 "WHERE playerid = %i;", playerId);
00301 if (!ipaddresses.empty())
00302 {
00303 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00304 for (itor = ipaddressesRows.begin();
00305 itor != ipaddressesRows.end();
00306 itor++)
00307 {
00308 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00309 ipaddresses.push_back(rowResult.columns[0]);
00310 }
00311 }
00312
00313 std::list<std::string>::iterator itor;
00314 for (itor = ipaddresses.begin();
00315 itor != ipaddresses.end();
00316 itor++)
00317 {
00318 const char *ipaddress = (*itor).c_str();
00319 std::list<int> newplayers;
00320
00321 std::list<StatsLoggerDatabase::RowResult> ipaddressRows =
00322 runSelectQuery("SELECT playerid FROM scorched3d_ipaddress "
00323 "WHERE ipaddress = \"%s\";", ipaddress);
00324 if (!ipaddressRows.empty())
00325 {
00326 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00327 for (itor = ipaddressRows.begin();
00328 itor != ipaddressRows.end();
00329 itor++)
00330 {
00331 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00332 int newplayerid = atoi(rowResult.columns[0].c_str());
00333 if (currentPlayers.find(newplayerid) == currentPlayers.end())
00334 {
00335 newplayers.push_back(newplayerid);
00336 }
00337 }
00338 }
00339
00340 std::list<int>::iterator itor2;
00341 for (itor2 = newplayers.begin();
00342 itor2 != newplayers.end();
00343 itor2++)
00344 {
00345 addIpAliases((*itor2), currentPlayers, results);
00346 }
00347 }
00348 }
00349
00350 std::string StatsLoggerDatabase::getTopRanks()
00351 {
00352 createLogger();
00353 if (!success_) return "";
00354
00355 const char *columns =
00356 "rank, kills, deaths, selfkills, teamkills, shots, wins, "
00357 "overallwinner, resigns, gamesplayed, timeplayed, roundsplayed, "
00358 "moneyearned, skill, name";
00359
00360 std::string stringResult;
00361 std::list<StatsLoggerDatabase::RowResult> rankRows =
00362 runSelectQuery(
00363 "select %s from scorched3d_stats "
00364 "left join scorched3d_players on scorched3d_stats.playerid = "
00365 "scorched3d_players.playerid where seriesid=%i and prefixid=%i "
00366 "order by skill desc limit 0,50",
00367 columns, seriesid_, prefixid_);
00368
00369 stringResult.append("<table>");
00370
00371 std::string cols(columns);
00372 char *token = strtok((char *) cols.c_str(), " ");
00373 stringResult.append("<tr>");
00374 while(token != 0)
00375 {
00376 stringResult.append("<td><b>").append(token).append("</b></td>");
00377 token = strtok(0, " ");
00378 }
00379 stringResult.append("</tr>");
00380
00381 if (!rankRows.empty())
00382 {
00383 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00384 for (itor = rankRows.begin();
00385 itor != rankRows.end();
00386 itor++)
00387 {
00388 StatsLoggerDatabase::RowResult &result = (*itor);
00389 stringResult.append("<tr>");
00390 for (unsigned int i=0; i<result.columns.size(); i++)
00391 {
00392 stringResult.append("<td>").append(result.columns[i]).append("</td>");
00393 }
00394 stringResult.append("</tr>");
00395 }
00396 }
00397 stringResult.append("</table>");
00398 return S3D::formatStringBuffer("%s", stringResult.c_str());
00399 }
00400
00401 std::string StatsLoggerDatabase::getPlayerInfo(const char *player)
00402 {
00403 createLogger();
00404 if (!success_) return "";
00405
00406 std::string stringResult;
00407 std::list<StatsLoggerDatabase::RowResult> playerRows =
00408 runSelectQuery("select "
00409 "scorched3d_names.playerid as playerid, "
00410 "scorched3d_names.name as name, "
00411 "uniqueid from "
00412 "scorched3d_names left join "
00413 "scorched3d_players on scorched3d_players.playerid = scorched3d_names.playerid "
00414 "where LOCATE(LOWER(\"%s\"), LOWER(scorched3d_names.name)) != 0 limit 0,50",
00415 player);
00416 if (!playerRows.empty())
00417 {
00418 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00419 for (itor = playerRows.begin();
00420 itor != playerRows.end();
00421 itor++)
00422 {
00423 StatsLoggerDatabase::RowResult &result = (*itor);
00424 for (unsigned int i=0; i<result.columns.size(); i++)
00425 {
00426 stringResult.append(result.columns[i]);
00427 if (i < result.columns.size() - 1) stringResult.append(",");
00428 else stringResult.append("\n");
00429 }
00430 }
00431 }
00432
00433 return S3D::formatStringBuffer("%s", stringResult.c_str());
00434 }
00435
00436 void StatsLoggerDatabase::combinePlayers(unsigned int player1, unsigned int player2)
00437 {
00438 createLogger();
00439 if (!success_) return;
00440
00441
00442 std::list<StatsLoggerDatabase::RowResult> player1Rows =
00443 runSelectQuery("select name, uniqueid from "
00444 "scorched3d_players where playerid=%i",
00445 player1);
00446 std::list<StatsLoggerDatabase::RowResult> player2Rows =
00447 runSelectQuery("select name, uniqueid from "
00448 "scorched3d_players where playerid=%i",
00449 player2);
00450 if (player1Rows.empty()) return;
00451 if (player2Rows.empty()) return;
00452
00453
00454 std::list<StatsLoggerDatabase::RowResult> player2Results =
00455 runSelectQuery("select * from scorched3d_stats where playerid=%i",
00456 player2);
00457 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00458 for (itor = player2Results.begin();
00459 itor != player2Results.end();
00460 itor++)
00461 {
00462 StatsLoggerDatabase::RowResult &player2Result = *itor;
00463 const char *prefixId = player2Result.getValue("prefixid");
00464 const char *seriesId = player2Result.getValue("seriesid");
00465 if (prefixId && seriesId)
00466 {
00467
00468 std::list<StatsLoggerDatabase::RowResult> player1Results =
00469 runSelectQuery("select * from scorched3d_stats where playerid=%i AND "
00470 "prefixid=%s and seriesid=%s",
00471 player1, prefixId, seriesId);
00472 if (player1Results.empty())
00473 {
00474
00475 runQuery("update scorched3d_stats set playerid=%i where "
00476 "playerid=%i and prefixid=%s and seriesid=%s",
00477 player1, player2,
00478 prefixId, seriesId);
00479 }
00480 else
00481 {
00482
00483 StatsLoggerDatabase::RowResult &player1Result = player1Results.front();
00484 std::map<std::string, unsigned int>::iterator itor;
00485 for (itor = player1Result.names.begin();
00486 itor != player1Result.names.end();
00487 itor++)
00488 {
00489 std::string name = itor->first;
00490 std::string value1 = player1Result.getValue(name.c_str());
00491 std::string value2 = player2Result.getValue(name.c_str());
00492
00493 std::string query = "update scorched3d_stats ";
00494 if (name == "playerid" ||
00495 name == "prefixid" ||
00496 name == "seriesid" ||
00497 name == "lastconnected" ||
00498 name == "rank" ||
00499 name == "skill")
00500 {
00501
00502 }
00503 else
00504 {
00505 int v1 = atoi(value1.c_str());
00506 int v2 = atoi(value2.c_str());
00507 int value = v1 + v2;
00508 query.append(S3D::formatStringBuffer("set %s=%i ", name.c_str(), value));
00509 }
00510 query.append(
00511 S3D::formatStringBuffer("where playerid=%i and prefixid=%s and seriesid=%s",
00512 player1, prefixId, seriesId));
00513 runQuery(query.c_str());
00514 }
00515 }
00516 }
00517 }
00518
00519
00520 runQuery("delete from scorched3d_players where playerid=%i", player2);
00521 runQuery("delete from scorched3d_stats where playerid=%i", player2);
00522 runQuery("delete from scorched3d_names where playerid=%i", player2);
00523 runQuery("delete from scorched3d_ipaddresses where playerid=%i", player2);
00524 }
00525
00526 std::list<std::string> StatsLoggerDatabase::getIpAliases(const char *unqiueId)
00527 {
00528 std::list<std::string> results;
00529 createLogger();
00530 if (!success_) return results;
00531
00532 int playerId = getPlayerId(unqiueId);
00533 if (playerId == 0) return results;
00534
00535 std::set<int> currentPlayers;
00536 addIpAliases(playerId, currentPlayers, results);
00537
00538 return results;
00539 }
00540
00541 static bool findInList(std::list<std::string> &results,
00542 const char *name)
00543 {
00544 std::list<std::string>::iterator itor;
00545 for (itor = results.begin();
00546 itor != results.end();
00547 itor++)
00548 {
00549 if (0 == strcmp(name, (*itor).c_str())) return true;
00550 }
00551 return false;
00552 }
00553
00554 void StatsLoggerDatabase::addAliases(int playerId,
00555 std::list<std::string> &results)
00556 {
00557
00558
00559 {
00560 std::list<StatsLoggerDatabase::RowResult> nameRows =
00561 runSelectQuery("SELECT name FROM scorched3d_players "
00562 "WHERE playerid = %i;", playerId);
00563 if (!nameRows.empty())
00564 {
00565 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00566 for (itor = nameRows.begin();
00567 itor != nameRows.end();
00568 itor++)
00569 {
00570 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00571 if (!findInList(results, rowResult.columns[0].c_str()))
00572 {
00573 results.push_back(rowResult.columns[0].c_str());
00574 }
00575 }
00576 }
00577 }
00578
00579
00580 {
00581 std::list<StatsLoggerDatabase::RowResult> nameRows =
00582 runSelectQuery("SELECT name FROM scorched3d_names "
00583 "WHERE playerid = %i;", playerId);
00584 if (!nameRows.empty())
00585 {
00586 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00587 for (itor = nameRows.begin();
00588 itor != nameRows.end();
00589 itor++)
00590 {
00591 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00592 if (!findInList(results, rowResult.columns[0].c_str()))
00593 {
00594 results.push_back(rowResult.columns[0].c_str());
00595 }
00596 }
00597 }
00598 }
00599 }
00600
00601 std::list<std::string> StatsLoggerDatabase::getAliases(const char *unqiueId)
00602 {
00603 std::list<std::string> results;
00604 createLogger();
00605 if (!success_) return results;
00606
00607 int playerId = getPlayerId(unqiueId);
00608 if (playerId == 0) return results;
00609 addAliases(playerId, results);
00610
00611 return results;
00612 }
00613
00614 void StatsLoggerDatabase::gameStart(std::list<Tank *> &tanks)
00615 {
00616 createLogger();
00617 if (!success_) return;
00618
00619 runQuery("UPDATE scorched3d_series SET games = games + 1, "
00620 "ended = NOW() WHERE seriesid = %i;",
00621 seriesid_);
00622
00623 std::list<Tank *>::iterator itor;
00624 for (itor = tanks.begin();
00625 itor != tanks.end();
00626 itor++)
00627 {
00628 Tank *tank = *itor;
00629 if (!tank->getState().getSpectator())
00630 {
00631 runQuery("UPDATE scorched3d_stats SET gamesplayed=gamesplayed+1 "
00632 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
00633 playerId_[tank->getUniqueId()],
00634 prefixid_,
00635 seriesid_);
00636 }
00637 }
00638 }
00639
00640 void StatsLoggerDatabase::roundStart(std::list<Tank *> &tanks)
00641 {
00642 createLogger();
00643 if (!success_) return;
00644
00645 runQuery("UPDATE scorched3d_series SET rounds = rounds + 1, "
00646 "ended = NOW() WHERE seriesid = %i;",
00647 seriesid_);
00648
00649 std::list<Tank *>::iterator itor;
00650 for (itor = tanks.begin();
00651 itor != tanks.end();
00652 itor++)
00653 {
00654 Tank *tank = *itor;
00655 if (!tank->getState().getSpectator())
00656 {
00657 runQuery("UPDATE scorched3d_stats SET roundsplayed=roundsplayed+1 "
00658 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
00659 playerId_[tank->getUniqueId()],
00660 prefixid_,
00661 seriesid_);
00662 }
00663 }
00664 }
00665
00666 void StatsLoggerDatabase::tankFired(Tank *firedTank, Weapon *weapon)
00667 {
00668 createLogger();
00669 if (!success_) return;
00670
00671 runQuery("UPDATE scorched3d_stats SET shots=shots+1 "
00672 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
00673 playerId_[firedTank->getUniqueId()],
00674 prefixid_,
00675 seriesid_);
00676 }
00677
00678 void StatsLoggerDatabase::tankResigned(Tank *tank)
00679 {
00680 createLogger();
00681 if (!success_) return;
00682
00683 runQuery("INSERT INTO scorched3d_events "
00684 "(prefixid, seriesid, eventtype, playerid, otherplayerid, weaponid, eventtime) "
00685 "VALUES(%i, %i, %i, %i, 0, 0, NOW());",
00686 prefixid_, seriesid_,
00687 EventResigned,
00688 playerId_[tank->getUniqueId()]);
00689
00690 runQuery("UPDATE scorched3d_stats SET resigns=resigns+1 "
00691 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
00692 playerId_[tank->getUniqueId()],
00693 prefixid_,
00694 seriesid_);
00695 }
00696
00697 void StatsLoggerDatabase::updateStats(Tank *tank)
00698 {
00699 createLogger();
00700 if (!success_) return;
00701
00702 if (!tank->getState().getSpectator())
00703 {
00704 unsigned int playerId = playerId_[tank->getUniqueId()];
00705
00706 runQuery("UPDATE scorched3d_stats SET "
00707 "timeplayed=timeplayed+%i, moneyearned=moneyearned+%i, scoreearned=scoreearned+%i "
00708 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
00709 ((unsigned int) tank->getScore().getTimePlayedStat()),
00710 tank->getScore().getTotalMoneyEarnedStat(),
00711 tank->getScore().getTotalScoreEarnedStat(),
00712 playerId,
00713 prefixid_,
00714 seriesid_);
00715 }
00716 }
00717
00718 void StatsLoggerDatabase::periodicUpdate()
00719 {
00720 time_t currentTime = time(0);
00721 if (currentTime - updateTime_ > 60 * 60 * 12)
00722 {
00723 updateTime_ = currentTime;
00724 Logger::log(S3D::formatStringBuffer("statslogger database starting periodics"));
00725
00726
00727 std::list<StatsLoggerDatabase::RowResult> binaryRows =
00728 runSelectQuery("select binaryid, count(binaryid) "
00729 "from scorched3d_binary left join "
00730 "scorched3d_players on binaryid = avatarid group by avatarid");
00731 if (!binaryRows.empty())
00732 {
00733 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00734 for (itor = binaryRows.begin();
00735 itor != binaryRows.end();
00736 itor++)
00737 {
00738 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00739 if (rowResult.columns[1] == "0")
00740 {
00741 runQuery("delete from scorched3d_binary where binaryid = %s",
00742 rowResult.columns[0].c_str());
00743 }
00744 }
00745 }
00746
00747
00748 std::list<StatsLoggerDatabase::RowResult>::iterator playerItor;
00749 std::list<StatsLoggerDatabase::RowResult> playerRows =
00750 runSelectQuery(
00751 "SELECT playerid, skill from scorched3d_stats "
00752 "WHERE seriesid=%u and prefixid=%u order by skill desc",
00753 seriesid_,
00754 prefixid_);
00755
00756 int rank = 1;
00757 for (playerItor = playerRows.begin();
00758 playerItor != playerRows.end();
00759 playerItor++, rank++)
00760 {
00761 StatsLoggerDatabase::RowResult &playerRow = *playerItor;
00762 runQuery("UPDATE scorched3d_stats SET rank=%i "
00763 "WHERE seriesid=%u and prefixid=%u and playerid=%s",
00764 rank,
00765 seriesid_,
00766 prefixid_,
00767 playerRow.columns[0].c_str());
00768 }
00769
00770 Logger::log(S3D::formatStringBuffer("statslogger database finished periodics"));
00771 }
00772 }
00773
00774 StatsLogger::TankRank StatsLoggerDatabase::tankRank(Tank *tank)
00775 {
00776 TankRank result;
00777
00778 createLogger();
00779 if (!success_ || !displayStats_) return result;
00780
00781
00782 std::list<StatsLoggerDatabase::RowResult> skillRows =
00783 runSelectQuery("SELECT skill FROM scorched3d_stats "
00784 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
00785 playerId_[tank->getUniqueId()],
00786 prefixid_,
00787 seriesid_);
00788 if (!skillRows.empty())
00789 {
00790 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00791 for (itor = skillRows.begin();
00792 itor != skillRows.end();
00793 itor++)
00794 {
00795 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00796 result.skill = atoi(rowResult.columns[0].c_str());
00797 }
00798
00799 std::list<StatsLoggerDatabase::RowResult> countRows =
00800 runSelectQuery("SELECT count(*) FROM scorched3d_stats "
00801 "WHERE skill > \"%i\" AND prefixid = %i AND seriesid = %i;",
00802 result.skill,
00803 prefixid_,
00804 seriesid_);
00805 if (!countRows.empty())
00806 {
00807 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00808 for (itor = countRows.begin();
00809 itor != countRows.end();
00810 itor++)
00811 {
00812 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00813 result.rank = atoi(rowResult.columns[0].c_str()) + 1;
00814 }
00815 }
00816 }
00817
00818 return result;
00819 }
00820
00821 int StatsLoggerDatabase::getPlayerId(const char *uniqueId)
00822 {
00823 createLogger();
00824 if (!success_) return 0;
00825
00826
00827 int playerId = 0;
00828 std::list<StatsLoggerDatabase::RowResult> playerIdRows =
00829 runSelectQuery("SELECT playerid FROM scorched3d_players "
00830 "WHERE uniqueid = \"%s\";", uniqueId);
00831 if (!playerIdRows.empty())
00832 {
00833 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
00834 for (itor = playerIdRows.begin();
00835 itor != playerIdRows.end();
00836 itor++)
00837 {
00838 StatsLoggerDatabase::RowResult &rowResult = (*itor);
00839 playerId = atoi(rowResult.columns[0].c_str());
00840 }
00841 }
00842
00843 return playerId;
00844 }
00845
00846 unsigned int StatsLoggerDatabase::getStatsId(const char *uniqueId)
00847 {
00848 createLogger();
00849 if (!success_) return 0;
00850 int id = getPlayerId(uniqueId);
00851 return id;
00852 }
00853
00854 std::string StatsLoggerDatabase::allocateId()
00855 {
00856 const char possibleChars [] = {
00857 '1', '2', '3', '4', '5', '6', '7', '8', '9',
00858 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
00859 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S',
00860 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
00861 char buffer[128];
00862
00863 do
00864 {
00865 int pos = 0;
00866 for (int j=0; j<3; j++)
00867 {
00868 for (int i=0; i<8; i++)
00869 {
00870 buffer[pos++] = possibleChars[rand() % 33];
00871 }
00872 buffer[pos++] = '-';
00873 }
00874 buffer[pos - 1] = '\0';
00875
00876 } while (getPlayerId(buffer) != 0);
00877 return buffer;
00878 }
00879
00880 void StatsLoggerDatabase::addInfo(Tank *tank)
00881 {
00882 char playerName[1024];
00883 escapeString(playerName,
00884 tank->getCStrName().c_str(),
00885 tank->getCStrName().size());
00886
00887
00888 runQuery("INSERT INTO scorched3d_names (playerid, name, count) VALUES "
00889 "(%i, \"%s\", 0);",
00890 playerId_[tank->getUniqueId()],
00891 playerName);
00892 runQuery("UPDATE scorched3d_names SET count=count+1 WHERE "
00893 "playerid=%i AND name=\"%s\";",
00894 playerId_[tank->getUniqueId()],
00895 playerName);
00896
00897
00898 runQuery("INSERT INTO scorched3d_ipaddress (playerid, ipaddress, count) VALUES "
00899 "(%i, \"%s\", 0);",
00900 playerId_[tank->getUniqueId()],
00901 NetInterface::getIpName(tank->getIpAddress()));
00902 runQuery("UPDATE scorched3d_ipaddress SET count=count+1 WHERE "
00903 "playerid=%i AND ipaddress=\"%s\";",
00904 playerId_[tank->getUniqueId()],
00905 NetInterface::getIpName(tank->getIpAddress()));
00906
00907
00908 runQuery("UPDATE scorched3d_players SET "
00909 "name=\"%s\", ipaddress=\"%s\" "
00910 "WHERE playerid = %i;",
00911 playerName,
00912 NetInterface::getIpName(tank->getIpAddress()),
00913 playerId_[tank->getUniqueId()]);
00914 }
00915
00916 void StatsLoggerDatabase::tankConnected(Tank *tank)
00917 {
00918 createLogger();
00919 if (!success_) return;
00920
00921
00922 int playerId = getPlayerId(tank->getUniqueId());
00923 if (playerId == 0)
00924 {
00925 if (runQuery("INSERT INTO scorched3d_players (uniqueid) "
00926 "VALUES(\"%s\");",
00927 tank->getUniqueId()))
00928 {
00929 playerId = getLastInsertId();
00930 Logger::log(S3D::formatStringBuffer("Add new stats user \"%i\"", playerId));
00931 }
00932 }
00933 else
00934 {
00935 Logger::log(S3D::formatStringBuffer("Found stats user \"%i\"", playerId));
00936 }
00937
00938
00939 std::list<StatsLoggerDatabase::RowResult> playerIdRows =
00940 runSelectQuery("SELECT playerid FROM scorched3d_stats "
00941 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
00942 playerId,
00943 prefixid_,
00944 seriesid_);
00945 if (playerIdRows.empty())
00946 {
00947 runQuery("INSERT INTO scorched3d_stats (playerid, prefixid, seriesid) "
00948 "VALUES(%i, %i, %i);",
00949 playerId,
00950 prefixid_,
00951 seriesid_);
00952 }
00953
00954
00955 playerId_[tank->getUniqueId()] = playerId;
00956
00957
00958 addInfo(tank);
00959
00960
00961 runQuery("INSERT INTO scorched3d_events "
00962 "(prefixid, seriesid, eventtype, playerid, otherplayerid, weaponid, eventtime) "
00963 "VALUES(%i, %i, %i, %i, 0, 0, NOW());",
00964 prefixid_, seriesid_,
00965 EventConnected,
00966 playerId_[tank->getUniqueId()]);
00967
00968
00969 runQuery("UPDATE scorched3d_players SET osdesc=\"%s\" "
00970 "WHERE playerid = %i;",
00971 tank->getHostDesc(),
00972 playerId);
00973 runQuery("UPDATE scorched3d_stats SET connects=connects+1, "
00974 "lastconnected=NOW() "
00975 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
00976 playerId,
00977 prefixid_,
00978 seriesid_);
00979
00980 TankRank rank = StatsLogger::instance()->tankRank(tank);
00981 tank->getScore().setRank(rank.rank);
00982 tank->getScore().setSkill(rank.skill);
00983 }
00984
00985 void StatsLoggerDatabase::tankJoined(Tank *tank)
00986 {
00987 createLogger();
00988 if (!success_) return;
00989
00990
00991 runQuery("INSERT INTO scorched3d_events "
00992 "(prefixid, seriesid, eventtype, playerid, otherplayerid, weaponid, eventtime) "
00993 "VALUES(%i, %i, %i, %i, 0, 0, NOW());",
00994 prefixid_, seriesid_,
00995 EventJoined,
00996 playerId_[tank->getUniqueId()]);
00997
00998
00999 addInfo(tank);
01000
01001
01002 if (tank->getAvatar().getName()[0])
01003 {
01004 char buffer[32];
01005 for (int i=0; i<30; i++)
01006 {
01007 buffer[i] = tank->getAvatar().getName()[i];
01008 if (!buffer[i]) break;
01009 }
01010 buffer[30] = '\0';
01011
01012 int binaryid = 0;
01013 unsigned int crc = tank->getAvatar().getCrc();
01014 std::list<StatsLoggerDatabase::RowResult> binaryIdRows =
01015 runSelectQuery("SELECT binaryid FROM scorched3d_binary "
01016 "WHERE name = \"%s\" AND crc = %u;",
01017 buffer,
01018 crc);
01019 if (!binaryIdRows.empty())
01020 {
01021 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
01022 for (itor = binaryIdRows.begin();
01023 itor != binaryIdRows.end();
01024 itor++)
01025 {
01026 StatsLoggerDatabase::RowResult &rowResult = (*itor);
01027 binaryid = atoi(rowResult.columns[0].c_str());
01028 }
01029 }
01030
01031 if (binaryid == 0)
01032 {
01033 char *to = new char[tank->getAvatar().getFile().getBufferUsed() * 2];
01034 escapeString(to,
01035 tank->getAvatar().getFile().getBuffer(),
01036 tank->getAvatar().getFile().getBufferUsed());
01037 if (runQuery("INSERT INTO scorched3d_binary "
01038 "(name, crc, length, data) "
01039 "VALUES(\"%s\", %u, %u, \"%s\");",
01040 buffer,
01041 crc,
01042 tank->getAvatar().getFile().getBufferUsed(),
01043 to))
01044 {
01045 binaryid = getLastInsertId();
01046 }
01047
01048 delete [] to;
01049 }
01050
01051
01052 runQuery("UPDATE scorched3d_players SET avatarid = %i "
01053 "WHERE playerid = %i;",
01054 binaryid,
01055 playerId_[tank->getUniqueId()]);
01056 }
01057 }
01058
01059 int StatsLoggerDatabase::getKillCount(const char *uniqueId)
01060 {
01061 createLogger();
01062 if (!success_) return 0;
01063
01064 int kills = 0;
01065 int playerId = getPlayerId(uniqueId);
01066 if (playerId != 0)
01067 {
01068 std::list<StatsLoggerDatabase::RowResult> killsRows =
01069 runSelectQuery("SELECT kills FROM scorched3d_stats "
01070 "WHERE playerid = %i;",
01071 playerId);
01072 if (!killsRows.empty())
01073 {
01074 std::list<StatsLoggerDatabase::RowResult>::iterator itor;
01075 for (itor = killsRows.begin();
01076 itor != killsRows.end();
01077 itor++)
01078 {
01079 StatsLoggerDatabase::RowResult &rowResult = (*itor);
01080 kills += atoi(rowResult.columns[0].c_str());
01081 }
01082 }
01083 }
01084
01085 return kills;
01086 }
01087
01088 void StatsLoggerDatabase::tankDisconnected(Tank *tank)
01089 {
01090 createLogger();
01091 if (!success_) return;
01092
01093
01094 runQuery("INSERT INTO scorched3d_events "
01095 "(prefixid, seriesid, eventtype, playerid, otherplayerid, weaponid, eventtime) "
01096 "VALUES(%i, %i, %i, %i, 0, 0, NOW());",
01097 prefixid_, seriesid_,
01098 EventDisconnected,
01099 playerId_[tank->getUniqueId()]);
01100
01101 updateStats(tank);
01102 }
01103
01104 void StatsLoggerDatabase::tankKilled(Tank *firedTank, Tank *deadTank, Weapon *weapon)
01105 {
01106 createLogger();
01107 if (!success_) return;
01108
01109 runQuery("INSERT INTO scorched3d_events "
01110 "(prefixid, seriesid, eventtype, playerid, otherplayerid, weaponid, eventtime) "
01111 "VALUES(%i, %i, %i, %i, %i, %i, NOW());",
01112 prefixid_, seriesid_,
01113 EventKill,
01114 playerId_[firedTank->getUniqueId()],
01115 playerId_[deadTank->getUniqueId()],
01116 weaponId_[weapon->getParent()->getName()]);
01117
01118 runQuery("UPDATE scorched3d_stats SET kills=kills+1, skill=%i "
01119 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
01120 firedTank->getScore().getSkill(),
01121 playerId_[firedTank->getUniqueId()],
01122 prefixid_,
01123 seriesid_);
01124
01125 runQuery("UPDATE scorched3d_stats SET deaths=deaths+1, skill=%i "
01126 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
01127 deadTank->getScore().getSkill(),
01128 playerId_[deadTank->getUniqueId()],
01129 prefixid_,
01130 seriesid_);
01131 }
01132
01133 void StatsLoggerDatabase::tankTeamKilled(Tank *firedTank, Tank *deadTank, Weapon *weapon)
01134 {
01135 createLogger();
01136 if (!success_) return;
01137
01138 runQuery("INSERT INTO scorched3d_events "
01139 "(prefixid, seriesid, eventtype, playerid, otherplayerid, weaponid, eventtime) "
01140 "VALUES(%i, %i, %i, %i, %i, %i, NOW());",
01141 prefixid_, seriesid_,
01142 EventTeamKill,
01143 playerId_[firedTank->getUniqueId()],
01144 playerId_[deadTank->getUniqueId()],
01145 weaponId_[weapon->getParent()->getName()]);
01146
01147 runQuery("UPDATE scorched3d_stats SET teamkills=teamkills+1, skill=%i "
01148 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
01149 firedTank->getScore().getSkill(),
01150 playerId_[firedTank->getUniqueId()],
01151 prefixid_,
01152 seriesid_);
01153
01154 runQuery("UPDATE scorched3d_stats SET deaths=deaths+1 "
01155 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
01156 playerId_[deadTank->getUniqueId()],
01157 prefixid_,
01158 seriesid_);
01159 }
01160
01161 void StatsLoggerDatabase::tankSelfKilled(Tank *firedTank, Weapon *weapon)
01162 {
01163 createLogger();
01164 if (!success_) return;
01165
01166 runQuery("INSERT INTO scorched3d_events "
01167 "(prefixid, seriesid, eventtype, playerid, otherplayerid, weaponid, eventtime) "
01168 "VALUES(%i, %i, %i, %i, 0, %i, NOW());",
01169 prefixid_, seriesid_,
01170 EventSelfKill,
01171 playerId_[firedTank->getUniqueId()],
01172 weaponId_[weapon->getParent()->getName()]);
01173
01174 runQuery("UPDATE scorched3d_stats SET selfkills=selfkills+1, deaths=deaths+1, skill=%i "
01175 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
01176 firedTank->getScore().getSkill(),
01177 playerId_[firedTank->getUniqueId()],
01178 prefixid_,
01179 seriesid_);
01180 }
01181
01182 void StatsLoggerDatabase::tankWon(Tank *tank)
01183 {
01184 createLogger();
01185 if (!success_) return;
01186
01187 runQuery("INSERT INTO scorched3d_events "
01188 "(prefixid, seriesid, eventtype, playerid, otherplayerid, weaponid, eventtime) "
01189 "VALUES(%i, %i, %i, %i, 0, 0, NOW());",
01190 prefixid_, seriesid_,
01191 EventWon,
01192 playerId_[tank->getUniqueId()]);
01193
01194 runQuery("UPDATE scorched3d_stats SET wins=wins+1, skill=%i "
01195 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
01196 tank->getScore().getSkill(),
01197 playerId_[tank->getUniqueId()],
01198 prefixid_,
01199 seriesid_);
01200 }
01201
01202 void StatsLoggerDatabase::tankOverallWinner(Tank *tank)
01203 {
01204 createLogger();
01205 if (!success_) return;
01206
01207 runQuery("INSERT INTO scorched3d_events "
01208 "(prefixid, seriesid, eventtype, playerid, otherplayerid, weaponid, eventtime) "
01209 "VALUES(%i, %i, %i, %i, 0, 0, NOW());",
01210 prefixid_, seriesid_,
01211 EventOverallWinner,
01212 playerId_[tank->getUniqueId()]);
01213
01214 runQuery("UPDATE scorched3d_stats SET overallwinner=overallwinner+1, skill=%i "
01215 "WHERE playerid = %i AND prefixid = %i AND seriesid = %i;",
01216 tank->getScore().getSkill(),
01217 playerId_[tank->getUniqueId()],
01218 prefixid_,
01219 seriesid_);
01220 }
01221
01222 void StatsLoggerDatabase::weaponFired(Weapon *weapon, bool deathAni)
01223 {
01224 createLogger();
01225 if (!success_) return;
01226
01227 if (deathAni)
01228 {
01229 runQuery("UPDATE scorched3d_weapons SET deathshots=deathshots+1 "
01230 "WHERE weaponid = \"%i\" AND prefixid = %i AND seriesid = %i;",
01231 weaponId_[weapon->getParent()->getName()],
01232 prefixid_,
01233 seriesid_);
01234 }
01235 else
01236 {
01237 runQuery("UPDATE scorched3d_weapons SET shots=shots+1 "
01238 "WHERE weaponid = \"%i\" AND prefixid = %i AND seriesid = %i;",
01239 weaponId_[weapon->getParent()->getName()],
01240 prefixid_,
01241 seriesid_);
01242 }
01243 }
01244
01245 void StatsLoggerDatabase::weaponKilled(Weapon *weapon, bool deathAni)
01246 {
01247 createLogger();
01248 if (!success_) return;
01249
01250 if (deathAni)
01251 {
01252 runQuery("UPDATE scorched3d_weapons SET deathkills=deathkills+1 "
01253 "WHERE weaponid = \"%i\" AND prefixid = %i AND seriesid = %i;",
01254 weaponId_[weapon->getParent()->getName()],
01255 prefixid_,
01256 seriesid_);
01257 }
01258 else
01259 {
01260 runQuery("UPDATE scorched3d_weapons SET kills=kills+1 "
01261 "WHERE weaponid = \"%i\" AND prefixid = %i AND seriesid = %i;",
01262 weaponId_[weapon->getParent()->getName()],
01263 prefixid_,
01264 seriesid_);
01265 }
01266 }
01267