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