rendered paste bodyCompiled 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;
}