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
00029
00030
00031 #include <cfnewspaper.h>
00032 #include <stdarg.h>
00033 #ifndef __CEXTRACT__
00034 #include <cfnewspaper_proto.h>
00035 #endif
00036 #include <sqlite3.h>
00037
00038 f_plug_api gethook;
00039
00040 f_plug_api registerGlobalEvent;
00041
00042 f_plug_api unregisterGlobalEvent;
00043
00044 f_plug_api reCmp;
00045
00046 static sqlite3 *logger_database;
00047
00048 static sqlite3 *newspaper_database;
00049
00050 static void do_sql(const char *sql, sqlite3 *base) {
00051 int err;
00052 char *msg;
00053
00054 if (!base)
00055 return;
00056
00057 err = sqlite3_exec(base, sql, NULL, NULL, &msg);
00058 if (err != SQLITE_OK) {
00059 cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
00060 sqlite3_free(msg);
00061 }
00062 }
00063
00064 static int get_living_id(object *living) {
00065 char **line;
00066 char *sql;
00067 int nrow, ncolumn, id;
00068
00069 if (living->type == PLAYER)
00070 sql = sqlite3_mprintf("select liv_id from living where liv_name='%q' and liv_is_player = 1", living->name);
00071 else
00072 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);
00073 sqlite3_get_table(logger_database, sql, &line, &nrow, &ncolumn, NULL);
00074
00075 if (nrow > 0)
00076 id = atoi(line[ncolumn]);
00077 else {
00078 sqlite3_free(sql);
00079 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);
00080 do_sql(sql, logger_database);
00081 id = sqlite3_last_insert_rowid(logger_database);
00082 }
00083 sqlite3_free(sql);
00084 sqlite3_free_table(line);
00085 return id;
00086 }
00087
00088 static int get_region_id(region *reg) {
00089 char **line;
00090 char *sql;
00091 int nrow, ncolumn, id;
00092
00093 if (!reg)
00094 return 0;
00095
00096 sql = sqlite3_mprintf("select reg_id from region where reg_name='%q'", reg->name);
00097 sqlite3_get_table(logger_database, sql, &line, &nrow, &ncolumn, NULL);
00098
00099 if (nrow > 0)
00100 id = atoi(line[ncolumn]);
00101 else {
00102 sqlite3_free(sql);
00103 sql = sqlite3_mprintf("insert into region(reg_name) values( '%q' )", reg->name);
00104 do_sql(sql, logger_database);
00105 id = sqlite3_last_insert_rowid(logger_database);
00106 }
00107 sqlite3_free(sql);
00108 sqlite3_free_table(line);
00109 return id;
00110 }
00111
00112 static void format_time(timeofday_t *tod, char *buffer, int size) {
00113 snprintf(buffer, size, "%10d-%2d-%2d %2d:%2d", tod->year, tod->month, tod->day, tod->hour, tod->minute);
00114 }
00115
00116 static int get_time_id(timeofday_t *tod, int create) {
00117 char **line;
00118 char *sql;
00119 int nrow, ncolumn, id = 0;
00120 char date[50];
00121
00122 format_time(tod, date, 50);
00123
00124 sql = sqlite3_mprintf("select time_id from time where time_time='%q'", date);
00125 sqlite3_get_table(logger_database, sql, &line, &nrow, &ncolumn, NULL);
00126 if (nrow > 0)
00127 id = atoi(line[ncolumn]);
00128 else if (create) {
00129 sqlite3_free(sql);
00130 sql = sqlite3_mprintf("insert into time(time_time) values( '%q' )", date);
00131 do_sql(sql, logger_database);
00132 id = sqlite3_last_insert_rowid(logger_database);
00133 }
00134 sqlite3_free(sql);
00135 sqlite3_free_table(line);
00136 return id;
00137 }
00138
00139 static void read_parameters(void) {
00140 }
00141
00142 CF_PLUGIN int initPlugin(const char *iversion, f_plug_api gethooksptr) {
00143 cf_init_plugin(gethooksptr);
00144 cf_log(llevInfo, "%s init\n", PLUGIN_VERSION);
00145 return 0;
00146 }
00147
00148 CF_PLUGIN void *getPluginProperty(int *type, ...) {
00149 va_list args;
00150 const char *propname;
00151 int size;
00152 char *buf;
00153
00154 va_start(args, type);
00155 propname = va_arg(args, const char *);
00156
00157 if (!strcmp(propname, "Identification")) {
00158 buf = va_arg(args, char *);
00159 size = va_arg(args, int);
00160 va_end(args);
00161 snprintf(buf, size, PLUGIN_NAME);
00162 return NULL;
00163 }
00164
00165 if (!strcmp(propname, "FullName")) {
00166 buf = va_arg(args, char *);
00167 size = va_arg(args, int);
00168 va_end(args);
00169 snprintf(buf, size, PLUGIN_VERSION);
00170 return NULL;
00171 }
00172
00173 va_end(args);
00174 return NULL;
00175 }
00176
00177 CF_PLUGIN int cfnewspaper_runPluginCommand(object *op, char *params) {
00178 return -1;
00179 }
00180
00181 CF_PLUGIN void *cfnewspaper_globalEventListener(int *type, ...) {
00182 va_list args;
00183 static int rv = 0;
00184 int event_code;
00185
00186 va_start(args, type);
00187 event_code = va_arg(args, int);
00188
00189 switch (event_code) {
00190 }
00191 va_end(args);
00192
00193 return &rv;
00194 }
00195
00196 CF_PLUGIN int postInitPlugin(void) {
00197 char path[500];
00198 const char *dir;
00199
00200 cf_log(llevInfo, "%s post init\n", PLUGIN_VERSION);
00201
00202 dir = cf_get_directory(4);
00203 snprintf(path, 500, "%s/cflogger.db", dir);
00204
00205 if (sqlite3_open(path, &logger_database) != SQLITE_OK) {
00206 cf_log(llevError, " [%s] couldn't connect to logger database!\n", PLUGIN_NAME);
00207 sqlite3_close(logger_database);
00208 logger_database = NULL;
00209 return 0;
00210 }
00211
00212 snprintf(path, 500, "%s/cfnewspaper.db", dir);
00213 if (sqlite3_open(path, &newspaper_database) != SQLITE_OK) {
00214 cf_log(llevError, " [%s] unable to open newspaper database!\n", PLUGIN_NAME);
00215 sqlite3_close(logger_database);
00216 sqlite3_close(newspaper_database);
00217 logger_database = NULL;
00218 newspaper_database = NULL;
00219 return 0;
00220 }
00221
00222 read_parameters();
00223
00224 return 0;
00225 }
00226
00227 typedef struct paper_properties {
00228 const char *name;
00229 int info_region;
00230 int info_world;
00231 } paper_properties;
00232
00233 static paper_properties default_properties = {
00234 "world newspaper",
00235 0,
00236 1
00237 };
00238
00239 typedef struct kill_format {
00240 const char *no_player_death;
00241 const char *one_player_death;
00242 const char *many_player_death;
00243 const char *no_monster_death;
00244 const char *one_monster_death;
00245 const char *many_monster_death;
00246 } kill_format;
00247
00248 static paper_properties *get_newspaper(const char *name) {
00249 return &default_properties;
00250 }
00251
00252 static void news_cat(char *buffer, int size, const char *format, ...) {
00253 va_list args;
00254
00255 size -= strlen(buffer)-1;
00256 buffer += strlen(buffer);
00257
00258 va_start(args, format);
00259 vsprintf(buffer, format, args);
00260 va_end(args);
00261 }
00262
00263 static void do_kills(char *buffer, int size, time_t start, time_t end, const char *reg, kill_format *format) {
00264 char *sql;
00265 char **results;
00266 int deaths = 0;
00267 int nrow, ncolumn;
00268 int err;
00269 char *msg;
00270 const char *raw = "select sum(1) as deaths from kill_event inner join living on liv_id = ke_victim_id where liv_is_player = %d and ke_time >= %d and ke_time < %d %s";
00271
00272 sql = sqlite3_mprintf(raw, 1, start, end, reg);
00273 err = sqlite3_get_table(logger_database, sql, &results, &nrow, &ncolumn, &msg);
00274 if (err != SQLITE_OK) {
00275 cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
00276 sqlite3_free(msg);
00277 }
00278 if (nrow > 0 && results[ncolumn] != NULL)
00279 deaths = atoi(results[ncolumn]);
00280 sqlite3_free_table(results);
00281
00282 if (deaths == 0)
00283 news_cat(buffer, size, format->no_player_death);
00284 else if (deaths == 1)
00285 news_cat(buffer, size, format->one_player_death);
00286 else
00287 news_cat(buffer, size, format->many_player_death, deaths);
00288 news_cat(buffer, size, "\n");
00289
00290 sql = sqlite3_mprintf(raw, 0, start, end);
00291 err = sqlite3_get_table(logger_database, sql, &results, &nrow, &ncolumn, &msg);
00292 if (err != SQLITE_OK) {
00293 cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
00294 sqlite3_free(msg);
00295 }
00296 if (nrow > 0 && results[ncolumn] != NULL)
00297 deaths = atoi(results[ncolumn]);
00298 sqlite3_free_table(results);
00299
00300 if (deaths == 0)
00301 news_cat(buffer, size, format->no_monster_death);
00302 else if (deaths == 1)
00303 news_cat(buffer, size, format->one_monster_death);
00304 else
00305 news_cat(buffer, size, format->many_monster_death, deaths);
00306 news_cat(buffer, size, "\n");
00307 }
00308
00309 static void do_region_kills(region *reg, char *buffer, int size, time_t start, time_t end) {
00310 kill_format f;
00311 char where[50];
00312 int region_id;
00313
00314 f.no_player_death = "No player died.";
00315 f.one_player_death = "Only one player died, May Fido(tm) Have Mercy.";
00316 f.many_player_death = "Monsters were busy, %d players died.";
00317 f.no_monster_death = "No monster was killed, players were lazy around here.";
00318 f.one_monster_death = "One poor monster was killed.";
00319 f.many_monster_death = "Players tried hard to kill monsters, with %d victims.";
00320
00321 region_id = get_region_id(reg);
00322 snprintf(where, 50, "and map_reg_id = %d", region_id);
00323
00324 do_kills(buffer, size, start, end, where, &f);
00325 }
00326
00327 static void do_region(region *reg, char *buffer, int size, time_t start, time_t end) {
00328 news_cat(buffer, size, "--- local %s news ---\n", reg->name);
00329 do_region_kills(reg, buffer, size, start, end);
00330 news_cat(buffer, size, "\n\n");
00331 }
00332
00333 static void do_world_kills(char *buffer, int size, time_t start, time_t end) {
00334 kill_format f;
00335
00336 f.no_player_death = "No player died at all.";
00337 f.one_player_death = "Only one player died in the whole world, May Fido(tm) Have Mercy.";
00338 f.many_player_death = "Monsters all around the world were busy, %d players died.";
00339 f.no_monster_death = "No monster was killed at all, players must be tired!";
00340 f.one_monster_death = "One poor monster was killed in the whole, too bad for it.";
00341 f.many_monster_death = "Bad day for monsters, with %d dead in their ranks.";
00342 do_kills(buffer, size, start, end, "", &f);
00343 }
00344
00345 static void do_world(char *buffer, int size, time_t start, time_t end) {
00346 news_cat(buffer, size, "--- worldnews section ---\n");
00347 do_world_kills(buffer, size, start, end);
00348 news_cat(buffer, size, "\n\n");
00349 }
00350
00351 static void get_newspaper_content(object *paper, paper_properties *properties, region *reg) {
00352 char contents[5000];
00353 char *sql;
00354 char **results;
00355 char date[50];
00356 int nrow, ncolumn;
00357 time_t start, end;
00358 timeofday_t tod;
00359 int err;
00360 char *msg;
00361
00362 start = 0;
00363 time(&end);
00364
00365 cf_get_time(&tod);
00366 format_time(&tod, date, 50);
00367
00368 sql = sqlite3_mprintf("select * from time where time_ingame < '%q' order by time_ingame desc", date);
00369 err = sqlite3_get_table(logger_database, sql, &results, &nrow, &ncolumn, &msg);
00370 if (err != SQLITE_OK) {
00371 cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
00372 sqlite3_free(msg);
00373 }
00374 if (nrow > 1 && results[ncolumn+1] != NULL) {
00375 end = atol(results[ncolumn+1]);
00376 if (nrow > 1 && results[ncolumn+2] != NULL)
00377 start = atol(results[ncolumn+2]);
00378 }
00379
00380 contents[0] = '\0';
00381
00382 if (properties->info_region)
00383 do_region(reg, contents, 5000, start, end);
00384
00385 if (properties->info_world)
00386 do_world(contents, 5000, start, end);
00387
00388 cf_object_set_string_property(paper, CFAPI_OBJECT_PROP_MESSAGE, contents);
00389 }
00390
00391 CF_PLUGIN void *eventListener(int *type, ...) {
00392 static int rv = 0;
00393 va_list args;
00394 object *who;
00395 int event_code;
00396 object *activator;
00397 object *third;
00398 object *event;
00399 char *buf;
00400 int fix;
00401 object *newspaper;
00402 paper_properties *paper;
00403 region *reg;
00404
00405 va_start(args, type);
00406 who = va_arg(args, object *);
00407
00408 activator = va_arg(args, object *);
00409 third = va_arg(args, object *);
00410 buf = va_arg(args, char *);
00411 fix = va_arg(args, int);
00412
00413 event = va_arg(args, object *);
00414 event_code = event->subtype;
00415
00416 va_end(args);
00417
00418 if (event_code != EVENT_APPLY)
00419 return &rv;
00420
00421 paper = get_newspaper(event->slaying);
00422
00423 newspaper = cf_create_object_by_name("scroll");
00424
00425 cf_object_set_string_property(newspaper, CFAPI_OBJECT_PROP_NAME, paper->name);
00426 cf_object_set_string_property(newspaper, CFAPI_OBJECT_PROP_NAME_PLURAL, paper->name);
00427
00428 if (activator->map)
00429 reg = cf_map_get_region_property(activator->map, CFAPI_MAP_PROP_REGION);
00430 else
00431 reg = NULL;
00432
00433 get_newspaper_content(newspaper, paper, reg);
00434
00435 cf_object_insert_object(newspaper, who);
00436
00437 return &rv;
00438 }
00439
00440 CF_PLUGIN int closePlugin(void) {
00441 cf_log(llevInfo, "%s closing.\n", PLUGIN_VERSION);
00442 if (logger_database) {
00443 sqlite3_close(logger_database);
00444 logger_database = NULL;
00445 }
00446 if (newspaper_database) {
00447 sqlite3_close(newspaper_database);
00448 newspaper_database = NULL;
00449 }
00450 return 0;
00451 }