All pastes #293922 Raw Edit

Unnamed

public text v1 · immutable
#293922 ·published 2006-12-28 00:45 UTC
rendered paste body
Compiled with:
-I/opt/local/include/mysql5/mysql/ -L/opt/local/lib/mysql5/mysql/ -lmysqlclient -lconfig++

(boost/ is a symlink)

Outputs:
Stats Plugin: Loading config file (config)... loaded config version 0.10!
Stats Plugin: Loaded Stats version 0.10.
Stats Plugin: Connected to MySQL 5.0.27 server via protocol 10.
SELECT svrid FROM servers WHERE hostport = 'bzflag.bounceme.net:5154'
Stats Plugin: Found server number 1 in database () (Warning count: 0).
UPDATE servers SET display = '1' WHERE hostport = 'bzflag.bounceme.net:5154'
.........Stats Plugin: Getting player data for "test"... done!
INSERT INTO sessions VALUES(null,1,'test',null,'127.0.0.1',NOW(),null,5,0)
Stats Plugin: Player session ID is 0 () (Warning count: 0).
......^C.UPDATE servers SET display = '0' WHERE hostport = 'bzflag.bounceme.net:5154'
Stats Plugin: Closed MySQL connection.

Stats.h:

// System
#include <iostream>
#include <fstream>

// BZFS
#include "bzfsAPI.h"
#include "TextUtils.h"

// MySQL
#include <mysql/mysql.h>

// LibConfig
#include <libconfig.h++>

// Boost libs
#include <boost/config.hpp>
#include <boost/format.hpp>

using namespace std;
using namespace libconfig;

using boost::format;

// The main beef
class StatsHandler : public bz_EventHandler {
public:
	StatsHandler() {}; // constructor
	~StatsHandler() {}; // destructor
	
	virtual void process(bz_EventData *eventData); // shebang
	
	// Server info
	bool addServer();
	bool updateServerInfo();
	bool isServerPublic();
	string getServerAddress();
	string getServerDescription();
	
	// Events
	bool addPlayer(int playerID);
	
	// Misc
	int convertTeamToInt(bz_eTeamType team);
	
private:
	// Server info
	struct serverInfo {
		int id;
		bool publicServer;
		string publicAddress;
		string publicDescription;
	} srvInfo;
	
	// Player info
	bool updateCurrentPlayer(int playerID);
	struct currentPlayerInfo {
		int id;
		string callsign;
		string email;
		int team;
		string ip;
	} plrInfo;
	
	struct allPlayerInfo {
		int sessid;
		int id;
		string callsign;
		string email;
		int team;
		string ip;
	};
	
	map<int, int> plrIDtoSessID; // playerID to session ID
	map<int, allPlayerInfo> allPlrInfo;
};

// BZFX Command
class CommandHandler : public bz_CustomSlashCommandHandler, public StatsHandler {
public:
	CommandHandler() {}; // constructor
	~CommandHandler() {}; // destructor
	virtual bool handle(int playerID, bzApiString command, bzApiString message, bzAPIStringList *param);
};

Stats.cpp:

#include "Stats.h"

BZ_GET_PLUGIN_VERSION

bool pluginFailed = false;
float pluginVersion = 0.1;

StatsHandler statsHandle;
CommandHandler commandHandle;

MYSQL *mysql = NULL;
MYSQL_RES *result;
MYSQL_ROW row;
string dbQuery;

// libconfig
Config cfg;

