00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00048 #include <cflogger.h>
00049 #ifndef __CEXTRACT__
00050 #include <cflogger_proto.h>
00051 #endif
00052
00053
00054 #include <sqlite3.h>
00055
00057 #define CFLOGGER_CURRENT_FORMAT 3
00058
00060 static sqlite3 *database;
00061
00063 static int last_stored_day = -1;
00064
00080 static int check_tables_callback(void *param, int argc, char **argv, char **azColName) {
00081 int *format = (int *)param;
00082
00083 *format = atoi(argv[0]);
00084 return 0;
00085 }
00086
00102 static int do_sql(const char *sql) {
00103 int err;
00104 char *msg;
00105
00106 if (!database)
00107 return -1;
00108
00109 err = sqlite3_exec(database, sql, NULL, NULL, &msg);
00110 if (err != SQLITE_OK) {
00111 cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
00112 sqlite3_free(msg);
00113 }
00114 return err;
00115 }
00116
00143 static int update_table_format(const char *table, const char *newschema,
00144 const char *select_columns) {
00145 char *sql;
00146 int err;
00147
00148 sql = sqlite3_mprintf("ALTER TABLE %s RENAME TO %s_old;", table, table);
00149 err = do_sql(sql);
00150 sqlite3_free(sql);
00151 if (err != SQLITE_OK)
00152 return err;
00153
00154 sql = sqlite3_mprintf("CREATE TABLE %s(%s);", table, newschema);
00155 err = do_sql(sql);
00156 sqlite3_free(sql);
00157 if (err != SQLITE_OK)
00158 return err;
00159
00160 sql = sqlite3_mprintf("INSERT INTO %s SELECT %s FROM %s_old;",
00161 table, select_columns, table);
00162 err = do_sql(sql);
00163 sqlite3_free(sql);
00164 if (err != SQLITE_OK)
00165 return err;
00166
00167 sql = sqlite3_mprintf("DROP TABLE %s_old;", table, table);
00168 err = do_sql(sql);
00169 sqlite3_free(sql);
00170
00171 return err;
00172 }
00173
00180 #define DO_OR_ROLLBACK(sqlstring) \
00181 if (do_sql(sqlstring) != SQLITE_OK) { \
00182 do_sql("rollback transaction;"); \
00183 cf_log(llevError, " [%s] Logger database format update failed! Couldn't upgrade from format %d to fromat %d!. Won't log.\n", PLUGIN_NAME, format, CFLOGGER_CURRENT_FORMAT);\
00184 sqlite3_close(database); \
00185 database = NULL; \
00186 return; \
00187 }
00188
00189 #define UPDATE_OR_ROLLBACK(tbl, newschema, select_columns) \
00190 if (update_table_format((tbl), (newschema), (select_columns)) != SQLITE_OK) { \
00191 do_sql("rollback transaction;"); \
00192 cf_log(llevError, " [%s] Logger database format update failed! Couldn't upgrade from format %d to fromat %d!. Won't log.\n", PLUGIN_NAME, format, CFLOGGER_CURRENT_FORMAT);\
00193 sqlite3_close(database); \
00194 database = NULL; \
00195 return; \
00196 }
00197
00201 static void check_tables(void) {
00202 int format;
00203 int err;
00204 format = 0;
00205
00206 err = sqlite3_exec(database, "select param_value from parameters where param_name = 'version';", check_tables_callback, &format, NULL);
00207
00208
00209 if (format > CFLOGGER_CURRENT_FORMAT) {
00210 cf_log(llevError, " [%s] Logger database format (%d) is newer than supported (%d) by this binary!. Won't log.\n", PLUGIN_NAME, format, CFLOGGER_CURRENT_FORMAT);
00211
00212
00213
00214 sqlite3_close(database);
00215 database = NULL;
00216 }
00217
00218
00219 if (format < 1) {
00220 cf_log(llevDebug, " [%s] Creating logger database schema (format 1).\n", PLUGIN_NAME);
00221 if (do_sql("BEGIN EXCLUSIVE TRANSACTION;") != SQLITE_OK) {
00222 cf_log(llevError, " [%s] Logger database format update failed! Couldn't acquire exclusive lock to database when upgrading from format %d to fromat %d!. Won't log.\n", PLUGIN_NAME, format, CFLOGGER_CURRENT_FORMAT);
00223 sqlite3_close(database);
00224 database = NULL;
00225 return;
00226 }
00227 DO_OR_ROLLBACK("create table living(liv_id integer primary key autoincrement, liv_name text, liv_is_player integer, liv_level integer);");
00228 DO_OR_ROLLBACK("create table region(reg_id integer primary key autoincrement, reg_name text);");
00229 DO_OR_ROLLBACK("create table map(map_id integer primary key autoincrement, map_path text, map_reg_id integer);");
00230 DO_OR_ROLLBACK("create table time(time_real integer, time_ingame text);");
00231
00232 DO_OR_ROLLBACK("create table living_event(le_liv_id integer, le_time integer, le_code integer, le_map_id integer);");
00233 DO_OR_ROLLBACK("create table map_event(me_map_id integer, me_time integer, me_code integer, me_living_id integer);");
00234 DO_OR_ROLLBACK("create table kill_event(ke_time integer, ke_victim_id integer, ke_victim_level integer, ke_map_id integer , ke_killer_id integer, ke_killer_level integer);");
00235
00236 DO_OR_ROLLBACK("create table parameters(param_name text, param_value text);");
00237 DO_OR_ROLLBACK("insert into parameters values( 'version', '1' );");
00238 do_sql("COMMIT TRANSACTION;");
00239 }
00240
00241
00242
00243
00244
00245
00246 if (format < 2) {
00247 cf_log(llevDebug, " [%s] Upgrading logger database schema (to format 2).\n", PLUGIN_NAME);
00248 if (do_sql("BEGIN EXCLUSIVE TRANSACTION;") != SQLITE_OK) {
00249 cf_log(llevError, " [%s] Logger database format update failed! Couldn't acquire exclusive lock to database when upgrading from format %d to fromat %d!. Won't log.\n", PLUGIN_NAME, format, CFLOGGER_CURRENT_FORMAT);
00250 sqlite3_close(database);
00251 database = NULL;
00252 return;
00253 }
00254
00255
00256
00257 UPDATE_OR_ROLLBACK("living", "liv_id INTEGER PRIMARY KEY AUTOINCREMENT, liv_name TEXT NOT NULL, liv_is_player INTEGER NOT NULL, liv_level INTEGER NOT NULL", "*");
00258 UPDATE_OR_ROLLBACK("region", "reg_id INTEGER PRIMARY KEY AUTOINCREMENT, reg_name TEXT UNIQUE NOT NULL", "*");
00259 UPDATE_OR_ROLLBACK("map", "map_id INTEGER PRIMARY KEY AUTOINCREMENT, map_path TEXT NOT NULL, map_reg_id INTEGER NOT NULL, CONSTRAINT map_path_reg_id UNIQUE(map_path, map_reg_id)", "*");
00260 #if 0
00261
00262 UPDATE_OR_ROLLBACK("time", "time_real INTEGER PRIMARY KEY, time_ingame TEXT UNIQUE NOT NULL");
00263 #endif
00264 UPDATE_OR_ROLLBACK("living_event", "le_liv_id INTEGER NOT NULL, le_time INTEGER NOT NULL, le_code INTEGER NOT NULL, le_map_id INTEGER NOT NULL", "*");
00265 UPDATE_OR_ROLLBACK("map_event", "me_map_id INTEGER NOT NULL, me_time INTEGER NOT NULL, me_code INTEGER NOT NULL, me_living_id INTEGER NOT NULL", "*");
00266 UPDATE_OR_ROLLBACK("kill_event", "ke_time INTEGER NOT NULL, ke_victim_id INTEGER NOT NULL, ke_victim_level INTEGER NOT NULL, ke_map_id INTEGER NOT NULL, ke_killer_id INTEGER NOT NULL, ke_killer_level INTEGER NOT NULL", "*");
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276 DO_OR_ROLLBACK("DROP TABLE parameters;");
00277 DO_OR_ROLLBACK("CREATE TABLE parameters(param_name TEXT NOT NULL PRIMARY KEY, param_value TEXT);");
00278 DO_OR_ROLLBACK("INSERT INTO parameters (param_name, param_value) VALUES( 'version', '2' );");
00279
00280
00281 DO_OR_ROLLBACK("CREATE INDEX living_name_player_level ON living(liv_name,liv_is_player,liv_level);");
00282
00283
00284 DO_OR_ROLLBACK("CREATE INDEX kill_event_time ON kill_event(ke_time);");
00285 DO_OR_ROLLBACK("CREATE INDEX map_reg_id ON map(map_reg_id);");
00286
00287
00288 do_sql("COMMIT TRANSACTION;");
00289 }
00290
00291 if (format < 3) {
00292 cf_log(llevDebug, " [%s] Upgrading logger database schema (to format 3).\n", PLUGIN_NAME);
00293 if (do_sql("BEGIN EXCLUSIVE TRANSACTION;") != SQLITE_OK) {
00294 cf_log(llevError, " [%s] Logger database format update failed! Couldn't acquire exclusive lock to database when upgrading from format %d to fromat %d!. Won't log.\n", PLUGIN_NAME, format, CFLOGGER_CURRENT_FORMAT);
00295 sqlite3_close(database);
00296 database = NULL;
00297 return;
00298 }
00299 UPDATE_OR_ROLLBACK("time", "time_ingame TEXT NOT NULL PRIMARY KEY, time_real INTEGER NOT NULL", "time_ingame, time_real");
00300 DO_OR_ROLLBACK("UPDATE parameters SET param_value = '3' WHERE param_name = 'version';");
00301 do_sql("COMMIT TRANSACTION;");
00302
00303
00304
00305
00306 do_sql("VACUUM;");
00307 }
00308 }
00309
00325 static int get_living_id(object *living) {
00326 char **line;
00327 char *sql;
00328 int nrow, ncolumn, id;
00329
00330 if (living->type == PLAYER)
00331 sql = sqlite3_mprintf("select liv_id from living where liv_name='%q' and liv_is_player = 1", living->name);
00332 else
00333 sql = sqlite3_mprintf("select liv_id from living where liv_name='%q' and liv_is_player = 0 and liv_level = %d", living->name, living->level);
00334 sqlite3_get_table(database, sql, &line, &nrow, &ncolumn, NULL);
00335
00336 if (nrow > 0)
00337 id = atoi(line[ncolumn]);
00338 else {
00339 sqlite3_free(sql);
00340 sql = sqlite3_mprintf("insert into living(liv_name, liv_is_player, liv_level) values('%q', %d, %d)", living->name, living->type == PLAYER ? 1 : 0, living->level);
00341 do_sql(sql);
00342 id = sqlite3_last_insert_rowid(database);
00343 }
00344 sqlite3_free(sql);
00345 sqlite3_free_table(line);
00346 return id;
00347 }
00348
00359 static int get_region_id(region *reg) {
00360 char **line;
00361 char *sql;
00362 int nrow, ncolumn, id;
00363
00364 if (!reg)
00365 return 0;
00366
00367 sql = sqlite3_mprintf("select reg_id from region where reg_name='%q'", reg->name);
00368 sqlite3_get_table(database, sql, &line, &nrow, &ncolumn, NULL);
00369
00370 if (nrow > 0)
00371 id = atoi(line[ncolumn]);
00372 else {
00373 sqlite3_free(sql);
00374 sql = sqlite3_mprintf("insert into region(reg_name) values( '%q' )", reg->name);
00375 do_sql(sql);
00376 id = sqlite3_last_insert_rowid(database);
00377 }
00378 sqlite3_free(sql);
00379 sqlite3_free_table(line);
00380 return id;
00381 }
00382
00395 static int get_map_id(mapstruct *map) {
00396 char **line;
00397 char *sql;
00398 int nrow, ncolumn, id, reg_id;
00399 const char *path = map->path;
00400
00401 if (strncmp(path, "/random/", 7) == 0)
00402 path = "/random/";
00403
00404 reg_id = get_region_id(map->region);
00405 sql = sqlite3_mprintf("select map_id from map where map_path='%q' and map_reg_id = %d", path, reg_id);
00406 sqlite3_get_table(database, sql, &line, &nrow, &ncolumn, NULL);
00407
00408 if (nrow > 0)
00409 id = atoi(line[ncolumn]);
00410 else {
00411 sqlite3_free(sql);
00412 sql = sqlite3_mprintf("insert into map(map_path, map_reg_id) values( '%q', %d)", path, reg_id);
00413 do_sql(sql);
00414 id = sqlite3_last_insert_rowid(database);
00415 }
00416 sqlite3_free(sql);
00417 sqlite3_free_table(line);
00418
00419 return id;
00420 }
00421
00428 static int store_time(void) {
00429 char **line;
00430 char *sql;
00431 int nrow, ncolumn;
00432 char date[50];
00433 time_t now;
00434 timeofday_t tod;
00435
00436 cf_get_time(&tod);
00437 now = time(NULL);
00438
00439 if (tod.day == last_stored_day)
00440 return 0;
00441 last_stored_day = tod.day;
00442
00443 snprintf(date, 50, "%10d-%2d-%2d %2d:%2d", tod.year, tod.month, tod.day, tod.hour, tod.minute);
00444
00445 sql = sqlite3_mprintf("select * from time where time_ingame='%q'", date);
00446 sqlite3_get_table(database, sql, &line, &nrow, &ncolumn, NULL);
00447 sqlite3_free(sql);
00448 sqlite3_free_table(line);
00449 if (nrow > 0)
00450 return 0;
00451
00452 sql = sqlite3_mprintf("insert into time (time_ingame, time_real) values( '%s', %d )", date, now);
00453 do_sql(sql);
00454 sqlite3_free(sql);
00455 return 1;
00456 }
00457
00466 static void add_player_event(object *pl, int event_code) {
00467 int id = get_living_id(pl);
00468 int map_id = 0;
00469 char *sql;
00470
00471 if (pl->map)
00472 map_id = get_map_id(pl->map);
00473
00474 sql = sqlite3_mprintf("insert into living_event values( %d, %d, %d, %d)", id, time(NULL), event_code, map_id);
00475 do_sql(sql);
00476 sqlite3_free(sql);
00477 }
00478
00489 static void add_map_event(mapstruct *map, int event_code, object *pl) {
00490 int mapid;
00491 int playerid = 0;
00492 char *sql;
00493
00494 if (pl && pl->type == PLAYER)
00495 playerid = get_living_id(pl);
00496
00497 mapid = get_map_id(map);
00498 sql = sqlite3_mprintf("insert into map_event values( %d, %d, %d, %d)", mapid, time(NULL), event_code, playerid);
00499 do_sql(sql);
00500 sqlite3_free(sql);
00501 }
00502
00513 static void add_death(object *victim, object *killer) {
00514 int vid, kid, map_id;
00515 char *sql;
00516
00517 if (!victim || !killer)
00518 return;
00519 if (victim->type != PLAYER && killer->type != PLAYER) {
00520
00521 object *owner = cf_object_get_object_property(killer, CFAPI_OBJECT_PROP_OWNER);
00522 if (owner != NULL && owner->type == PLAYER)
00523 killer = owner;
00524 else
00525 return;
00526 }
00527
00528 vid = get_living_id(victim);
00529 kid = get_living_id(killer);
00530 map_id = get_map_id(victim->map);
00531 sql = sqlite3_mprintf("insert into kill_event values( %d, %d, %d, %d, %d, %d)", time(NULL), vid, victim->level, map_id, kid, killer->level);
00532 do_sql(sql);
00533 sqlite3_free(sql);
00534 }
00535
00546 CF_PLUGIN int initPlugin(const char *iversion, f_plug_api gethooksptr) {
00547 cf_init_plugin(gethooksptr);
00548 cf_log(llevInfo, "%s init\n", PLUGIN_VERSION);
00549 return 0;
00550 }
00551
00562 CF_PLUGIN void *getPluginProperty(int *type, ...) {
00563 va_list args;
00564 const char *propname;
00565 char *buf;
00566 int size;
00567
00568 va_start(args, type);
00569 propname = va_arg(args, const char *);
00570
00571 if (!strcmp(propname, "Identification")) {
00572 buf = va_arg(args, char *);
00573 size = va_arg(args, int);
00574 va_end(args);
00575 snprintf(buf, size, PLUGIN_NAME);
00576 return NULL;
00577 } else if (!strcmp(propname, "FullName")) {
00578 buf = va_arg(args, char *);
00579 size = va_arg(args, int);
00580 va_end(args);
00581 snprintf(buf, size, PLUGIN_VERSION);
00582 return NULL;
00583 }
00584 va_end(args);
00585 return NULL;
00586 }
00587
00598 CF_PLUGIN int cflogger_runPluginCommand(object *op, char *params) {
00599 return -1;
00600 }
00601
00610 void *eventListener(int *type, ...) {
00611 static int rv = 0;
00612
00613 return &rv;
00614 }
00615
00624 CF_PLUGIN void *cflogger_globalEventListener(int *type, ...) {
00625 va_list args;
00626 static int rv = 0;
00627 player *pl;
00628 object *op;
00629 int event_code;
00630 mapstruct *map;
00631
00632 va_start(args, type);
00633 event_code = va_arg(args, int);
00634
00635 switch (event_code) {
00636 case EVENT_BORN:
00637 case EVENT_PLAYER_DEATH:
00638 case EVENT_REMOVE:
00639 case EVENT_MUZZLE:
00640 case EVENT_KICK:
00641 op = va_arg(args, object *);
00642 add_player_event(op, event_code);
00643 break;
00644
00645 case EVENT_LOGIN:
00646 case EVENT_LOGOUT:
00647 pl = va_arg(args, player *);
00648 add_player_event(pl->ob, event_code);
00649 break;
00650
00651 case EVENT_MAPENTER:
00652 case EVENT_MAPLEAVE:
00653 op = va_arg(args, object *);
00654 map = va_arg(args, mapstruct *);
00655 add_map_event(map, event_code, op);
00656 break;
00657
00658 case EVENT_MAPLOAD:
00659 case EVENT_MAPUNLOAD:
00660 case EVENT_MAPRESET:
00661 map = va_arg(args, mapstruct *);
00662 add_map_event(map, event_code, NULL);
00663 break;
00664
00665 case EVENT_GKILL: {
00666 object *killer;
00667 op = va_arg(args, object *);
00668 killer = va_arg(args, object *);
00669 add_death(op, killer);
00670 }
00671 break;
00672
00673 case EVENT_CLOCK:
00674 store_time();
00675 break;
00676 }
00677 va_end(args);
00678
00679 return &rv;
00680 }
00681
00690 CF_PLUGIN int postInitPlugin(void) {
00691 char path[500];
00692 const char *dir;
00693
00694 cf_log(llevInfo, "%s post init\n", PLUGIN_VERSION);
00695
00696 dir = cf_get_directory(4);
00697 snprintf(path, sizeof(path), "%s/cflogger.db", dir);
00698 cf_log(llevDebug, " [%s] database file: %s\n", PLUGIN_NAME, path);
00699
00700 if (sqlite3_open(path, &database) != SQLITE_OK) {
00701 cf_log(llevError, " [%s] database error!\n", PLUGIN_NAME);
00702 sqlite3_close(database);
00703 database = NULL;
00704 return 0;
00705 }
00706
00707 check_tables();
00708
00709 store_time();
00710
00711 cf_system_register_global_event(EVENT_BORN, PLUGIN_NAME, cflogger_globalEventListener);
00712 cf_system_register_global_event(EVENT_REMOVE, PLUGIN_NAME, cflogger_globalEventListener);
00713 cf_system_register_global_event(EVENT_GKILL, PLUGIN_NAME, cflogger_globalEventListener);
00714 cf_system_register_global_event(EVENT_LOGIN, PLUGIN_NAME, cflogger_globalEventListener);
00715 cf_system_register_global_event(EVENT_LOGOUT, PLUGIN_NAME, cflogger_globalEventListener);
00716
00717 cf_system_register_global_event(EVENT_PLAYER_DEATH, PLUGIN_NAME, cflogger_globalEventListener);
00718
00719 cf_system_register_global_event(EVENT_MAPENTER, PLUGIN_NAME, cflogger_globalEventListener);
00720 cf_system_register_global_event(EVENT_MAPLEAVE, PLUGIN_NAME, cflogger_globalEventListener);
00721 cf_system_register_global_event(EVENT_MAPRESET, PLUGIN_NAME, cflogger_globalEventListener);
00722 cf_system_register_global_event(EVENT_MAPLOAD, PLUGIN_NAME, cflogger_globalEventListener);
00723 cf_system_register_global_event(EVENT_MAPUNLOAD, PLUGIN_NAME, cflogger_globalEventListener);
00724
00725 cf_system_register_global_event(EVENT_MUZZLE, PLUGIN_NAME, cflogger_globalEventListener);
00726 cf_system_register_global_event(EVENT_KICK, PLUGIN_NAME, cflogger_globalEventListener);
00727
00728 cf_system_register_global_event(EVENT_CLOCK, PLUGIN_NAME, cflogger_globalEventListener);
00729
00730 return 0;
00731 }
00732
00741 CF_PLUGIN int closePlugin(void) {
00742 cf_log(llevInfo, "%s closing.\n", PLUGIN_VERSION);
00743 if (database) {
00744 sqlite3_close(database);
00745 database = NULL;
00746 }
00747 return 0;
00748 }