git repos / oastat

src/db/Db2DbiXX.cpp

browsing at commit = 4a7db8e036a093bc5f193c94df93c0defb65d6d5

raw · blame · history

/*
===========================================================================
oastat - a program for parsing log files and write the result to a database
Copyright (C) 2012 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 <c++/4.6/stdexcept>

#include "Db2DbiXX.hpp"

void Db2DbiXX::InitStrings(const string &backend)
{
	last_value = false;
	if (backend == "pgsql") {
		last_value = true; //Now also use last for PostgreSQL
	}
	if (backend == "mysql") {
		last_value = true;
	}
	sql_backend = backend;
}

void Db2DbiXX::ReadConfigFromDb()
{
	result res;
	row r;
	string value;
	*sql<<"SELECT thekey FROM oastat_config_uservars2save",res;
	while (res.next(r)) {
		r >> value;
		uservars2save.insert(value);
	}
	*sql<< "SELECT cvar FROM oastat_config_gamevars2save",res;
	while (res.next(r)) {
		r >> value;
		cvars2save.insert(value);
	}
}

bool Db2DbiXX::IsDuplicate(const string &servername,const tm &thetime)
{
	result res;
	*sql << "SELECT 'X' FROM oastat_games WHERE servername = ? AND time = ?",servername,thetime,res;
	if (res.rows()) {
		return true;
	} else {
		return false;
	}
}

Db2DbiXX::Db2DbiXX()
{
	sql = new session("pgsql");
	InitStrings("pgsql");
	sql->param("dbname","oastat");
	sql->connect();
	ReadConfigFromDb();
	commitlock = new transaction(*sql);
	debug = false;
}

Db2DbiXX::Db2DbiXX(const string &dbargs)
{
	stringstream stream(stringstream::in | stringstream::out);
	stream << dbargs;
	string holder;
	stream >> holder;
	sql = new session(holder);
	InitStrings(holder);
	while (!stream.eof()) {
		stream >> holder;
		if (!stream.eof()) {
			string param;
			stream >> param;
			sql->param(holder,param);
		}
	}
	sql->connect();
	ReadConfigFromDb();
	commitlock = new transaction(*sql);
	debug = false;
	//sql(dbargs);
}

Db2DbiXX::Db2DbiXX(const Db2DbiXX& orig)
{
	throw runtime_error("May not make copy of Db2DbiXX");
}

Db2DbiXX::~Db2DbiXX()
{
	delete commitlock;
	delete sql;
}

void Db2DbiXX::createTables()
{

}

void Db2DbiXX::startGame(int gametype, const string &mapname, const string &basegame, const string &servername, const OaStatStruct &oss)
{
	sql->reconnect();
	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) {
		*sql << "INSERT INTO oastat_games(gametype, mapname, basegame,servername,time) VALUES (?,LOWER(?),?,?,?)",gametype,mapname,basegame,servername,timestamp,exec();
		gamenumber = getLastGameNumber();
		if (gamenumber < 1) {
			SetOk(false);
			cout << "Must FAIL!" << endl;
		}
	} else {
		gamenumber = getNextGameNumber();
		*sql << "INSERT INTO oastat_games(gamenumber,gametype, mapname, basegame,servername,time) VALUES (?,?,LOWER(?),?,?,?)",
			 gamenumber,gametype,mapname,basegame,servername,timestamp,exec();
	}
	DebugMessage("startGame");
}

void Db2DbiXX::addGameCvar(const std::string &cvar, const std::string &value)
{
	if (!isok) {
		return;
	}
	if (cvars2save.find(cvar) == cvars2save.end()) {
		return;    //not to be saved
	}
	*sql << "INSERT INTO oastat_gamecvars(gamenumber,cvar,value,numericvalue) VALUES (?,LOWER(?),?,?)",gamenumber,cvar,value,0,exec();
	DebugMessage("addCvar");
}

/*
 endGame is called then we finnish the game. This also means that it is safe to commit
 */
void Db2DbiXX::endGame(int second)
{
	if (!isok) {
		return;
	}
	*sql << "UPDATE oastat_games SET second=? WHERE gamenumber = ?",second,gamenumber,exec();
	Commit(); //Game have ended, transaction is in a stable state
	DebugMessage("endgame");
}

int Db2DbiXX::getGameNumber()
{
	return gamenumber;
}