BZF_PLUGIN_CALL int bz_Load(const char* commandLine) {
	// Register them events
	bz_registerCustomSlashCommand ("bzfx", &commandHandle);
	bz_registerEvent(bz_eTickEvent, &statsHandle);
	bz_registerEvent(bz_ePlayerJoinEvent, &statsHandle);
	bz_setMaxWaitTime(5.0f);
		
	// Read the config file
	try {
		printf("Stats Plugin: Loading config file (%s)...", commandLine);
		cfg.readFile(commandLine);
		printf(" loaded config version %.2f!\n", float(cfg.lookup("plugin.version")));
	} catch (...) {
		printf("Stats Plugin: Could not read config file: %s\n", commandLine);
	}
	
	if (pluginVersion == float(cfg.lookup("plugin.version"))) {
		printf("Stats Plugin: Loaded Stats version %.2f.\n", pluginVersion);
	} else {
		printf("Stats Plugin: Plugin and config mismatch! Plugin version %.2f and config version %.2f. There may be unexpected results!\n",
				pluginVersion,
				float(cfg.lookup("plugin.version")));
	}
		
	// Init MySQL
	mysql = mysql_init(mysql);
	
	if (!mysql) {
		pluginFailed = true;
		printf("Stats Plugin: MySQL init failed, out of memory? (%s) (Warning count: %i)\n",
				mysql_error(mysql),
				mysql_warning_count(mysql));
	}
	
	if (!mysql_real_connect(mysql,
							cfg.lookup("mysql.host"),
							cfg.lookup("mysql.user"),
							cfg.lookup("mysql.password"),
							cfg.lookup("mysql.database"),
							0,
							NULL,
							0 )) {
		printf("Stats Plugin: MySQL connection failed (%s) (Warning count: %i).\n",
				mysql_error(mysql),
				mysql_warning_count(mysql));
	} else {
		printf("Stats Plugin: Connected to MySQL %s server via protocol %i.\n", 
				mysql_get_server_info(mysql),
				mysql_get_proto_info(mysql));
	}
	
	statsHandle.addServer();
	
	return 0;
}

BZF_PLUGIN_CALL int bz_Unload(void) {
	// Unregister the events
	bz_removeCustomSlashCommand("bzfx");
	bz_removeEvent(bz_eTickEvent, &statsHandle);
	bz_removeEvent(bz_ePlayerJoinEvent, &statsHandle);
	
	// Disconnect from MySQL only if we are connected
	dbQuery = str(format("UPDATE servers SET display = '0' WHERE hostport = '%s'")
					% statsHandle.getServerAddress());
	cout << dbQuery << endl;
	
	if (mysql_query(mysql, dbQuery.c_str())) {
		printf("Stats Plugin: Server shutdown query failed (%s) (Warning count: %i).\n",
				mysql_error(mysql),
				mysql_warning_count(mysql));
	}
	
	if (mysql) {
		mysql_close(mysql);
		printf("Stats Plugin: Closed MySQL connection.\n");
	}
	
	return 0;
}

void StatsHandler::process(bz_EventData *eventData) {
	if (eventData->eventType == bz_eTickEvent) {
		cout << ".";
		cout.flush();
		return;
	} else if (eventData->eventType == bz_ePlayerJoinEvent) {
		statsHandle.addPlayer(((bz_PlayerJoinPartEventData*)eventData)->playerID);
	} else {
		// WTF?
		return;
	}
}

bool CommandHandler::handle(int playerID, bzApiString command, bzApiString message, bzAPIStringList *param) {
	printf("Stats Plugin: Got command. %s - %s - %i\n", statsHandle.getServerAddress().c_str(), statsHandle.getServerDescription().c_str(), statsHandle.isServerPublic());
	return true;
}

bool StatsHandler::updateServerInfo() {
	this->srvInfo.publicServer = bz_getPublic();
	this->srvInfo.publicAddress = bz_getPublicAddr().c_str();
	this->srvInfo.publicDescription = bz_getPublicDescription().c_str();
	
	return true;
}

