Crossfire Server, Branches 1.12  R18729
cfnewspaper.c
Go to the documentation of this file.
1 /*****************************************************************************/
2 /* Newspaper 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 
29 /* First let's include the header file needed */
30 
31 #include <cfnewspaper.h>
32 #include <stdarg.h>
33 #ifndef __CEXTRACT__
34 #include <cfnewspaper_proto.h>
35 #endif
36 #include <sqlite3.h>
37 
39 
41 
43 
45 
46 static sqlite3 *logger_database;
47 
48 static sqlite3 *newspaper_database;
49 
50 static void do_sql(const char *sql, sqlite3 *base) {
51  int err;
52  char *msg;
53 
54  if (!base)
55  return;
56 
57  err = sqlite3_exec(base, sql, NULL, NULL, &msg);
58  if (err != SQLITE_OK) {
59  cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
60  sqlite3_free(msg);
61  }
62 }
63 
64 static int get_living_id(object *living) {
65  char **line;
66  char *sql;
67  int nrow, ncolumn, id;
68 
69  if (living->type == PLAYER)
70  sql = sqlite3_mprintf("select liv_id from living where liv_name='%q' and liv_is_player = 1", living->name);
71  else
72  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);
73  sqlite3_get_table(logger_database, sql, &line, &nrow, &ncolumn, NULL);
74  /* printf("get_table: nrow = %d, ncolumn = %d\n", nrow, ncolumn); */
75  if (nrow > 0)
76  id = atoi(line[ncolumn]);
77  else {
78  sqlite3_free(sql);
79  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);
80  do_sql(sql, logger_database);
81  id = sqlite3_last_insert_rowid(logger_database);
82  }
83  sqlite3_free(sql);
84  sqlite3_free_table(line);
85  return id;
86 }
87 
88 static int get_region_id(region *reg) {
89  char **line;
90  char *sql;
91  int nrow, ncolumn, id;
92 
93  if (!reg)
94  return 0;
95 
96  sql = sqlite3_mprintf("select reg_id from region where reg_name='%q'", reg->name);
97  sqlite3_get_table(logger_database, sql, &line, &nrow, &ncolumn, NULL);
98 
99  if (nrow > 0)
100  id = atoi(line[ncolumn]);
101  else {
102  sqlite3_free(sql);
103  sql = sqlite3_mprintf("insert into region(reg_name) values( '%q' )", reg->name);
104  do_sql(sql, logger_database);
105  id = sqlite3_last_insert_rowid(logger_database);
106  }
107  sqlite3_free(sql);
108  sqlite3_free_table(line);
109  return id;
110 }
111 
112 static void format_time(timeofday_t *tod, char *buffer, int size) {
113  snprintf(buffer, size, "%10d-%2d-%2d %2d:%2d", tod->year, tod->month, tod->day, tod->hour, tod->minute);
114 }
115 
116 static int get_time_id(timeofday_t *tod, int create) {
117  char **line;
118  char *sql;
119  int nrow, ncolumn, id = 0;
120  char date[50];
121 
122  format_time(tod, date, 50);
123 
124  sql = sqlite3_mprintf("select time_id from time where time_time='%q'", date);
125  sqlite3_get_table(logger_database, sql, &line, &nrow, &ncolumn, NULL);
126  if (nrow > 0)
127  id = atoi(line[ncolumn]);
128  else if (create) {
129  sqlite3_free(sql);
130  sql = sqlite3_mprintf("insert into time(time_time) values( '%q' )", date);
131  do_sql(sql, logger_database);
132  id = sqlite3_last_insert_rowid(logger_database);
133  }
134  sqlite3_free(sql);
135  sqlite3_free_table(line);
136  return id;
137 }
138 
139 static void read_parameters(void) {
140 }
141 
142 CF_PLUGIN int initPlugin(const char *iversion, f_plug_api gethooksptr) {
143  cf_init_plugin(gethooksptr);
144  cf_log(llevInfo, "%s init\n", PLUGIN_VERSION);
145  return 0;
146 }
147 
148 CF_PLUGIN void *getPluginProperty(int *type, ...) {
149  va_list args;
150  const char *propname;
151  int size;
152  char *buf;
153 
154  va_start(args, type);
155  propname = va_arg(args, const char *);
156 
157  if (!strcmp(propname, "Identification")) {
158  buf = va_arg(args, char *);
159  size = va_arg(args, int);
160  va_end(args);
161  snprintf(buf, size, PLUGIN_NAME);
162  return NULL;
163  }
164 
165  if (!strcmp(propname, "FullName")) {
166  buf = va_arg(args, char *);
167  size = va_arg(args, int);
168  va_end(args);
169  snprintf(buf, size, PLUGIN_VERSION);
170  return NULL;
171  }
172 
173  va_end(args);
174  return NULL;
175 }
176 
177 CF_PLUGIN int cfnewspaper_runPluginCommand(object *op, char *params) {
178  return -1;
179 }
180 
182  va_list args;
183  static int rv = 0;
184  int event_code;
185 
186  va_start(args, type);
187  event_code = va_arg(args, int);
188 
189  switch (event_code) {
190  }
191  va_end(args);
192 
193  return &rv;
194 }
195 
197  char path[500];
198  const char *dir;
199 
200  cf_log(llevInfo, "%s post init\n", PLUGIN_VERSION);
201 
202  dir = cf_get_directory(4);
203  snprintf(path, 500, "%s/cflogger.db", dir);
204 
205  if (sqlite3_open(path, &logger_database) != SQLITE_OK) {
206  cf_log(llevError, " [%s] couldn't connect to logger database!\n", PLUGIN_NAME);
207  sqlite3_close(logger_database);
208  logger_database = NULL;
209  return 0;
210  }
211 
212  snprintf(path, 500, "%s/cfnewspaper.db", dir);
213  if (sqlite3_open(path, &newspaper_database) != SQLITE_OK) {
214  cf_log(llevError, " [%s] unable to open newspaper database!\n", PLUGIN_NAME);
215  sqlite3_close(logger_database);
216  sqlite3_close(newspaper_database);
217  logger_database = NULL;
218  newspaper_database = NULL;
219  return 0;
220  }
221 
222  read_parameters();
223 
224  return 0;
225 }
226 
227 typedef struct paper_properties {
228  const char *name;
232 
234  "world newspaper",
235  0,
236  1
237 };
238 
239 typedef struct kill_format {
240  const char *no_player_death;
241  const char *one_player_death;
242  const char *many_player_death;
243  const char *no_monster_death;
244  const char *one_monster_death;
245  const char *many_monster_death;
246 } kill_format;
247 
248 static paper_properties *get_newspaper(const char *name) {
249  return &default_properties;
250 }
251 
252 static void news_cat(char *buffer, int size, const char *format, ...) {
253  va_list args;
254 
255  size -= strlen(buffer)-1;
256  buffer += strlen(buffer);
257 
258  va_start(args, format);
259  vsprintf(buffer, format, args);
260  va_end(args);
261 }
262 
263 static void do_kills(char *buffer, int size, time_t start, time_t end, const char *reg, kill_format *format) {
264  char *sql;
265  char **results;
266  int deaths = 0;
267  int nrow, ncolumn;
268  int err;
269  char *msg;
270  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";
271 
272  sql = sqlite3_mprintf(raw, 1, start, end, reg);
273  err = sqlite3_get_table(logger_database, sql, &results, &nrow, &ncolumn, &msg);
274  if (err != SQLITE_OK) {
275  cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
276  sqlite3_free(msg);
277  }
278  if (nrow > 0 && results[ncolumn] != NULL)
279  deaths = atoi(results[ncolumn]);
280  sqlite3_free_table(results);
281 
282  if (deaths == 0)
283  news_cat(buffer, size, format->no_player_death);
284  else if (deaths == 1)
285  news_cat(buffer, size, format->one_player_death);
286  else
287  news_cat(buffer, size, format->many_player_death, deaths);
288  news_cat(buffer, size, "\n");
289 
290  sql = sqlite3_mprintf(raw, 0, start, end);
291  err = sqlite3_get_table(logger_database, sql, &results, &nrow, &ncolumn, &msg);
292  if (err != SQLITE_OK) {
293  cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
294  sqlite3_free(msg);
295  }
296  if (nrow > 0 && results[ncolumn] != NULL)
297  deaths = atoi(results[ncolumn]);
298  sqlite3_free_table(results);
299 
300  if (deaths == 0)
301  news_cat(buffer, size, format->no_monster_death);
302  else if (deaths == 1)
303  news_cat(buffer, size, format->one_monster_death);
304  else
305  news_cat(buffer, size, format->many_monster_death, deaths);
306  news_cat(buffer, size, "\n");
307 }
308 
309 static void do_region_kills(region *reg, char *buffer, int size, time_t start, time_t end) {
310  kill_format f;
311  char where[50];
312  int region_id;
313 
314  f.no_player_death = "No player died.";
315  f.one_player_death = "Only one player died, May Fido(tm) Have Mercy.";
316  f.many_player_death = "Monsters were busy, %d players died.";
317  f.no_monster_death = "No monster was killed, players were lazy around here.";
318  f.one_monster_death = "One poor monster was killed.";
319  f.many_monster_death = "Players tried hard to kill monsters, with %d victims.";
320 
321  region_id = get_region_id(reg);
322  snprintf(where, 50, "and map_reg_id = %d", region_id);
323 
324  do_kills(buffer, size, start, end, where, &f);
325 }
326 
327 static void do_region(region *reg, char *buffer, int size, time_t start, time_t end) {
328  news_cat(buffer, size, "--- local %s news ---\n", reg->name);
329  do_region_kills(reg, buffer, size, start, end);
330  news_cat(buffer, size, "\n\n");
331 }
332 
333 static void do_world_kills(char *buffer, int size, time_t start, time_t end) {
334  kill_format f;
335 
336  f.no_player_death = "No player died at all.";
337  f.one_player_death = "Only one player died in the whole world, May Fido(tm) Have Mercy.";
338  f.many_player_death = "Monsters all around the world were busy, %d players died.";
339  f.no_monster_death = "No monster was killed at all, players must be tired!";
340  f.one_monster_death = "One poor monster was killed in the whole, too bad for it.";
341  f.many_monster_death = "Bad day for monsters, with %d dead in their ranks.";
342  do_kills(buffer, size, start, end, "", &f);
343 }
344 
345 static void do_world(char *buffer, int size, time_t start, time_t end) {
346  news_cat(buffer, size, "--- worldnews section ---\n");
347  do_world_kills(buffer, size, start, end);
348  news_cat(buffer, size, "\n\n");
349 }
350 
351 static void get_newspaper_content(object *paper, paper_properties *properties, region *reg) {
352  char contents[5000];
353  char *sql;
354  char **results;
355  char date[50];
356  int nrow, ncolumn;
357  time_t start, end;
358  timeofday_t tod;
359  int err;
360  char *msg;
361 
362  start = 0;
363  time(&end);
364 
365  cf_get_time(&tod);
366  format_time(&tod, date, 50);
367 
368  sql = sqlite3_mprintf("select * from time where time_ingame < '%q' order by time_ingame desc", date);
369  err = sqlite3_get_table(logger_database, sql, &results, &nrow, &ncolumn, &msg);
370  if (err != SQLITE_OK) {
371  cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
372  sqlite3_free(msg);
373  }
374  if (nrow > 1 && results[ncolumn+1] != NULL) {
375  end = atol(results[ncolumn+1]);
376  if (nrow > 1 && results[ncolumn+2] != NULL)
377  start = atol(results[ncolumn+2]);
378  }
379 
380  contents[0] = '\0';
381 
382  if (properties->info_region)
383  do_region(reg, contents, 5000, start, end);
384 
385  if (properties->info_world)
386  do_world(contents, 5000, start, end);
387 
389 }
390 
391 CF_PLUGIN void *eventListener(int *type, ...) {
392  static int rv = 0;
393  va_list args;
394  object *who;
395  int event_code;
396  object *activator;
397  object *third;
398  object *event;
399  char *buf;
400  int fix;
401  object *newspaper;
402  paper_properties *paper;
403  region *reg;
404 
405  va_start(args, type);
406  who = va_arg(args, object *);
407  /*event_code = va_arg(args, int);*/
408  activator = va_arg(args, object *);
409  third = va_arg(args, object *);
410  buf = va_arg(args, char *);
411  fix = va_arg(args, int);
412  /*buf = va_arg(args, char *);*/
413  event = va_arg(args, object *);
414  event_code = event->subtype;
415 
416  va_end(args);
417 
418  if (event_code != EVENT_APPLY)
419  return &rv;
420 
421  paper = get_newspaper(event->slaying);
422 
423  newspaper = cf_create_object_by_name("scroll");
424 
427 
428  if (activator->map)
430  else
431  reg = NULL;
432 
433  get_newspaper_content(newspaper, paper, reg);
434 
435  cf_object_insert_object(newspaper, who);
436 
437  return &rv;
438 }
439 
441  cf_log(llevInfo, "%s closing.\n", PLUGIN_VERSION);
442  if (logger_database) {
443  sqlite3_close(logger_database);
444  logger_database = NULL;
445  }
446  if (newspaper_database) {
447  sqlite3_close(newspaper_database);
448  newspaper_database = NULL;
449  }
450  return 0;
451 }
static paper_properties * get_newspaper(const char *name)
Definition: cfnewspaper.c:248
#define CFAPI_OBJECT_PROP_NAME
Definition: plugin.h:176
void cf_get_time(timeofday_t *tod)
const char * one_player_death
Definition: cfnewspaper.c:241
static void do_region(region *reg, char *buffer, int size, time_t start, time_t end)
Definition: cfnewspaper.c:327
static sqlite3 * logger_database
Definition: cfnewspaper.c:46
static int get_time_id(timeofday_t *tod, int create)
Definition: cfnewspaper.c:116
struct kill_format kill_format
int minute
Definition: tod.h:67
region * cf_map_get_region_property(mapstruct *map, int propcode)
const char * name
Definition: cfnewspaper.c:228
CF_PLUGIN void * getPluginProperty(int *type,...)
Definition: cfnewspaper.c:148
static paper_properties default_properties
Definition: cfnewspaper.c:233
static void get_newspaper_content(object *paper, paper_properties *properties, region *reg)
Definition: cfnewspaper.c:351
#define CFAPI_OBJECT_PROP_NAME_PLURAL
Definition: plugin.h:177
const char * slaying
Definition: object.h:172
f_plug_api unregisterGlobalEvent
Definition: cfnewspaper.c:42
void *(* f_plug_api)(int *type,...)
Definition: plugin.h:121
#define CFAPI_OBJECT_PROP_MESSAGE
Definition: plugin.h:182
Definition: living.h:77
const char * many_monster_death
Definition: cfnewspaper.c:245
#define PLAYER
Definition: define.h:113
f_plug_api registerGlobalEvent
Definition: cfnewspaper.c:40
int year
Definition: tod.h:62
void cf_log(LogLevel logLevel, const char *format,...)
static void do_world_kills(char *buffer, int size, time_t start, time_t end)
Definition: cfnewspaper.c:333
const char * name
Definition: map.h:299
static sqlite3 * newspaper_database
Definition: cfnewspaper.c:48
CF_PLUGIN int postInitPlugin(void)
Definition: cfnewspaper.c:196
int cf_init_plugin(f_plug_api getHooks)
f_plug_api reCmp
Definition: cfnewspaper.c:44
struct mapdef * map
Definition: object.h:155
Definition: map.h:297
CF_PLUGIN int closePlugin(void)
Definition: cfnewspaper.c:440
int month
Definition: tod.h:63
#define PLUGIN_NAME
Definition: cfanim.h:32
const char * name
Definition: object.h:167
object * cf_object_insert_object(object *op, object *container)
int day
Definition: tod.h:64
f_plug_api gethook
Definition: cfnewspaper.c:38
const char * many_player_death
Definition: cfnewspaper.c:242
static void read_parameters(void)
Definition: cfnewspaper.c:139
static void news_cat(char *buffer, int size, const char *format,...)
Definition: cfnewspaper.c:252
CF_PLUGIN void * eventListener(int *type,...)
Definition: cfnewspaper.c:391
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
#define CF_PLUGIN
Definition: plugin_common.h:35
static void do_region_kills(region *reg, char *buffer, int size, time_t start, time_t end)
Definition: cfnewspaper.c:309
static void do_kills(char *buffer, int size, time_t start, time_t end, const char *reg, kill_format *format)
Definition: cfnewspaper.c:263
#define EVENT_APPLY
Definition: plugin.h:62
static void format_time(timeofday_t *tod, char *buffer, int size)
Definition: cfnewspaper.c:112
Definition: tod.h:61
#define CFAPI_MAP_PROP_REGION
Definition: plugin.h:300
int hour
Definition: tod.h:66
const char * cf_get_directory(int id)
static int get_living_id(object *living)
Definition: cfnewspaper.c:64
static int get_region_id(region *reg)
Definition: cfnewspaper.c:88
void cf_object_set_string_property(object *op, int propcode, const char *value)
CF_PLUGIN int initPlugin(const char *iversion, f_plug_api gethooksptr)
Definition: cfnewspaper.c:142
const char * no_player_death
Definition: cfnewspaper.c:240
const char * no_monster_death
Definition: cfnewspaper.c:243
CF_PLUGIN void * cfnewspaper_globalEventListener(int *type,...)
Definition: cfnewspaper.c:181
const char * one_monster_death
Definition: cfnewspaper.c:244
sint16 level
Definition: object.h:202
static void do_world(char *buffer, int size, time_t start, time_t end)
Definition: cfnewspaper.c:345
object * cf_create_object_by_name(const char *name)
uint8 type
Definition: object.h:189
static void do_sql(const char *sql, sqlite3 *base)
Definition: cfnewspaper.c:50
CF_PLUGIN int cfnewspaper_runPluginCommand(object *op, char *params)
Definition: cfnewspaper.c:177
struct paper_properties paper_properties
#define PLUGIN_VERSION
Definition: cfanim.h:33