diff --git a/src/Makefile b/src/Makefile index 52909a7..845fc34 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,8 @@ USEDBIXX=1 +USECPPDB=1 BASE_LIBS=-lgcrypt -lboost_program_options -BASE_CFLAGS=-c -g -O2 -Wall +BASE_CFLAGS=-c -g -O2 -Wall -std=c++0x O_FILES=oastat.o oastatstruct.o oss2db/kill2db.o oss2db/init2db.o oss2db/shutdown2db.o oss2db/userinfo2db.o oss2db/Disconnect2Db.o oss2db/Award2Db.o oss2db/Ctf2Db.o oss2db/Point2Db.o oss2db/Ctf1f2Db.o oss2db/Elimination2Db.o oss2db/CtfElimination2Db.o oss2db/Harvester2Db.o oss2db/Challenge2Db.o oss2db/Warmup2Db.o oss2db/Accuracy2Db.o @@ -15,6 +16,12 @@ endif BASE_CFLAGS+= -DUSEDBIXX=1 endif +ifeq ($(USECPPDB),1) + BASE_LIBS+= -lcppdb + O_FILES+= db/Db2CppDb.o + BASE_CFLAGS+= -DUSECPPDB=1 +endif + O_FILES+= db/Db2Xml.o BASE_CFLAGS+= -DUSEXML=1 diff --git a/src/db/Db2CppDb.cpp b/src/db/Db2CppDb.cpp new file mode 100644 index 0000000..0a5b496 --- /dev/null +++ b/src/db/Db2CppDb.cpp @@ -0,0 +1,415 @@ +/* +=========================================================================== +oastat - a program for parsing log files and write the result to a database +Copyright (C) 2014 Poul Sander + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see http://www.gnu.org/licenses/ + +Source information and contacts persons can be found at +http://code.google.com/p/oastat/ +=========================================================================== +*/ + +#include "Db2CppDb.hpp" +#include +#include +#include + +using namespace std; +using namespace cppdb; + +void Db2CppDb::InitStrings(const string &backend) +{ + last_value = true; + if (backend == "pgsql") { + last_value = true; //Now also use last for PostgreSQL + } + if (backend == "mysql") { + last_value = true; + } + sql_backend = backend; +} + +void Db2CppDb::ReadConfigFromDb() +{ + string value; + cppdb::result res = *sql<<"SELECT thekey FROM oastat_config_uservars2save"; + while (res.next()) { + res >> value; + uservars2save.insert(value); + } + res = *sql<< "SELECT cvar FROM oastat_config_gamevars2save"; + while (res.next()) { + res >> value; + cvars2save.insert(value); + } +} + +bool Db2CppDb::IsDuplicate(const string &servername,const tm &thetime) +{ + cppdb::result res = *sql << "SELECT 'X' FROM oastat_games WHERE servername = ? AND time = ?"<(new cppdb::session(connectstring)); + commitlock = shared_ptr(new transaction(*sql)); + InitStrings("pgsql"); + ReadConfigFromDb(); + debug = false; +} + +Db2CppDb::Db2CppDb(const string &dbargs) +{ + stringstream stream(stringstream::in | stringstream::out); + stream << dbargs; + string holder; + getline(stream,holder,':'); + connectstring = dbargs; + sql = shared_ptr(new cppdb::session(connectstring)); + commitlock = shared_ptr(new transaction(*sql)); + InitStrings(holder); + ReadConfigFromDb(); + debug = false; +} + +Db2CppDb::Db2CppDb(const Db2CppDb& orig) +{ + throw runtime_error("May not make copy of Db2CppDb"); +} + +Db2CppDb::~Db2CppDb() +{ +} + +void Db2CppDb::createTables() +{ + +} + +void Db2CppDb::startGame(int gametype, const string &mapname, const string &basegame, const string &servername, const OaStatStruct &oss) +{ + sql = shared_ptr(new cppdb::session(connectstring)); + commitlock = nullptr; + commitlock = shared_ptr(new transaction(*sql)); + Rollback(); //in case there was some garbage that could be comitted (like warmup or an unfinished game) + SetOk(true); + timestamp = oss.getDateTime(); + if (oss.restOfLine.find("\\isWarmup\\1") != string::npos) { + SetOk(false); + cout << "Warmup: " << servername << ", " << oss.getTimeStamp() << endl; + return; + } + if (IsDuplicate(servername,timestamp)) { + SetOk(false); + cout << "Duplicate:" << servername << ", " << oss.getTimeStamp() << endl; + return; + } + if (last_value) { + statement st = *sql << "INSERT INTO oastat_games(gametype, mapname, basegame,servername,time) VALUES (?,LOWER(?),?,?,?)"; //<-1) { + try { + statement st = sql->create_statement("SAVEPOINT SETPLAYER"); + st.exec(); + *sql << "INSERT INTO oastat_players(guid,nickname,lastseen,isBot, model, headmodel) VALUES (?,?,?,?,?,?)" << + guid << nickname << timestamp << (isBot? "y":"n") << model << headmodel << exec; + statement str = sql->create_statement("RELEASE SAVEPOINT SETPLAYER"); + str.exec(); + } catch (cppdb::cppdb_error &e) { + DebugMessage("Already inserted? "+(string)e.what()); + statement str = sql->create_statement("ROLLBACK TO SAVEPOINT SETPLAYER"); + str.exec(); + } + *sql << "UPDATE oastat_players SET nickname = ?,lastseen = ?,isBot = ?, model = ?, headmodel = ? WHERE guid = ? AND lastseen < ?" << + nickname << timestamp << (isBot? "y":"n") << model << headmodel << guid << timestamp << exec; + } + try { + statement st = sql->create_statement("SAVEPOINT SETUSERINFO"); + st.exec(); + *sql << "INSERT INTO oastat_userinfo(gamenumber,second,player,team,model,skill) VALUES (?,?,?,?,?,?)" << gamenumber << second << getPlayerId(guid) << team << model << skill << exec; + statement str = sql->create_statement("RELEASE SAVEPOINT SETUSERINFO"); + str.exec(); + } catch (cppdb_error &e) { + statement str = sql->create_statement("ROLLBACK TO SAVEPOINT SETUSERINFO"); + str.exec(); + *sql << "UPDATE oastat_userinfo SET team = ?, model = ?, skill = ? WHERE gamenumber = ? AND second = ? AND player = ?" << + team << model << skill << gamenumber << second << getPlayerId(guid) << exec; + } + DebugMessage("setPlayerInfo for "+nickname+" with GUID: "+guid); +} + +void Db2CppDb::addKill(int second, const std::string &attackerID, const std::string &targetID, int type) +{ + if (!isok) { + return; + } + *sql<< "INSERT INTO oastat_kills(gamenumber,second,attacker,target,modtype) VALUES(?,?,?,?,?)" << gamenumber << second << getPlayerId(attackerID) << getPlayerId(targetID) << type << exec; + DebugMessage("addKill"); +} + +void Db2CppDb::addAward(int second, const std::string &player, int award) +{ + if (!isok) { + return; + } + *sql << "INSERT INTO oastat_awards(gamenumber,second,player,award) VALUES (?,?,?,?)" << gamenumber << second << getPlayerId(player) << award << exec; + DebugMessage("addAward"); +} + +void Db2CppDb::addScoreInfo(int second, const std::string &player, int score) +{ + if (!isok) { + return; + } + *sql << "INSERT INTO oastat_points(gamenumber,second,player,score) VALUES (?,?,?,?)" << gamenumber << second << getPlayerId(player) << score << exec; + DebugMessage("addScoreInfo"); +} + +void Db2CppDb::addCtf(int second, const std::string &player, int team, int event) +{ + if (!isok) { + return; + } + *sql << "INSERT INTO oastat_team_events(gamenumber,second,team,player,gametype,eventtype) VALUES (?,?,?,?,'ctf',?)" << gamenumber << second << team << getPlayerId(player) << event << exec; + DebugMessage("addCtf"); +} + +void Db2CppDb::addCtf1f(int second, const std::string &player, int team, int event) +{ + if (!isok) { + return; + } + *sql << "INSERT INTO oastat_team_events(gamenumber,second,team,player,gametype,eventtype) VALUES (?,?,?,?,'1fctf',?)" << gamenumber << second << team << getPlayerId(player) << event << exec; + DebugMessage("addCtf1f"); +} + +void Db2CppDb::addElimination(int second, int roundnumber, int team, int event) +{ + if (!isok) { + return; + } + *sql << "INSERT INTO oastat_team_events(gamenumber,second,team,eventtype,generic1,gametype) VALUES (?,?,?,?,?,'elimination')" << + gamenumber << second << team << event << roundnumber << exec; + DebugMessage("addElimination"); +} + +void Db2CppDb::addCtfElimination(int second, int roundnumber, const std::string &player, int team, int event) +{ + if (!isok) { + return; + } + *sql << "INSERT INTO oastat_team_events(gamenumber,second,team,player,eventtype,generic1,gametype) VALUES (?,?,?,?,?,?,'ctfelim')" << gamenumber << second << + team << getPlayerId(player) << event << roundnumber << exec; + DebugMessage("addCtfElimination"); +} + +void Db2CppDb::addHarvester(int second, const std::string &player1, const std::string &player2, int team, int event, int score) +{ + if (!isok) { + return; + } + *sql << "INSERT INTO oastat_team_events(gamenumber,second,team,player,player2,eventtype,amount,gametype) VALUES (?,?,?,?,?,?,?,'harvester')" << + gamenumber << second << team << getPlayerId(player1) << getPlayerId(player2) << event << score << exec; + DebugMessage("addHarvester"); +} + +void Db2CppDb::addGenericTeamEvent(int second, int team, int amount, const std::string &gametype, const std::string &player1, const std::string &player2, int event, int generic1) +{ + if (!isok) { + return; + } + *sql << "INSERT INTO oastat_team_events(gamenumber,second,team,player,player2,eventtype,amount,generic1,gametype) VALUES (?,?,?,?,?,?,?,?,?)" << + gamenumber << second << team << getPlayerId(player1) << getPlayerId(player2) << event << amount << generic1 << gametype << exec; + DebugMessage("addGenericTeamEvent"); +} + +void Db2CppDb::addChallenge(int second, const std::string &player, int challenge, int amount) +{ + if (!isok) { + return; + } + *sql << "INSERT INTO oastat_challenges(gamenumber,player,challenge,amount) VALUES (?,?,?,?)" << gamenumber << getPlayerId(player) << challenge << amount << exec; + DebugMessage("addChallenge"); +} + +void Db2CppDb::addAccuracy(int second, const std::string &player, int type, int shotsFired, int shotsHit) +{ + if (!isok) { + return; + } + try { + statement st = sql->create_statement("SAVEPOINT SETACCURACY"); + st.exec(); + *sql << "INSERT INTO oastat_accuracy(gamenumber,player,shotsfired,shotshit,modtype) VALUES (?,?,?,?,?)" << gamenumber << getPlayerId(player) << shotsFired << shotsHit << type << exec; + statement str = sql->create_statement("RELEASE SAVEPOINT SETACCURACY"); + str.exec(); + } catch (cppdb_error &e) { + statement str = sql->create_statement("ROLLBACK TO SAVEPOINT SETACCURACY"); + str.exec(); + *sql << "UPDATE oastat_accuracy SET shotsfired = ?, shotshit = ?, modtype = ? WHERE player = ? AND gamenumber = ?" << shotsFired << shotsHit << type << getPlayerId(player) << gamenumber << exec; + } +} + +int Db2CppDb::getNextGameNumber() +{ + int ret = -1; + result res = *sql<< "SELECT nextval('oastat_games_gamenumber_seq')"; + DebugMessage("Gettings next game number"); + if (res.next()) { + res >> ret; + return ret; + } else { + throw runtime_error("Could not get next gamenumber"); + } +} + +int Db2CppDb::getLastGameNumber() +{ + int result = -1; + cppdb::result res; + if (sql_backend == "mysql") { + res = *sql << "SELECT LAST_INSERT_ID() FROM DUAL"; + } else { + res = *sql<< "SELECT currval('oastat_games_gamenumber_seq')"; + } + DebugMessage("Gettings last game number"); + if (res.next()) { + res>> result; + cout << "Game number: " << result << endl; + return result; + } else { + throw runtime_error("Could not get last gamenumber"); + } +} + + +/* + This is a helper function so we never forget to start a new transaction after a commit. + */ +void Db2CppDb::Commit() +{ + if (!isok) { + Rollback(); + return; + } + commitlock->commit(); + commitlock = nullptr; + commitlock = shared_ptr(new transaction(*sql)); + DebugMessage("Commited"); +} + +void Db2CppDb::Rollback() +{ + commitlock->rollback(); + commitlock = nullptr; + commitlock = shared_ptr(new transaction(*sql)); + DebugMessage("Rollback"); +} + +bool Db2CppDb::Ok() +{ + return isok; +} + +void Db2CppDb::SetOk(bool ok) +{ + isok = ok; +} + +void Db2CppDb::doNotCommit() +{ + SetOk(false); +} + +void Db2CppDb::DebugMessage(const string &msg) +{ + if (debug) { + cout << "oastat: " << msg << endl; + } +} + +int Db2CppDb::getPlayerId(const string& guid) +{ + int ret = 0; + if (playerids.count(guid)) { + ret = playerids[guid]; + } else { + result res = *sql << "SELECT playerid FROM oastat_players WHERE guid = ?" << guid; + if (res.next()) { + res >> ret; + playerids[guid] = ret; + } + } + return ret; +} diff --git a/src/db/Db2CppDb.hpp b/src/db/Db2CppDb.hpp new file mode 100644 index 0000000..8ef7d2f --- /dev/null +++ b/src/db/Db2CppDb.hpp @@ -0,0 +1,84 @@ +/* +=========================================================================== +oastat - a program for parsing log files and write the result to a database +Copyright (C) 2014 Poul Sander + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see http://www.gnu.org/licenses/ + +Source information and contacts persons can be found at +http://code.google.com/p/oastat/ +=========================================================================== +*/ + +#ifndef DB2CPPDB_HPP +#define DB2CPPDB_HPP + +#include "database.hpp" +#include +#include +#include + +class Db2CppDb : public Database { +public: + Db2CppDb(); + Db2CppDb(const std::string &dbargs); + Db2CppDb(const Db2CppDb& orig); + virtual ~Db2CppDb(); + void createTables(); + void startGame(int gametype, const std::string &mapname, const std::string &basegame, const std::string &servername, const OaStatStruct &oss); + void addGameCvar(const std::string &cvar, const std::string &value); + void endGame(int second); + int getGameNumber(); + void setPlayerInfo(const std::string &guid, const std::string &nickname, bool isBot, int second, int team, const std::string &model, const std::string &headmodel, int skill); + void addKill(int second, const std::string &attackerID, const std::string &targetID, int type); + //void addCapture(int second, string player, int team); + void addAward(int second, const std::string &player, int award); + void addScoreInfo(int second, const std::string &player, int score); + void addCtf(int second, const std::string &player, int team, int event); + void addCtf1f(int second, const std::string &player, int team, int event); + void addElimination(int second, int roundnumber, int team, int event); + void addCtfElimination(int second, int roundnumber, const std::string &player, int team, int event); + void addHarvester(int second, const std::string &player1, const std::string &player2, int team, int event, int score); + void addChallenge(int second, const std::string &player, int challenge, int amount); + void addAccuracy(int second, const std::string &player, int type, int shotsFired, int shotsHit); + void addGenericTeamEvent(int second, int team, int amount, const std::string &gametype, const std::string &player1, const std::string &player2, int event, int generic1); + void doNotCommit(); +private: + std::shared_ptr sql; + std::shared_ptr commitlock; + std::string connectstring; + bool isok; + bool debug; + int gamenumber; /** Gamenumber we are currently working on */ + tm timestamp; /** Start time of the game we are currently working on */ + int getNextGameNumber(); + int getLastGameNumber(); + void Commit(); + void Rollback(); + bool Ok(); + void SetOk(bool ok); + void DebugMessage(const std::string &msg); + void InitStrings(const std::string &backend); + void ReadConfigFromDb(); + bool IsDuplicate(const std::string &servername, const tm &thetime); + int getPlayerId(const std::string &guid); + std::map playerids; + std::string sql_backend; + bool last_value; + std::set cvars2save; + std::set uservars2save; +}; + +#endif /* DB2CPPDB_HPP */ + diff --git a/src/db/Db2DbiXX.hpp b/src/db/Db2DbiXX.hpp index cf30acf..54cd152 100644 --- a/src/db/Db2DbiXX.hpp +++ b/src/db/Db2DbiXX.hpp @@ -21,8 +21,8 @@ http://code.google.com/p/oastat/ =========================================================================== */ -#ifndef _DB2DBIXX_HPP -#define _DB2DBIXX_HPP +#ifndef DB2DBIXX_HPP +#define DB2DBIXX_HPP #include "database.hpp" @@ -85,5 +85,5 @@ private: set uservars2save; }; -#endif /* _DB2DBIXX_HPP */ +#endif /* DB2DBIXX_HPP */ diff --git a/src/oastat.cpp b/src/oastat.cpp index 36943fc..f22ab6e 100644 --- a/src/oastat.cpp +++ b/src/oastat.cpp @@ -35,12 +35,14 @@ http://code.google.com/p/oastat/ using namespace std; -using namespace boost; #include "db/database.hpp" #ifdef USEDBIXX #include "db/Db2DbiXX.hpp" #endif +#ifdef USECPPDB +#include "db/Db2CppDb.hpp" +#endif #include "db/Db2Xml.hpp" @@ -65,7 +67,7 @@ using namespace boost; vector clientIdMap; -static int processStdIn(istream &in_p,vector > &commands); +static int processStdIn(istream &in_p,vector > &commands); @@ -75,28 +77,28 @@ static int processStdIn(istream &in_p,vector > &commands); * * @param[in] db The database object. May not be freed once given as an argument to this function */ -static void addCommands(shared_ptr &db,vector > &commands) +static void addCommands(boost::shared_ptr &db,vector > &commands) { if (!db) { throw runtime_error("db was uninizialized in addCommands"); } //Add new commands here - commands.push_back(shared_ptr(new Kill2Db() ) ); - commands.push_back(shared_ptr(new Init2Db() ) ); - commands.push_back(shared_ptr(new Shutdown2Db() ) ); - commands.push_back(shared_ptr(new Userinfo2Db()) ); - commands.push_back(shared_ptr(new Disconnect2Db()) ); - commands.push_back(shared_ptr(new Award2Db()) ); - commands.push_back(shared_ptr(new Ctf2Db()) ); - commands.push_back(shared_ptr(new Point2Db()) ); - commands.push_back(shared_ptr(new Ctf1f2Db()) ); - commands.push_back(shared_ptr(new Elimination2Db()) ); - commands.push_back(shared_ptr(new CtfElimination2Db()) ); - commands.push_back(shared_ptr(new Harvester2Db()) ); - commands.push_back(shared_ptr(new Challenge2Db()) ); - commands.push_back(shared_ptr(new Warmup2Db()) ); - commands.push_back(shared_ptr(new Accuracy2Db()) ); + commands.push_back(boost::shared_ptr(new Kill2Db() ) ); + commands.push_back(boost::shared_ptr(new Init2Db() ) ); + commands.push_back(boost::shared_ptr(new Shutdown2Db() ) ); + commands.push_back(boost::shared_ptr(new Userinfo2Db()) ); + commands.push_back(boost::shared_ptr(new Disconnect2Db()) ); + commands.push_back(boost::shared_ptr(new Award2Db()) ); + commands.push_back(boost::shared_ptr(new Ctf2Db()) ); + commands.push_back(boost::shared_ptr(new Point2Db()) ); + commands.push_back(boost::shared_ptr(new Ctf1f2Db()) ); + commands.push_back(boost::shared_ptr(new Elimination2Db()) ); + commands.push_back(boost::shared_ptr(new CtfElimination2Db()) ); + commands.push_back(boost::shared_ptr(new Harvester2Db()) ); + commands.push_back(boost::shared_ptr(new Challenge2Db()) ); + commands.push_back(boost::shared_ptr(new Warmup2Db()) ); + commands.push_back(boost::shared_ptr(new Accuracy2Db()) ); //Add more commands just above here for (unsigned int i=0; i &db,vector > -static int processStdIn(istream &in_p,vector > &commands) +static int processStdIn(istream &in_p,vector > &commands) { bool done = true; OaStatStruct *startstruct; @@ -198,7 +200,7 @@ int main (int argc, const char* argv[]) string backend = "Xml"; bool useTail = false; bool doIntegrationTest = false; - vector > commands; + vector > commands; ///////////// //dbargs = "mysql dbname oastat"; boost::format f("%1%/.openarena/baseoa/games.log"); @@ -244,24 +246,34 @@ int main (int argc, const char* argv[]) } try { - shared_ptr db; + boost::shared_ptr db; #if USEDBIXX if (backend == "DbiXX") { cout << "Using DBI" << endl; if (dbargs.length()<1) { - db = shared_ptr(new Db2DbiXX() ); + db = boost::shared_ptr(new Db2DbiXX() ); + } else { + db = boost::shared_ptr(new Db2DbiXX(dbargs) ); + } + } +#endif +#if USECPPDB + if (backend == "CppDb") { + cout << "Using CppDb" << endl; + if (dbargs.length()<1) { + db = boost::shared_ptr(new Db2CppDb() ); } else { - db = shared_ptr(new Db2DbiXX(dbargs) ); + db = boost::shared_ptr(new Db2CppDb(dbargs) ); } } #endif if (backend == "Xml") { cout << "Using XML" << endl; if (dbargs.length()<1) { - db = shared_ptr(new Db2Xml() ); + db = boost::shared_ptr(new Db2Xml() ); } else { - db = shared_ptr(new Db2Xml(dbargs) ); + db = boost::shared_ptr(new Db2Xml(dbargs) ); } }