void Db2DbiXX::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)
{
	if (!isok) {
		return;
	}
	if (team>-1) {
		try {
			*sql << "SAVEPOINT SETPLAYER",exec();
			*sql << "INSERT INTO oastat_players(guid,nickname,lastseen,isBot, model, headmodel) VALUES (?,?,?,?,?,?)",guid,nickname,timestamp,(isBot? "y":"n"),model,headmodel,exec();
			*sql << "RELEASE SAVEPOINT SETPLAYER",exec(); //Needed by postgresql
		} catch (dbixx_error &e) {
			DebugMessage("Already inserted? "+(string)e.what());
			*sql << "ROLLBACK TO SAVEPOINT SETPLAYER",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 {
		*sql << "SAVEPOINT SETUSERINFO",exec();
		*sql << "INSERT INTO oastat_userinfo(gamenumber,second,player,team,model,skill) VALUES (?,?,?,?,?,?)",gamenumber,second,getPlayerId(guid),team,model,skill,exec();
		*sql << "RELEASE SAVEPOINT SETUSERINFO",exec(); //Needed by postgresql
	} catch (dbixx_error &e) {
		*sql << "ROLLBACK TO SAVEPOINT SETUSERINFO",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 Db2DbiXX::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 Db2DbiXX::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 Db2DbiXX::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 Db2DbiXX::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 Db2DbiXX::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 Db2DbiXX::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 Db2DbiXX::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 Db2DbiXX::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 Db2DbiXX::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 Db2DbiXX::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 Db2DbiXX::addAccuracy(int second, const std::string &player, int type, int shotsFired, int shotsHit)
{
	if (!isok) {
		return;
	}
	try {
		*sql << "SAVEPOINT SETACCURACY",exec();
		*sql << "INSERT INTO oastat_accuracy(gamenumber,player,shotsfired,shotshit,modtype) VALUES (?,?,?,?,?)",gamenumber,getPlayerId(player),shotsFired,shotsHit,type,exec();
		*sql << "RELEASE SAVEPOINT SETACCURACY",exec(); //Needed by postgresql
	} catch (dbixx_error &e) {
		*sql << "ROLLBACK TO SAVEPOINT SETACCURACY",exec();
		*sql << "UPDATE oastat_accuracy SET shotsfired = ?, shotshit = ?, modtype = ? WHERE player = ? AND gamenumber = ?",shotsFired,shotsHit,type,getPlayerId(player),gamenumber,exec();
	}
}

int Db2DbiXX::getNextGameNumber()
{
	int result = -1;
	row r;
	*sql<< "SELECT nextval('oastat_games_gamenumber_seq')";
	DebugMessage("Gettings next game number");
	if (sql->single(r)) {
		r>> result;
		return result;
	} else {
		throw runtime_error("Could not get next gamenumber");
	}
}

int Db2DbiXX::getLastGameNumber()
{
	int result = -1;
	row r;
	if (sql_backend == "mysql") {
		*sql << "SELECT LAST_INSERT_ID() FROM DUAL";
	} else {
		*sql<< "SELECT currval('oastat_games_gamenumber_seq')";
	}
	DebugMessage("Gettings last game number");
	if (sql->single(r)) {
		r>> 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 Db2DbiXX::Commit()
{
	if (!isok) {
		Rollback();
		return;
	}
	commitlock->commit();
	delete commitlock;
	commitlock = new transaction(*sql);
	DebugMessage("Commited");
}

void Db2DbiXX::Rollback()
{
	commitlock->rollback();
	delete commitlock;
	commitlock = new transaction(*sql);
	DebugMessage("Rollback");
}

bool Db2DbiXX::Ok()
{
	return isok;
}

void Db2DbiXX::SetOk(bool ok)
{
	isok = ok;
}

void Db2DbiXX::doNotCommit()
{
	SetOk(false);
}

void Db2DbiXX::DebugMessage(const string &msg)
{
	if (debug) {
		cout << "oastat: " << msg << endl;
	}
}

int Db2DbiXX::getPlayerId(const string& guid)
{
	int ret = 0;
	if (playerids.count(guid)) {
		ret = playerids[guid];
	} else {
		row r;
		*sql << "SELECT playerid FROM oastat_players WHERE guid = ?",guid;
		if (sql->single(r)) {
			r >> ret;
			playerids[guid] = ret;
		}
	}
	return ret;
}