Crossfire Server, Branch 1.12  R12190
cfnewspaper.c
Go to the documentation of this file.
00001 /*****************************************************************************/
00002 /* Newspaper plugin version 1.0 alpha.                                       */
00003 /* Contact:                                                                  */
00004 /*****************************************************************************/
00005 /* That code is placed under the GNU General Public Licence (GPL)            */
00006 /* (C)2007 by Weeger Nicolas (Feel free to deliver your complaints)          */
00007 /*****************************************************************************/
00008 /*  CrossFire, A Multiplayer game for X-windows                              */
00009 /*                                                                           */
00010 /*  Copyright (C) 2000 Mark Wedel                                            */
00011 /*  Copyright (C) 1992 Frank Tore Johansen                                   */
00012 /*                                                                           */
00013 /*  This program is free software; you can redistribute it and/or modify     */
00014 /*  it under the terms of the GNU General Public License as published by     */
00015 /*  the Free Software Foundation; either version 2 of the License, or        */
00016 /*  (at your option) any later version.                                      */
00017 /*                                                                           */
00018 /*  This program is distributed in the hope that it will be useful,          */
00019 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
00020 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
00021 /*  GNU General Public License for more details.                             */
00022 /*                                                                           */
00023 /*  You should have received a copy of the GNU General Public License        */
00024 /*  along with this program; if not, write to the Free Software              */
00025 /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
00026 /*                                                                           */
00027 /*****************************************************************************/
00028 
00029 /* First let's include the header file needed                                */
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     /* printf("get_table: nrow = %d, ncolumn = %d\n", nrow, ncolumn); */
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     /*event_code = va_arg(args, int);*/
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     /*buf = va_arg(args, char *);*/
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 }