Crossfire Server, Branches 1.12  R18729
cflogger.c
Go to the documentation of this file.
1 /*****************************************************************************/
2 /* Logger plugin version 1.0 alpha. */
3 /* Contact: */
4 /*****************************************************************************/
5 /* That code is placed under the GNU General Public Licence (GPL) */
6 /* (C)2007 by Weeger Nicolas (Feel free to deliver your complaints) */
7 /*****************************************************************************/
8 /* CrossFire, A Multiplayer game for X-windows */
9 /* */
10 /* Copyright (C) 2000 Mark Wedel */
11 /* Copyright (C) 1992 Frank Tore Johansen */
12 /* */
13 /* This program is free software; you can redistribute it and/or modify */
14 /* it under the terms of the GNU General Public License as published by */
15 /* the Free Software Foundation; either version 2 of the License, or */
16 /* (at your option) any later version. */
17 /* */
18 /* This program is distributed in the hope that it will be useful, */
19 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
20 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
21 /* GNU General Public License for more details. */
22 /* */
23 /* You should have received a copy of the GNU General Public License */
24 /* along with this program; if not, write to the Free Software */
25 /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
26 /* */
27 /*****************************************************************************/
28 
48 #include <cflogger.h>
49 #ifndef __CEXTRACT__
50 #include <cflogger_proto.h>
51 #endif
52 /*#include <stdarg.h>*/
53 
54 #include <sqlite3.h>
55 
57 #define CFLOGGER_CURRENT_FORMAT 3
58 
60 static sqlite3 *database;
61 
63 static int last_stored_day = -1;
64 
80 static int check_tables_callback(void *param, int argc, char **argv, char **azColName) {
81  int *format = (int *)param;
82 
83  *format = atoi(argv[0]);
84  return 0;
85 }
86 
102 static int do_sql(const char *sql) {
103  int err;
104  char *msg;
105 
106  if (!database)
107  return -1;
108 
109  err = sqlite3_exec(database, sql, NULL, NULL, &msg);
110  if (err != SQLITE_OK) {
111  cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
112  sqlite3_free(msg);
113  }
114  return err;
115 }
116 
143 static int update_table_format(const char *table, const char *newschema,
144  const char *select_columns) {
145  char *sql;
146  int err;
147 
148  sql = sqlite3_mprintf("ALTER TABLE %s RENAME TO %s_old;", table, table);
149  err = do_sql(sql);
150  sqlite3_free(sql);
151  if (err != SQLITE_OK)
152  return err;
153 
154  sql = sqlite3_mprintf("CREATE TABLE %s(%s);", table, newschema);
155  err = do_sql(sql);
156  sqlite3_free(sql);
157  if (err != SQLITE_OK)
158  return err;
159 
160  sql = sqlite3_mprintf("INSERT INTO %s SELECT %s FROM %s_old;",
161  table, select_columns, table);
162  err = do_sql(sql);
163  sqlite3_free(sql);
164  if (err != SQLITE_OK)
165  return err;
166 
167  sql = sqlite3_mprintf("DROP TABLE %s_old;", table, table);
168  err = do_sql(sql);
169  sqlite3_free(sql);
170  /* Final return. */
171  return err;
172 }
173 
180 #define DO_OR_ROLLBACK(sqlstring) \
181  if (do_sql(sqlstring) != SQLITE_OK) { \
182  do_sql("rollback transaction;"); \
183  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);\
184  sqlite3_close(database); \
185  database = NULL; \
186  return; \
187  }
188 
189 #define UPDATE_OR_ROLLBACK(tbl, newschema, select_columns) \
190  if (update_table_format((tbl), (newschema), (select_columns)) != SQLITE_OK) { \
191  do_sql("rollback transaction;"); \
192  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);\
193  sqlite3_close(database); \
194  database = NULL; \
195  return; \
196  }
197 
201 static void check_tables(void) {
202  int format;
203  int err;
204  format = 0;
205 
206  err = sqlite3_exec(database, "select param_value from parameters where param_name = 'version';", check_tables_callback, &format, NULL);
207 
208  /* Safety check. */
209  if (format > CFLOGGER_CURRENT_FORMAT) {
210  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);
211  /* This will disable using the db since do_sql() checks if database is
212  * NULL.
213  */
214  sqlite3_close(database);
215  database = NULL;
216  }
217 
218  /* Check if we need to upgrade/create database. */
219  if (format < 1) {
220  cf_log(llevDebug, " [%s] Creating logger database schema (format 1).\n", PLUGIN_NAME);
221  if (do_sql("BEGIN EXCLUSIVE TRANSACTION;") != SQLITE_OK) {
222  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);
223  sqlite3_close(database);
224  database = NULL;
225  return;
226  }
227  DO_OR_ROLLBACK("create table living(liv_id integer primary key autoincrement, liv_name text, liv_is_player integer, liv_level integer);");
228  DO_OR_ROLLBACK("create table region(reg_id integer primary key autoincrement, reg_name text);");
229  DO_OR_ROLLBACK("create table map(map_id integer primary key autoincrement, map_path text, map_reg_id integer);");
230  DO_OR_ROLLBACK("create table time(time_real integer, time_ingame text);");
231 
232  DO_OR_ROLLBACK("create table living_event(le_liv_id integer, le_time integer, le_code integer, le_map_id integer);");
233  DO_OR_ROLLBACK("create table map_event(me_map_id integer, me_time integer, me_code integer, me_living_id integer);");
234  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);");
235 
236  DO_OR_ROLLBACK("create table parameters(param_name text, param_value text);");
237  DO_OR_ROLLBACK("insert into parameters values( 'version', '1' );");
238  do_sql("COMMIT TRANSACTION;");
239  }
240 
241  /* Must be able to handle update from format 1. If we are creating a new
242  * database, format 1 is still created first, then updated.
243  *
244  * This way is simpler than having to create two ways to make a format 2 db.
245  */
246  if (format < 2) {
247  cf_log(llevDebug, " [%s] Upgrading logger database schema (to format 2).\n", PLUGIN_NAME);
248  if (do_sql("BEGIN EXCLUSIVE TRANSACTION;") != SQLITE_OK) {
249  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);
250  sqlite3_close(database);
251  database = NULL;
252  return;
253  }
254  /* Update schema for various tables. Why so complex? Because ALTER TABLE
255  * can't add the "primary key" bit or other constraints...
256  */
257  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", "*");
258  UPDATE_OR_ROLLBACK("region", "reg_id INTEGER PRIMARY KEY AUTOINCREMENT, reg_name TEXT UNIQUE NOT NULL", "*");
259  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)", "*");
260 #if 0
261  /* Turned out this was incorrect. And version 1 -> 3 directly works for this. */
262  UPDATE_OR_ROLLBACK("time", "time_real INTEGER PRIMARY KEY, time_ingame TEXT UNIQUE NOT NULL");
263 #endif
264  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", "*");
265  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", "*");
266  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", "*");
267 
268  /* Handle changed parameters table format: */
269  /* Due to backward compatiblity "primary key" in SQLite doesn't imply
270  * "not null" (http://www.sqlite.org/lang_createtable.html), unless it
271  * is "integer primary key".
272  *
273  * We don't need to save anything stored in this in format 1, it was
274  * only used for storing what format was used.
275  */
276  DO_OR_ROLLBACK("DROP TABLE parameters;");
277  DO_OR_ROLLBACK("CREATE TABLE parameters(param_name TEXT NOT NULL PRIMARY KEY, param_value TEXT);");
278  DO_OR_ROLLBACK("INSERT INTO parameters (param_name, param_value) VALUES( 'version', '2' );");
279 
280  /* Create various indexes. */
281  DO_OR_ROLLBACK("CREATE INDEX living_name_player_level ON living(liv_name,liv_is_player,liv_level);");
282 
283  /* Newspaper module could make use of some indexes too: */
284  DO_OR_ROLLBACK("CREATE INDEX kill_event_time ON kill_event(ke_time);");
285  DO_OR_ROLLBACK("CREATE INDEX map_reg_id ON map(map_reg_id);");
286 
287  /* Finally commit the transaction. */
288  do_sql("COMMIT TRANSACTION;");
289  }
290 
291  if (format < 3) {
292  cf_log(llevDebug, " [%s] Upgrading logger database schema (to format 3).\n", PLUGIN_NAME);
293  if (do_sql("BEGIN EXCLUSIVE TRANSACTION;") != SQLITE_OK) {
294  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);
295  sqlite3_close(database);
296  database = NULL;
297  return;
298  }
299  UPDATE_OR_ROLLBACK("time", "time_ingame TEXT NOT NULL PRIMARY KEY, time_real INTEGER NOT NULL", "time_ingame, time_real");
300  DO_OR_ROLLBACK("UPDATE parameters SET param_value = '3' WHERE param_name = 'version';");
301  do_sql("COMMIT TRANSACTION;");
302  /* After all these changes better vacuum... The tables could have been
303  * huge, and since we recreated several of them above there could be a
304  * lot of wasted space.
305  */
306  do_sql("VACUUM;");
307  }
308 }
309 
325 static int get_living_id(object *living) {
326  char **line;
327  char *sql;
328  int nrow, ncolumn, id;
329 
330  if (living->type == PLAYER)
331  sql = sqlite3_mprintf("select liv_id from living where liv_name='%q' and liv_is_player = 1", living->name);
332  else
333  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);
334  sqlite3_get_table(database, sql, &line, &nrow, &ncolumn, NULL);
335 
336  if (nrow > 0)
337  id = atoi(line[ncolumn]);
338  else {
339  sqlite3_free(sql);
340  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);
341  do_sql(sql);
342  id = sqlite3_last_insert_rowid(database);
343  }
344  sqlite3_free(sql);
345  sqlite3_free_table(line);
346  return id;
347 }
348 
359 static int get_region_id(region *reg) {
360  char **line;
361  char *sql;
362  int nrow, ncolumn, id;
363 
364  if (!reg)
365  return 0;
366 
367  sql = sqlite3_mprintf("select reg_id from region where reg_name='%q'", reg->name);
368  sqlite3_get_table(database, sql, &line, &nrow, &ncolumn, NULL);
369 
370  if (nrow > 0)
371  id = atoi(line[ncolumn]);
372  else {
373  sqlite3_free(sql);
374  sql = sqlite3_mprintf("insert into region(reg_name) values( '%q' )", reg->name);
375  do_sql(sql);
376  id = sqlite3_last_insert_rowid(database);
377  }
378  sqlite3_free(sql);
379  sqlite3_free_table(line);
380  return id;
381 }
382 
395 static int get_map_id(mapstruct *map) {
396  char **line;
397  char *sql;
398  int nrow, ncolumn, id, reg_id;
399  const char *path = map->path;
400 
401  if (strncmp(path, "/random/", 7) == 0)
402  path = "/random/";
403 
404  reg_id = get_region_id(map->region);
405  sql = sqlite3_mprintf("select map_id from map where map_path='%q' and map_reg_id = %d", path, reg_id);
406  sqlite3_get_table(database, sql, &line, &nrow, &ncolumn, NULL);
407 
408  if (nrow > 0)
409  id = atoi(line[ncolumn]);
410  else {
411  sqlite3_free(sql);
412  sql = sqlite3_mprintf("insert into map(map_path, map_reg_id) values( '%q', %d)", path, reg_id);
413  do_sql(sql);
414  id = sqlite3_last_insert_rowid(database);
415  }
416  sqlite3_free(sql);
417  sqlite3_free_table(line);
418 
419  return id;
420 }
421 
428 static int store_time(void) {
429  char **line;
430  char *sql;
431  int nrow, ncolumn;
432  char date[50];
433  time_t now;
434  timeofday_t tod;
435 
436  cf_get_time(&tod);
437  now = time(NULL);
438 
439  if (tod.day == last_stored_day)
440  return 0;
441  last_stored_day = tod.day;
442 
443  snprintf(date, 50, "%10d-%2d-%2d %2d:%2d", tod.year, tod.month, tod.day, tod.hour, tod.minute);
444 
445  sql = sqlite3_mprintf("select * from time where time_ingame='%q'", date);
446  sqlite3_get_table(database, sql, &line, &nrow, &ncolumn, NULL);
447  sqlite3_free(sql);
448  sqlite3_free_table(line);
449  if (nrow > 0)
450  return 0;
451 
452  sql = sqlite3_mprintf("insert into time (time_ingame, time_real) values( '%s', %d )", date, now);
453  do_sql(sql);
454  sqlite3_free(sql);
455  return 1;
456 }
457 
466 static void add_player_event(object *pl, int event_code) {
467  int id = get_living_id(pl);
468  int map_id = 0;
469  char *sql;
470 
471  if (pl->map)
472  map_id = get_map_id(pl->map);
473 
474  sql = sqlite3_mprintf("insert into living_event values( %d, %d, %d, %d)", id, time(NULL), event_code, map_id);
475  do_sql(sql);
476  sqlite3_free(sql);
477 }
478 
489 static void add_map_event(mapstruct *map, int event_code, object *pl) {
490  int mapid;
491  int playerid = 0;
492  char *sql;
493 
494  if (pl && pl->type == PLAYER)
495  playerid = get_living_id(pl);
496 
497  mapid = get_map_id(map);
498  sql = sqlite3_mprintf("insert into map_event values( %d, %d, %d, %d)", mapid, time(NULL), event_code, playerid);
499  do_sql(sql);
500  sqlite3_free(sql);
501 }
502 
513 static void add_death(object *victim, object *killer) {
514  int vid, kid, map_id;
515  char *sql;
516 
517  if (!victim || !killer)
518  return;
519  if (victim->type != PLAYER && killer->type != PLAYER) {
520  /* Killer might be a bullet, which might be owned by the player. */
522  if (owner != NULL && owner->type == PLAYER)
523  killer = owner;
524  else
525  return;
526  }
527 
528  vid = get_living_id(victim);
529  kid = get_living_id(killer);
530  map_id = get_map_id(victim->map);
531  sql = sqlite3_mprintf("insert into kill_event values( %d, %d, %d, %d, %d, %d)", time(NULL), vid, victim->level, map_id, kid, killer->level);
532  do_sql(sql);
533  sqlite3_free(sql);
534 }
535 
546 CF_PLUGIN int initPlugin(const char *iversion, f_plug_api gethooksptr) {
547  cf_init_plugin(gethooksptr);
548  cf_log(llevInfo, "%s init\n", PLUGIN_VERSION);
549  return 0;
550 }
551 
562 CF_PLUGIN void *getPluginProperty(int *type, ...) {
563  va_list args;
564  const char *propname;
565  char *buf;
566  int size;
567 
568  va_start(args, type);
569  propname = va_arg(args, const char *);
570 
571  if (!strcmp(propname, "Identification")) {
572  buf = va_arg(args, char *);
573  size = va_arg(args, int);
574  va_end(args);
575  snprintf(buf, size, PLUGIN_NAME);
576  return NULL;
577  } else if (!strcmp(propname, "FullName")) {
578  buf = va_arg(args, char *);
579  size = va_arg(args, int);
580  va_end(args);
581  snprintf(buf, size, PLUGIN_VERSION);
582  return NULL;
583  }
584  va_end(args);
585  return NULL;
586 }
587 
598 CF_PLUGIN int cflogger_runPluginCommand(object *op, char *params) {
599  return -1;
600 }
601 
610 void *eventListener(int *type, ...) {
611  static int rv = 0;
612 
613  return &rv;
614 }
615 
625  va_list args;
626  static int rv = 0;
627  player *pl;
628  object *op;
629  int event_code;
630  mapstruct *map;
631 
632  va_start(args, type);
633  event_code = va_arg(args, int);
634 
635  switch (event_code) {
636  case EVENT_BORN:
637  case EVENT_PLAYER_DEATH:
638  case EVENT_REMOVE:
639  case EVENT_MUZZLE:
640  case EVENT_KICK:
641  op = va_arg(args, object *);
642  add_player_event(op, event_code);
643  break;
644 
645  case EVENT_LOGIN:
646  case EVENT_LOGOUT:
647  pl = va_arg(args, player *);
648  add_player_event(pl->ob, event_code);
649  break;
650 
651  case EVENT_MAPENTER:
652  case EVENT_MAPLEAVE:
653  op = va_arg(args, object *);
654  map = va_arg(args, mapstruct *);
655  add_map_event(map, event_code, op);
656  break;
657 
658  case EVENT_MAPLOAD:
659  case EVENT_MAPUNLOAD:
660  case EVENT_MAPRESET:
661  map = va_arg(args, mapstruct *);
662  add_map_event(map, event_code, NULL);
663  break;
664 
665  case EVENT_GKILL: {
666  object *killer;
667  op = va_arg(args, object *);
668  killer = va_arg(args, object *);
669  add_death(op, killer);
670  }
671  break;
672 
673  case EVENT_CLOCK:
674  store_time();
675  break;
676  }
677  va_end(args);
678 
679  return &rv;
680 }
681 
691  char path[500];
692  const char *dir;
693 
694  cf_log(llevInfo, "%s post init\n", PLUGIN_VERSION);
695 
696  dir = cf_get_directory(4);
697  snprintf(path, sizeof(path), "%s/cflogger.db", dir);
698  cf_log(llevDebug, " [%s] database file: %s\n", PLUGIN_NAME, path);
699 
700  if (sqlite3_open(path, &database) != SQLITE_OK) {
701  cf_log(llevError, " [%s] database error!\n", PLUGIN_NAME);
702  sqlite3_close(database);
703  database = NULL;
704  return 0;
705  }
706 
707  check_tables();
708 
709  store_time();
710 
716 
718 
724 
727 
729 
730  return 0;
731 }
732 
742  cf_log(llevInfo, "%s closing.\n", PLUGIN_VERSION);
743  if (database) {
744  sqlite3_close(database);
745  database = NULL;
746  }
747  return 0;
748 }
char path[HUGE_BUF]
Definition: map.h:384
Definition: player.h:146
static sqlite3 * database
Definition: cflogger.c:60
#define EVENT_REMOVE
Definition: plugin.h:89
void cf_get_time(timeofday_t *tod)
#define EVENT_KICK
Definition: plugin.h:93
CF_PLUGIN int closePlugin(void)
Definition: cflogger.c:741
#define EVENT_LOGIN
Definition: plugin.h:84
int minute
Definition: tod.h:67
#define EVENT_MAPLEAVE
Definition: plugin.h:87
#define EVENT_LOGOUT
Definition: plugin.h:85
void *(* f_plug_api)(int *type,...)
Definition: plugin.h:121
#define CFAPI_OBJECT_PROP_OWNER
Definition: plugin.h:237
Definition: living.h:77
void cf_system_register_global_event(int event, const char *name, f_plug_api hook)
#define PLAYER
Definition: define.h:113
int year
Definition: tod.h:62
void cf_log(LogLevel logLevel, const char *format,...)
#define UPDATE_OR_ROLLBACK(tbl, newschema, select_columns)
Definition: cflogger.c:189
const char * name
Definition: map.h:299
static int do_sql(const char *sql)
Definition: cflogger.c:102
static void add_death(object *victim, object *killer)
Definition: cflogger.c:513
#define EVENT_MAPENTER
Definition: plugin.h:86
int cf_init_plugin(f_plug_api getHooks)
static int update_table_format(const char *table, const char *newschema, const char *select_columns)
Definition: cflogger.c:143
static int get_map_id(mapstruct *map)
Definition: cflogger.c:395
struct mapdef * map
Definition: object.h:155
Definition: map.h:297
static int last_stored_day
Definition: cflogger.c:63
static void add_player_event(object *pl, int event_code)
Definition: cflogger.c:466
int month
Definition: tod.h:63
#define DO_OR_ROLLBACK(sqlstring)
Definition: cflogger.c:180
#define PLUGIN_NAME
Definition: cfanim.h:32
const char * name
Definition: object.h:167
CF_PLUGIN int cflogger_runPluginCommand(object *op, char *params)
Definition: cflogger.c:598
int day
Definition: tod.h:64
CF_PLUGIN void * cflogger_globalEventListener(int *type,...)
Definition: cflogger.c:624
#define EVENT_MAPLOAD
Definition: plugin.h:95
static void add_map_event(mapstruct *map, int event_code, object *pl)
Definition: cflogger.c:489
#define EVENT_MAPUNLOAD
Definition: plugin.h:94
#define EVENT_MAPRESET
Definition: plugin.h:88
static int get_living_id(object *living)
Definition: cflogger.c:325
#define CFLOGGER_CURRENT_FORMAT
Definition: cflogger.c:57
#define EVENT_GKILL
Definition: plugin.h:83
static void check_tables(void)
Definition: cflogger.c:201
object * ob
Definition: player.h:207
object * cf_object_get_object_property(object *op, int propcode)
#define EVENT_CLOCK
Definition: plugin.h:80
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
#define CF_PLUGIN
Definition: plugin_common.h:35
Definition: tod.h:61
int hour
Definition: tod.h:66
const char * cf_get_directory(int id)
#define EVENT_PLAYER_DEATH
Definition: plugin.h:82
void * eventListener(int *type,...)
Definition: cflogger.c:610
CF_PLUGIN void * getPluginProperty(int *type,...)
Definition: cflogger.c:562
CF_PLUGIN int initPlugin(const char *iversion, f_plug_api gethooksptr)
Definition: cflogger.c:546
static int store_time(void)
Definition: cflogger.c:428
Definition: map.h:346
sint16 level
Definition: object.h:202
static int get_region_id(region *reg)
Definition: cflogger.c:359
struct regiondef * region
Definition: map.h:350
#define EVENT_BORN
Definition: plugin.h:79
static int check_tables_callback(void *param, int argc, char **argv, char **azColName)
Definition: cflogger.c:80
CF_PLUGIN int postInitPlugin(void)
Definition: cflogger.c:690
uint8 type
Definition: object.h:189
#define EVENT_MUZZLE
Definition: plugin.h:92
#define PLUGIN_VERSION
Definition: cfanim.h:33