bool StatsHandler::addServer() {
	this->updateServerInfo();

	// DB
	dbQuery = str(format("SELECT svrid FROM servers WHERE hostport = '%s'")
					% this->getServerAddress());
	cout << dbQuery << endl;
	
	if (mysql_query(mysql, dbQuery.c_str())) {
		printf("Stats Plugin: Server select query failed (%s) (Warning count: %i).\n",
				mysql_error(mysql),
				mysql_warning_count(mysql));
	} else {
		result = mysql_store_result(mysql);
		row = mysql_fetch_row(result);
		if (!result) { // no rows found!
			pluginFailed = true;
			printf("Stats Plugin: Could not find server in database (%s) (Warning count: %i).\n",
					mysql_error(mysql),
					mysql_warning_count(mysql));
			return false;
		} else {
			// set server info's id so we have it later
			// FIXME: I'm just asking for it if I don't check if this is really an int, fix later
			this->srvInfo.id = atoi(row[0]);
			printf("Stats Plugin: Found server number %d in database (%s) (Warning count: %i).\n", this->srvInfo.id, mysql_error(mysql), mysql_warning_count(mysql));
		}
	}
	
	// OK, we're online
	if (!pluginFailed) {
		dbQuery = str(format("UPDATE servers SET display = '1' WHERE hostport = '%s'")
					% this->getServerAddress());
		cout << dbQuery << endl;
	
		if (mysql_query(mysql, dbQuery.c_str())) {
			printf("Stats Plugin: Server SELECT query failed (%s) (Warning count: %i).\n", mysql_error(mysql), mysql_warning_count(mysql));
			return false;
		}
	}
	
	return true;
}

bool StatsHandler::isServerPublic() {
	return (this->srvInfo.publicServer);
}

string StatsHandler::getServerAddress() {
	return (this->srvInfo.publicAddress);
}

string StatsHandler::getServerDescription() {
	return (this->srvInfo.publicDescription);
}

bool StatsHandler::updateCurrentPlayer(int playerID) {
	bz_PlayerRecord *data = bz_getPlayerByIndex(playerID);
	if (data) {
		printf("Stats Plugin: Getting player data for \"%s\"... ", data->callsign.c_str());
		
		// Data
		this->allPlrInfo[playerID].id = playerID;
		this->allPlrInfo[playerID].callsign = data->callsign.c_str();
		this->allPlrInfo[playerID].email = data->email.c_str();
		this->allPlrInfo[playerID].ip = data->ipAddress.c_str();
		this->allPlrInfo[playerID].team = this->convertTeamToInt(data->team);
		bz_freePlayerRecord(data);
		
		printf("done!\n");
		
		return true;
	} else {
		bz_freePlayerRecord(data);
		printf("Stats Plugin: Player data is null! Aborting!\n");
		return false;
	}
	// Really, WTF?
	return false;
}

bool StatsHandler::addPlayer(int playerID) {
	this->updateCurrentPlayer(playerID);
	
	// DB
	dbQuery = str(format("INSERT INTO sessions VALUES(null,%i,'%s',null,'%s',NOW(),null,%i,%i)")
					% this->srvInfo.id
					% this->allPlrInfo[playerID].callsign
					% this->allPlrInfo[playerID].ip
					% this->allPlrInfo[playerID].team
					% this->allPlrInfo[playerID].id);
	cout << dbQuery << endl;
	
	if (mysql_query(mysql, dbQuery.c_str())) {
		printf("Stats Plugin: Player session insert failed (%s) (Warning count: %i).\n", mysql_error(mysql), mysql_warning_count(mysql));
		return false;
	} else {
		unsigned long lastID = mysql_insert_id(mysql);
		printf("Stats Plugin: Player session ID is %lu (%s) (Warning count: %i).\n", lastID, mysql_error(mysql), mysql_warning_count(mysql));
	}
	
	return true;
}

int StatsHandler::convertTeamToInt(bz_eTeamType team) {
	if (team == eRogueTeam)
		return 0;
	else if (team == eRedTeam)
		return 1;
	else if (team == eGreenTeam)
		return 2;
	else if (team == eBlueTeam)
		return 3;
	else if (team == ePurpleTeam)
		return 4;
	else if (team == eObservers)
		return 5;
	else if (team == eHunterTeam)
		return 0;
	else if (team == eRabbitTeam)
		return 1;
	else
		return -1;
}