Crossfire Server, Trunk  R20513
cfpython.c
Go to the documentation of this file.
1 /*****************************************************************************/
2 /* CFPython - A Python module for Crossfire RPG. */
3 /*****************************************************************************/
4 /* This is the third version of the Crossfire Scripting Engine. */
5 /* The first version used Guile. It was directly integrated in the server */
6 /* code, but since Guile wasn't perceived as an easy-to-learn, easy-to-use */
7 /* language by many, it was dropped in favor of Python. */
8 /* The second version, CFPython 1.0, was included as a plugin and provided */
9 /* just about the same level of functionality the current version has. But */
10 /* it used a rather counter-intuitive, procedural way of presenting things. */
11 /* */
12 /* CFPython 2.0 aims at correcting many of the design flaws crippling the */
13 /* older version. It is also the first plugin to be implemented using the */
14 /* new interface, that doesn't need awkward stuff like the horrible CFParm */
15 /* structure. For the Python writer, things should probably be easier and */
16 /* lead to more readable code: instead of writing "CFPython.getObjectXPos(ob)*/
17 /* he/she now can simply write "ob.X". */
18 /* */
19 /*****************************************************************************/
20 /* Please note that it is still very beta - some of the functions may not */
21 /* work as expected and could even cause the server to crash. */
22 /*****************************************************************************/
23 /* Version history: */
24 /* 0.1 "Ophiuchus" - Initial Alpha release */
25 /* 0.5 "Stalingrad" - Message length overflow corrected. */
26 /* 0.6 "Kharkov" - Message and Write correctly redefined. */
27 /* 0.7 "Koursk" - Setting informations implemented. */
28 /* 1.0a "Petersburg" - Last "old-fashioned" version, never submitted to CVS.*/
29 /* 2.0 "Arkangelsk" - First release of the 2.x series. */
30 /*****************************************************************************/
31 /* Version: 2.0beta8 (also known as "Alexander") */
32 /* Contact: yann.chachkoff@myrealbox.com */
33 /*****************************************************************************/
34 /* That code is placed under the GNU General Public Licence (GPL) */
35 /* (C)2001-2005 by Chachkoff Yann (Feel free to deliver your complaints) */
36 /*****************************************************************************/
37 /* CrossFire, A Multiplayer game for X-windows */
38 /* */
39 /* Copyright (C) 2000 Mark Wedel */
40 /* Copyright (C) 1992 Frank Tore Johansen */
41 /* */
42 /* This program is free software; you can redistribute it and/or modify */
43 /* it under the terms of the GNU General Public License as published by */
44 /* the Free Software Foundation; either version 2 of the License, or */
45 /* (at your option) any later version. */
46 /* */
47 /* This program is distributed in the hope that it will be useful, */
48 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
49 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
50 /* GNU General Public License for more details. */
51 /* */
52 /* You should have received a copy of the GNU General Public License */
53 /* along with this program; if not, write to the Free Software */
54 /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
55 /* */
56 /*****************************************************************************/
57 
58 /* First let's include the header file needed */
59 
60 #include <cfpython.h>
61 #include <stdarg.h>
62 #include <node.h>
63 #include <svnversion.h>
64 
65 CF_PLUGIN char SvnRevPlugin[] = SVN_REV;
66 
67 #define PYTHON_DEBUG /* give us some general infos out */
68 #define PYTHON_CACHE_SIZE 16 /* number of python scripts to store the bytecode of at a time */
69 
73 typedef struct {
75  PyCodeObject *code;
76  time_t cached_time,
77  used_time;
79 
80 /* This structure is used to define one python-implemented crossfire command.*/
81 typedef struct PythonCmdStruct {
82  sstring name; /* The name of the command, as known in the game. */
83  sstring script; /* The name of the script file to bind. */
84  double speed; /* The speed of the command execution. */
85 } PythonCmd;
86 
87 /* This plugin allows up to 1024 custom commands. */
88 #define NR_CUSTOM_CMD 1024
89 
92 
95 
96 static PyObject *CFPythonError;
97 
99 static void set_exception(const char *fmt, ...) {
100  char buf[1024];
101  va_list arg;
102 
103  va_start(arg, fmt);
104  vsnprintf(buf, sizeof(buf), fmt, arg);
105  va_end(arg);
106 
107  PyErr_SetString(PyExc_ValueError, buf);
108 }
109 
111 
113 
114 static int current_command = -999;
115 
116 static PyObject *shared_data = NULL;
117 
118 static PyObject *private_data = NULL;
119 
120 static PyObject *registerGEvent(PyObject *self, PyObject *args) {
121  int eventcode;
122 
123  if (!PyArg_ParseTuple(args, "i", &eventcode))
124  return NULL;
125 
127 
128  Py_INCREF(Py_None);
129  return Py_None;
130 }
131 
132 static PyObject *unregisterGEvent(PyObject *self, PyObject *args) {
133  int eventcode;
134 
135  if (!PyArg_ParseTuple(args, "i", &eventcode))
136  return NULL;
137 
139 
140  Py_INCREF(Py_None);
141  return Py_None;
142 }
143 
144 static PyObject *createCFObject(PyObject *self, PyObject *args) {
145  object *op;
146 
147  op = cf_create_object();
148 
149  return Crossfire_Object_wrap(op);
150 }
151 
152 static PyObject *createCFObjectByName(PyObject *self, PyObject *args) {
153  char *obname;
154  object *op;
155 
156  if (!PyArg_ParseTuple(args, "s", &obname))
157  return NULL;
158 
159  op = cf_create_object_by_name(obname);
160 
161  return Crossfire_Object_wrap(op);
162 }
163 
164 static PyObject *getCFPythonVersion(PyObject *self, PyObject *args) {
165  int i = 2044;
166 
167  return Py_BuildValue("i", i);
168 }
169 
170 static PyObject *getReturnValue(PyObject *self, PyObject *args) {
171  return Py_BuildValue("i", current_context->returnvalue);
172 }
173 
174 static PyObject *setReturnValue(PyObject *self, PyObject *args) {
175  int i;
176 
177  if (!PyArg_ParseTuple(args, "i", &i))
178  return NULL;
179  current_context->returnvalue = i;
180  Py_INCREF(Py_None);
181  return Py_None;
182 }
183 
184 static PyObject *matchString(PyObject *self, PyObject *args) {
185  char *premiere;
186  char *seconde;
187  const char *result;
188 
189  if (!PyArg_ParseTuple(args, "ss", &premiere, &seconde))
190  return NULL;
191 
192  result = cf_re_cmp(premiere, seconde);
193  if (result != NULL)
194  return Py_BuildValue("i", 1);
195  else
196  return Py_BuildValue("i", 0);
197 }
198 
199 static PyObject *findPlayer(PyObject *self, PyObject *args) {
200  player *foundpl;
201  char *txt;
202 
203  if (!PyArg_ParseTuple(args, "s", &txt))
204  return NULL;
205 
206  foundpl = cf_player_find(txt);
207 
208  if (foundpl != NULL)
209  return Py_BuildValue("O", Crossfire_Object_wrap(foundpl->ob));
210  else {
211  Py_INCREF(Py_None);
212  return Py_None;
213  }
214 }
215 
216 static PyObject *readyMap(PyObject *self, PyObject *args) {
217  char *mapname;
218  mapstruct *map;
219  int flags = 0;
220 
221  if (!PyArg_ParseTuple(args, "s|i", &mapname, &flags))
222  return NULL;
223 
224  map = cf_map_get_map(mapname, flags);
225 
226  return Crossfire_Map_wrap(map);
227 }
228 
229 static PyObject *createMap(PyObject *self, PyObject *args) {
230  int sizex, sizey;
231  mapstruct *map;
232 
233  if (!PyArg_ParseTuple(args, "ii", &sizex, &sizey))
234  return NULL;
235 
236  map = cf_get_empty_map(sizex, sizey);
237 
238  return Crossfire_Map_wrap(map);
239 }
240 
241 static PyObject *getMapDirectory(PyObject *self, PyObject *args) {
242  return Py_BuildValue("s", cf_get_directory(0));
243 }
244 
245 static PyObject *getUniqueDirectory(PyObject *self, PyObject *args) {
246  return Py_BuildValue("s", cf_get_directory(1));
247 }
248 
249 static PyObject *getTempDirectory(PyObject *self, PyObject *args) {
250  return Py_BuildValue("s", cf_get_directory(2));
251 }
252 
253 static PyObject *getConfigDirectory(PyObject *self, PyObject *args) {
254  return Py_BuildValue("s", cf_get_directory(3));
255 }
256 
257 static PyObject *getLocalDirectory(PyObject *self, PyObject *args) {
258  return Py_BuildValue("s", cf_get_directory(4));
259 }
260 
261 static PyObject *getPlayerDirectory(PyObject *self, PyObject *args) {
262  return Py_BuildValue("s", cf_get_directory(5));
263 }
264 
265 static PyObject *getDataDirectory(PyObject *self, PyObject *args) {
266  return Py_BuildValue("s", cf_get_directory(6));
267 }
268 
269 static PyObject *getWhoAmI(PyObject *self, PyObject *args) {
270  if (!current_context->who) {
271  Py_INCREF(Py_None);
272  return Py_None;
273  }
274  Py_INCREF(current_context->who);
275  return current_context->who;
276 }
277 
278 static PyObject *getWhoIsActivator(PyObject *self, PyObject *args) {
279  if (!current_context->activator) {
280  Py_INCREF(Py_None);
281  return Py_None;
282  }
283  Py_INCREF(current_context->activator);
284  return current_context->activator;
285 }
286 
287 static PyObject *getWhoIsThird(PyObject *self, PyObject *args) {
288  if (!current_context->third) {
289  Py_INCREF(Py_None);
290  return Py_None;
291  }
292  Py_INCREF(current_context->third);
293  return current_context->third;
294 }
295 
296 static PyObject *getWhatIsMessage(PyObject *self, PyObject *args) {
297  if (*current_context->message == '\0')
298  return Py_BuildValue("");
299  else
300  return Py_BuildValue("s", current_context->message);
301 }
302 
303 static PyObject *getScriptName(PyObject *self, PyObject *args) {
304  return Py_BuildValue("s", current_context->script);
305 }
306 
307 static PyObject *getScriptParameters(PyObject *self, PyObject *args) {
308  if (!*current_context->options) {
309  Py_INCREF(Py_None);
310  return Py_None;
311  }
312  return Py_BuildValue("s", current_context->options);
313 }
314 
315 static PyObject *getEvent(PyObject *self, PyObject *args) {
316  if (!current_context->event) {
317  Py_INCREF(Py_None);
318  return Py_None;
319  }
320  Py_INCREF(current_context->event);
321  return current_context->event;
322 }
323 
324 static PyObject *getPrivateDictionary(PyObject *self, PyObject *args) {
325  PyObject *data;
326 
327  data = PyDict_GetItemString(private_data, current_context->script);
328  if (!data) {
329  data = PyDict_New();
330  PyDict_SetItemString(private_data, current_context->script, data);
331  Py_DECREF(data);
332  }
333  Py_INCREF(data);
334  return data;
335 }
336 
337 static PyObject *getSharedDictionary(PyObject *self, PyObject *args) {
338  Py_INCREF(shared_data);
339  return shared_data;
340 }
341 
342 static PyObject *getArchetypes(PyObject *self, PyObject *args) {
343  PyObject *list;
344  archetype *arch;
345 
346  list = PyList_New(0);
347  arch = cf_archetype_get_first();
348  while (arch) {
349  PyList_Append(list, Crossfire_Archetype_wrap(arch));
350  arch = cf_archetype_get_next(arch);
351  }
352  return list;
353 }
354 
355 static PyObject *getPlayers(PyObject *self, PyObject *args) {
356  PyObject *list;
357  object *pl;
358 
359  list = PyList_New(0);
361  while (pl) {
362  PyList_Append(list, Crossfire_Object_wrap(pl));
364  }
365  return list;
366 }
367 
368 static PyObject *getMaps(PyObject *self, PyObject *args) {
369  PyObject *list;
370  mapstruct *map;
371 
372  list = PyList_New(0);
373  map = cf_map_get_first();
374  while (map) {
375  PyList_Append(list, Crossfire_Map_wrap(map));
377  }
378  return list;
379 }
380 
381 static PyObject *getParties(PyObject *self, PyObject *args) {
382  PyObject *list;
383  partylist *party;
384 
385  list = PyList_New(0);
386  party = cf_party_get_first();
387  while (party) {
388  PyList_Append(list, Crossfire_Party_wrap(party));
389  party = cf_party_get_next(party);
390  }
391  return list;
392 }
393 
394 static PyObject *getRegions(PyObject *self, PyObject *args) {
395  PyObject *list;
396  region *reg;
397 
398  list = PyList_New(0);
399  reg = cf_region_get_first();
400  while (reg) {
401  PyList_Append(list, Crossfire_Region_wrap(reg));
402  reg = cf_region_get_next(reg);
403  }
404  return list;
405 }
406 
407 static PyObject *getFriendlyList(PyObject *self, PyObject *args) {
408  PyObject *list;
409  object *ob;
410 
411  list = PyList_New(0);
413  while (ob) {
414  PyList_Append(list, Crossfire_Object_wrap(ob));
415  ob = cf_friendlylist_get_next(ob);
416  }
417  return list;
418 }
419 
420 static PyObject *registerCommand(PyObject *self, PyObject *args) {
421  char *cmdname;
422  char *scriptname;
423  double cmdspeed;
424  int i;
425 
426  if (!PyArg_ParseTuple(args, "ssd", &cmdname, &scriptname, &cmdspeed))
427  return NULL;
428 
429  if (cmdspeed < 0) {
430  set_exception("speed must not be negative");
431  return NULL;
432  }
433 
434  for (i = 0; i < NR_CUSTOM_CMD; i++) {
435  if (CustomCommand[i].name != NULL) {
436  if (!strcmp(CustomCommand[i].name, cmdname)) {
437  set_exception("command '%s' is already registered", cmdname);
438  return NULL;
439  }
440  }
441  }
442  for (i = 0; i < NR_CUSTOM_CMD; i++) {
443  if (CustomCommand[i].name == NULL) {
444  CustomCommand[i].name = cf_add_string(cmdname);
445  CustomCommand[i].script = cf_add_string(scriptname);
446  CustomCommand[i].speed = cmdspeed;
447  break;
448  }
449  }
450 
451  Py_INCREF(Py_None);
452  return Py_None;
453 }
454 
455 static PyObject *getTime(PyObject *self, PyObject *args) {
456  PyObject *list;
457  timeofday_t tod;
458 
459  cf_get_time(&tod);
460 
461  list = PyList_New(0);
462  PyList_Append(list, Py_BuildValue("i", tod.year));
463  PyList_Append(list, Py_BuildValue("i", tod.month));
464  PyList_Append(list, Py_BuildValue("i", tod.day));
465  PyList_Append(list, Py_BuildValue("i", tod.hour));
466  PyList_Append(list, Py_BuildValue("i", tod.minute));
467  PyList_Append(list, Py_BuildValue("i", tod.dayofweek));
468  PyList_Append(list, Py_BuildValue("i", tod.weekofmonth));
469  PyList_Append(list, Py_BuildValue("i", tod.season));
470  PyList_Append(list, Py_BuildValue("i", tod.periodofday));
471 
472  return list;
473 }
474 
475 static PyObject *destroyTimer(PyObject *self, PyObject *args) {
476  int id;
477 
478  if (!PyArg_ParseTuple(args, "i", &id))
479  return NULL;
480  return Py_BuildValue("i", cf_timer_destroy(id));
481 }
482 
483 static PyObject *getMapHasBeenLoaded(PyObject *self, PyObject *args) {
484  char *name;
485 
486  if (!PyArg_ParseTuple(args, "s", &name))
487  return NULL;
489 }
490 
491 static PyObject *findFace(PyObject *self, PyObject *args) {
492  char *name;
493 
494  if (!PyArg_ParseTuple(args, "s", &name))
495  return NULL;
496  return Py_BuildValue("i", cf_find_face(name, 0));
497 }
498 
499 static PyObject *log_message(PyObject *self, PyObject *args) {
500  LogLevel level;
501  int intLevel;
502  char *message;
503 
504  if (!PyArg_ParseTuple(args, "is", &intLevel, &message))
505  return NULL;
506 
507  switch (intLevel) {
508  case llevError:
509  level = llevError;
510  break;
511 
512  case llevInfo:
513  level = llevInfo;
514  break;
515 
516  case llevDebug:
517  level = llevDebug;
518  break;
519 
520  case llevMonster:
521  level = llevMonster;
522  break;
523 
524  default:
525  return NULL;
526  }
527  if ((message != NULL) && (message[strlen(message)] == '\n'))
528  cf_log(level, "CFPython: %s", message);
529  else
530  cf_log(level, "CFPython: %s\n", message);
531  Py_INCREF(Py_None);
532  return Py_None;
533 }
534 
535 static PyObject *findAnimation(PyObject *self, PyObject *args) {
536  char *name;
537 
538  if (!PyArg_ParseTuple(args, "s", &name))
539  return NULL;
540  return Py_BuildValue("i", cf_find_animation(name));
541 }
542 
543 static PyObject *getSeasonName(PyObject *self, PyObject *args) {
544  int i;
545 
546  if (!PyArg_ParseTuple(args, "i", &i))
547  return NULL;
548  return Py_BuildValue("s", cf_get_season_name(i));
549 }
550 
551 static PyObject *getMonthName(PyObject *self, PyObject *args) {
552  int i;
553 
554  if (!PyArg_ParseTuple(args, "i", &i))
555  return NULL;
556  return Py_BuildValue("s", cf_get_month_name(i));
557 }
558 
559 static PyObject *getWeekdayName(PyObject *self, PyObject *args) {
560  int i;
561 
562  if (!PyArg_ParseTuple(args, "i", &i))
563  return NULL;
564  return Py_BuildValue("s", cf_get_weekday_name(i));
565 }
566 
567 static PyObject *getPeriodofdayName(PyObject *self, PyObject *args) {
568  int i;
569 
570  if (!PyArg_ParseTuple(args, "i", &i))
571  return NULL;
572  return Py_BuildValue("s", cf_get_periodofday_name(i));
573 }
574 
575 static PyObject *addReply(PyObject *self, PyObject *args) {
576  char *word, *reply;
577  talk_info *talk;
578 
579  if (current_context->talk == NULL) {
580  set_exception("not in a dialog context");
581  return NULL;
582  }
583  talk = current_context->talk;
584 
585  if (!PyArg_ParseTuple(args, "ss", &word, &reply)) {
586  return NULL;
587  }
588 
589  if (talk->replies_count == MAX_REPLIES) {
590  set_exception("too many replies");
591  return NULL;
592  }
593 
594  talk->replies_words[talk->replies_count] = cf_add_string(word);
595  talk->replies[talk->replies_count] = cf_add_string(reply);
596  talk->replies_count++;
597  Py_INCREF(Py_None);
598  return Py_None;
599 
600 }
601 
602 static PyObject *setPlayerMessage(PyObject *self, PyObject *args) {
603  char *message;
604  int type = rt_reply;
605 
606  if (current_context->talk == NULL) {
607  set_exception("not in a dialog context");
608  return NULL;
609  }
610 
611  if (!PyArg_ParseTuple(args, "s|i", &message, &type)) {
612  return NULL;
613  }
614 
615  if (current_context->talk->message != NULL)
616  cf_free_string(current_context->talk->message);
617  current_context->talk->message = cf_add_string(message);
618  current_context->talk->message_type = type;
619 
620  Py_INCREF(Py_None);
621  return Py_None;
622 }
623 
624 static PyObject *npcSay(PyObject *self, PyObject *args) {
625  Crossfire_Object *npc = NULL;
626  char *message, buf[2048];
627 
628  if (!PyArg_ParseTuple(args, "O!s", &Crossfire_ObjectType, &npc, &message))
629  return NULL;
630 
631  if (current_context->talk == NULL) {
632  set_exception("not in a dialog context");
633  return NULL;
634  }
635 
636  if (current_context->talk->npc_msg_count == MAX_NPC) {
637  set_exception("too many NPCs");
638  return NULL;
639  }
640 
641  if (strlen(message) >= sizeof(buf) - 1)
642  cf_log(llevError, "warning, too long message in npcSay, will be truncated");
644  snprintf(buf, sizeof(buf), "%s says: %s", npc->obj->name, message);
645 
646  current_context->talk->npc_msgs[current_context->talk->npc_msg_count] = cf_add_string(buf);
647  current_context->talk->npc_msg_count++;
648 
649  Py_INCREF(Py_None);
650  return Py_None;
651 }
652 
653 static PyObject *costStringFromValue(PyObject *self, PyObject *args) {
654  uint64_t value;
655  char buf[2048];
656  int largest_coin = 0;
657 
658  if (!PyArg_ParseTuple(args, "L|i", &value, &largest_coin))
659  return NULL;
660 
661  cf_cost_string_from_value(value, largest_coin, buf, sizeof(buf));
662  return Py_BuildValue("s", buf);
663 }
664 
665 static PyMethodDef CFPythonMethods[] = {
666  { "WhoAmI", getWhoAmI, METH_NOARGS, NULL },
667  { "WhoIsActivator", getWhoIsActivator, METH_NOARGS, NULL },
668  { "WhoIsOther", getWhoIsThird, METH_NOARGS, NULL },
669  { "WhatIsMessage", getWhatIsMessage, METH_NOARGS, NULL },
670  { "ScriptName", getScriptName, METH_NOARGS, NULL },
671  { "ScriptParameters", getScriptParameters, METH_NOARGS, NULL },
672  { "WhatIsEvent", getEvent, METH_NOARGS, NULL },
673  { "MapDirectory", getMapDirectory, METH_NOARGS, NULL },
674  { "UniqueDirectory", getUniqueDirectory, METH_NOARGS, NULL },
675  { "TempDirectory", getTempDirectory, METH_NOARGS, NULL },
676  { "ConfigDirectory", getConfigDirectory, METH_NOARGS, NULL },
677  { "LocalDirectory", getLocalDirectory, METH_NOARGS, NULL },
678  { "PlayerDirectory", getPlayerDirectory, METH_NOARGS, NULL },
679  { "DataDirectory", getDataDirectory, METH_NOARGS, NULL },
680  { "ReadyMap", readyMap, METH_VARARGS, NULL },
681  { "CreateMap", createMap, METH_VARARGS, NULL },
682  { "FindPlayer", findPlayer, METH_VARARGS, NULL },
683  { "MatchString", matchString, METH_VARARGS, NULL },
684  { "GetReturnValue", getReturnValue, METH_NOARGS, NULL },
685  { "SetReturnValue", setReturnValue, METH_VARARGS, NULL },
686  { "PluginVersion", getCFPythonVersion, METH_NOARGS, NULL },
687  { "CreateObject", createCFObject, METH_NOARGS, NULL },
688  { "CreateObjectByName", createCFObjectByName, METH_VARARGS, NULL },
689  { "GetPrivateDictionary", getPrivateDictionary, METH_NOARGS, NULL },
690  { "GetSharedDictionary", getSharedDictionary, METH_NOARGS, NULL },
691  { "GetPlayers", getPlayers, METH_NOARGS, NULL },
692  { "GetArchetypes", getArchetypes, METH_NOARGS, NULL },
693  { "GetMaps", getMaps, METH_NOARGS, NULL },
694  { "GetParties", getParties, METH_NOARGS, NULL },
695  { "GetRegions", getRegions, METH_NOARGS, NULL },
696  { "GetFriendlyList", getFriendlyList, METH_NOARGS, NULL },
697  { "RegisterCommand", registerCommand, METH_VARARGS, NULL },
698  { "RegisterGlobalEvent", registerGEvent, METH_VARARGS, NULL },
699  { "UnregisterGlobalEvent", unregisterGEvent, METH_VARARGS, NULL },
700  { "GetTime", getTime, METH_NOARGS, NULL },
701  { "DestroyTimer", destroyTimer, METH_VARARGS, NULL },
702  { "MapHasBeenLoaded", getMapHasBeenLoaded, METH_VARARGS, NULL },
703  { "Log", log_message, METH_VARARGS, NULL },
704  { "FindFace", findFace, METH_VARARGS, NULL },
705  { "FindAnimation", findAnimation, METH_VARARGS, NULL },
706  { "GetSeasonName", getSeasonName, METH_VARARGS, NULL },
707  { "GetMonthName", getMonthName, METH_VARARGS, NULL },
708  { "GetWeekdayName", getWeekdayName, METH_VARARGS, NULL },
709  { "GetPeriodofdayName", getPeriodofdayName, METH_VARARGS, NULL },
710  { "AddReply", addReply, METH_VARARGS, NULL },
711  { "SetPlayerMessage", setPlayerMessage, METH_VARARGS, NULL },
712  { "NPCSay", npcSay, METH_VARARGS, NULL },
713  { "CostStringFromValue", costStringFromValue, METH_VARARGS, NULL },
714  { NULL, NULL, 0, NULL }
715 };
716 
717 static void initContextStack(void) {
718  current_context = NULL;
719  context_stack = NULL;
720 }
721 
722 static void pushContext(CFPContext *context) {
723  if (current_context == NULL) {
724  context_stack = context;
725  context->down = NULL;
726  } else {
727  context->down = current_context;
728  }
729  current_context = context;
730 }
731 
732 static CFPContext *popContext(void) {
733  CFPContext *oldcontext;
734 
735  if (current_context != NULL) {
736  oldcontext = current_context;
737  current_context = current_context->down;
738  return oldcontext;
739  }
740  else
741  return NULL;
742 }
743 
744 static void freeContext(CFPContext *context) {
745  Py_XDECREF(context->event);
746  Py_XDECREF(context->third);
747  Py_XDECREF(context->who);
748  Py_XDECREF(context->activator);
749  free(context);
750 }
751 
755 static PyObject* cfpython_openpyfile(char *filename) {
756  PyObject *scriptfile;
757 #ifdef IS_PY3K
758  {
759  int fd;
760  fd = open(filename, O_RDONLY);
761  if (fd == -1)
762  return NULL;
763  scriptfile = PyFile_FromFd(fd, filename, "r", -1, NULL, NULL, NULL, 1);
764  }
765 #else
766  if (!(scriptfile = PyFile_FromString(filename, "r"))) {
767  return NULL;
768  }
769 #endif
770  return scriptfile;
771 }
772 
777 static FILE* cfpython_pyfile_asfile(PyObject* obj) {
778 #ifdef IS_PY3K
779  return fdopen(PyObject_AsFileDescriptor(obj), "r");
780 #else
781  return PyFile_AsFile(obj);
782 #endif
783 }
784 
789 static PyObject *catcher = NULL;
790 
797 static void log_python_error(void) {
798 
799  PyErr_Print();
800 
801  if (catcher != NULL) {
802  PyObject *output = PyObject_GetAttrString(catcher, "value"); //get the stdout and stderr from our catchOutErr object
803  PyObject* empty = PyString_FromString("");
804 
805  cf_log_plain(llevError, PyString_AsString(output));
806  Py_DECREF(output);
807 
808  PyObject_SetAttrString(catcher, "value", empty);
809  Py_DECREF(empty);
810  }
811 
812  return;
813 }
814 
815 
817 static PyCodeObject *compilePython(char *filename) {
818  PyObject *scriptfile = NULL;
819  sstring sh_path;
820  struct stat stat_buf;
821  struct _node *n;
822  int i;
823  pycode_cache_entry *replace = NULL, *run = NULL;
824 
825  if (stat(filename, &stat_buf)) {
826  cf_log(llevDebug, "cfpython - The Script file %s can't be stat:ed\n", filename);
827  return NULL;
828  }
829 
830  sh_path = cf_add_string(filename);
831 
832  /* Search through cache. Three cases:
833  * 1) script in cache, but older than file -> replace cached
834  * 2) script in cache and up to date -> use cached
835  * 3) script not in cache, cache not full -> add to end of cache
836  * 4) script not in cache, cache full -> replace least recently used
837  */
838  for (i = 0; i < PYTHON_CACHE_SIZE; i++) {
839  if (pycode_cache[i].file == NULL) { /* script not in cache, cache not full */
840  replace = &pycode_cache[i]; /* add to end of cache */
841  break;
842  } else if (pycode_cache[i].file == sh_path) {
843  /* script in cache */
844  if (pycode_cache[i].code == NULL || (pycode_cache[i].cached_time < stat_buf.st_mtime)) {
845  /* cache older than file, replace cached */
846  replace = &pycode_cache[i];
847  } else {
848  /* cache uptodate, use cached*/
849  replace = NULL;
850  run = &pycode_cache[i];
851  }
852  break;
853  } else if (replace == NULL || pycode_cache[i].used_time < replace->used_time)
854  /* if we haven't found it yet, set replace to the oldest cache */
855  replace = &pycode_cache[i];
856  }
857 
858  /* replace a specific cache index with the file */
859  if (replace) {
860  Py_XDECREF(replace->code); /* safe to call on NULL */
861  replace->code = NULL;
862 
863  /* Need to replace path string? */
864  if (replace->file != sh_path) {
865  if (replace->file) {
866  cf_free_string(replace->file);
867  }
868  replace->file = cf_add_string(sh_path);
869  }
870 
871  /* Load, parse and compile. Note: because Pyhon may have been built with a
872  * different library than Crossfire, the FILE* it uses may be incompatible.
873  * Therefore we use PyFile to open the file, then convert to FILE* and get
874  * Python's own structure. Messy, but can't be helped... */
875  if (!(scriptfile = cfpython_openpyfile(filename))) {
876  cf_log(llevDebug, "cfpython - The Script file %s can't be opened\n", filename);
877  cf_free_string(sh_path);
878  return NULL;
879  } else {
880  /* Note: FILE* being opaque, it works, but the actual structure may be different! */
881  FILE* pyfile = cfpython_pyfile_asfile(scriptfile);
882  if ((n = PyParser_SimpleParseFile(pyfile, filename, Py_file_input))) {
883  replace->code = PyNode_Compile(n, filename);
884  PyNode_Free(n);
885  }
886 
887  if (PyErr_Occurred())
889  else
890  replace->cached_time = stat_buf.st_mtime;
891  run = replace;
892  }
893  }
894 
895  cf_free_string(sh_path);
896 
897  if (scriptfile) {
898  Py_DECREF(scriptfile);
899  }
900 
901  assert(run != NULL);
902  run->used_time = time(NULL);
903  return run->code;
904 }
905 
906 static int do_script(CFPContext *context, int silent) {
907  PyCodeObject *pycode;
908  PyObject *dict;
909  PyObject *ret;
910 #if 0
911  PyObject *list;
912  int item;
913 #endif
914 
915  pycode = compilePython(context->script);
916  if (pycode) {
917  pushContext(context);
918  dict = PyDict_New();
919  PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins());
920  ret = PyEval_EvalCode(pycode, dict, NULL);
921  if (PyErr_Occurred()) {
923  }
924  Py_XDECREF(ret);
925 #if 0
926  printf("cfpython - %d items in heap\n", PyDict_Size(dict));
927  list = PyDict_Values(dict);
928  for (item = PyList_Size(list)-1; item >= 0; item--) {
929  dict = PyList_GET_ITEM(list, item);
930  ret = PyObject_Str(dict);
931  printf(" ref %s = %d\n", PyString_AsString(ret), dict->ob_refcnt);
932  Py_XDECREF(ret);
933  }
934  Py_DECREF(list);
935 #endif
936  Py_DECREF(dict);
937  return 1;
938  } else
939  return 0;
940 }
941 
942 typedef struct {
943  const char *name;
944  const int value;
945 } CFConstant;
946 
947 static void addConstants(PyObject *module, const char *name, const CFConstant *constants) {
948  int i = 0;
949  char tmp[1024];
950  PyObject *new;
951  PyObject *dict;
952 
953  snprintf(tmp, sizeof(tmp), "Crossfire_%s", name);
954 
955  new = PyModule_New(tmp);
956  dict = PyDict_New();
957 
958  while (constants[i].name != NULL) {
959  PyModule_AddIntConstant(new, (char *)constants[i].name, constants[i].value);
960 #ifdef IS_PY3K
961  PyDict_SetItem(dict, PyLong_FromLong(constants[i].value), PyUnicode_FromString(constants[i].name));
962 #else
963  PyDict_SetItem(dict, PyLong_FromLong(constants[i].value), PyString_FromString(constants[i].name));
964 #endif
965  i++;
966  }
967  PyDict_SetItemString(PyModule_GetDict(module), name, new);
968 
969  snprintf(tmp, sizeof(tmp), "%sName", name);
970  PyDict_SetItemString(PyModule_GetDict(module), tmp, dict);
971  Py_DECREF(dict);
972 }
979 static void addSimpleConstants(PyObject *module, const char *name, const CFConstant *constants) {
980  int i = 0;
981  char tmp[1024];
982  PyObject *new;
983 
984  snprintf(tmp, sizeof(tmp), "Crossfire_%s", name);
985 
986  new = PyModule_New(tmp);
987 
988  while (constants[i].name != NULL) {
989  PyModule_AddIntConstant(new, (char *)constants[i].name, constants[i].value);
990  i++;
991  }
992  PyDict_SetItemString(PyModule_GetDict(module), name, new);
993 }
994 
995 static void initConstants(PyObject *module) {
996  static const CFConstant cstDirection[] = {
997  { "NORTH", 1 },
998  { "NORTHEAST", 2 },
999  { "EAST", 3 },
1000  { "SOUTHEAST", 4 },
1001  { "SOUTH", 5 },
1002  { "SOUTHWEST", 6 },
1003  { "WEST", 7 },
1004  { "NORTHWEST", 8 },
1005  { NULL, 0 }
1006  };
1007 
1008  static const CFConstant cstType[] = {
1009  { "PLAYER", PLAYER },
1010  { "TRANSPORT", TRANSPORT },
1011  { "ROD", ROD },
1012  { "TREASURE", TREASURE },
1013  { "POTION", POTION },
1014  { "FOOD", FOOD },
1015  { "POISON", POISON },
1016  { "BOOK", BOOK },
1017  { "CLOCK", CLOCK },
1018  { "ARROW", ARROW },
1019  { "BOW", BOW },
1020  { "WEAPON", WEAPON },
1021  { "ARMOUR", ARMOUR },
1022  { "PEDESTAL", PEDESTAL },
1023  { "ALTAR", ALTAR },
1024  { "LOCKED_DOOR", LOCKED_DOOR },
1025  { "SPECIAL_KEY", SPECIAL_KEY },
1026  { "MAP", MAP },
1027  { "DOOR", DOOR },
1028  { "KEY", KEY },
1029  { "TIMED_GATE", TIMED_GATE },
1030  { "TRIGGER", TRIGGER },
1031  { "GRIMREAPER", GRIMREAPER },
1032  { "MAGIC_EAR", MAGIC_EAR },
1033  { "TRIGGER_BUTTON", TRIGGER_BUTTON },
1034  { "TRIGGER_ALTAR", TRIGGER_ALTAR },
1035  { "TRIGGER_PEDESTAL", TRIGGER_PEDESTAL },
1036  { "SHIELD", SHIELD },
1037  { "HELMET", HELMET },
1038  { "MONEY", MONEY },
1039  { "CLASS", CLASS },
1040  { "AMULET", AMULET },
1041  { "PLAYERMOVER", PLAYERMOVER },
1042  { "TELEPORTER", TELEPORTER },
1043  { "CREATOR", CREATOR },
1044  { "SKILL", SKILL },
1045  { "EARTHWALL", EARTHWALL },
1046  { "GOLEM", GOLEM },
1047  { "THROWN_OBJ", THROWN_OBJ },
1048  { "BLINDNESS", BLINDNESS },
1049  { "GOD", GOD },
1050  { "DETECTOR", DETECTOR },
1051  { "TRIGGER_MARKER", TRIGGER_MARKER },
1052  { "DEAD_OBJECT", DEAD_OBJECT },
1053  { "DRINK", DRINK },
1054  { "MARKER", MARKER },
1055  { "HOLY_ALTAR", HOLY_ALTAR },
1056  { "PLAYER_CHANGER", PLAYER_CHANGER },
1057  { "BATTLEGROUND", BATTLEGROUND },
1058  { "PEACEMAKER", PEACEMAKER },
1059  { "GEM", GEM },
1060  { "FIREWALL", FIREWALL },
1061  { "CHECK_INV", CHECK_INV },
1062  { "MOOD_FLOOR", MOOD_FLOOR },
1063  { "EXIT", EXIT },
1064  { "ENCOUNTER", ENCOUNTER },
1065  { "SHOP_FLOOR", SHOP_FLOOR },
1066  { "SHOP_MAT", SHOP_MAT },
1067  { "RING", RING },
1068  { "FLOOR", FLOOR },
1069  { "FLESH", FLESH },
1070  { "INORGANIC", INORGANIC },
1071  { "SKILL_TOOL", SKILL_TOOL },
1072  { "LIGHTER", LIGHTER },
1073  { "WALL", WALL },
1074  { "MISC_OBJECT", MISC_OBJECT },
1075  { "MONSTER", MONSTER },
1076  { "LAMP", LAMP },
1077  { "DUPLICATOR", DUPLICATOR },
1078  { "SPELLBOOK", SPELLBOOK },
1079  { "CLOAK", CLOAK },
1080  { "SPINNER", SPINNER },
1081  { "GATE", GATE },
1082  { "BUTTON", BUTTON },
1083  { "CF_HANDLE", CF_HANDLE },
1084  { "HOLE", HOLE },
1085  { "TRAPDOOR", TRAPDOOR },
1086  { "SIGN", SIGN },
1087  { "BOOTS", BOOTS },
1088  { "GLOVES", GLOVES },
1089  { "SPELL", SPELL },
1090  { "SPELL_EFFECT", SPELL_EFFECT },
1091  { "CONVERTER", CONVERTER },
1092  { "BRACERS", BRACERS },
1093  { "POISONING", POISONING },
1094  { "SAVEBED", SAVEBED },
1095  { "WAND", WAND },
1096  { "SCROLL", SCROLL },
1097  { "DIRECTOR", DIRECTOR },
1098  { "GIRDLE", GIRDLE },
1099  { "FORCE", FORCE },
1100  { "POTION_RESIST_EFFECT", POTION_RESIST_EFFECT },
1101  { "EVENT_CONNECTOR", EVENT_CONNECTOR },
1102  { "CLOSE_CON", CLOSE_CON },
1103  { "CONTAINER", CONTAINER },
1104  { "ARMOUR_IMPROVER", ARMOUR_IMPROVER },
1105  { "WEAPON_IMPROVER", WEAPON_IMPROVER },
1106  { "SKILLSCROLL", SKILLSCROLL },
1107  { "DEEP_SWAMP", DEEP_SWAMP },
1108  { "IDENTIFY_ALTAR", IDENTIFY_ALTAR },
1109  { "SHOP_INVENTORY", SHOP_INVENTORY },
1110  { "RUNE", RUNE },
1111  { "TRAP", TRAP },
1112  { "POWER_CRYSTAL", POWER_CRYSTAL },
1113  { "CORPSE", CORPSE },
1114  { "DISEASE", DISEASE },
1115  { "SYMPTOM", SYMPTOM },
1116  { "BUILDER", BUILDER },
1117  { "MATERIAL", MATERIAL },
1118  { NULL, 0 }
1119  };
1120 
1121  static const CFConstant cstMove[] = {
1122  { "WALK", MOVE_WALK },
1123  { "FLY_LOW", MOVE_FLY_LOW },
1124  { "FLY_HIGH", MOVE_FLY_HIGH },
1125  { "FLYING", MOVE_FLYING },
1126  { "SWIM", MOVE_SWIM },
1127  { "BOAT", MOVE_BOAT },
1128  { "ALL", MOVE_ALL },
1129  { NULL, 0 }
1130  };
1131 
1132  static const CFConstant cstMessageFlag[] = {
1133  { "NDI_BLACK", NDI_BLACK },
1134  { "NDI_WHITE", NDI_WHITE },
1135  { "NDI_NAVY", NDI_NAVY },
1136  { "NDI_RED", NDI_RED },
1137  { "NDI_ORANGE", NDI_ORANGE },
1138  { "NDI_BLUE", NDI_BLUE },
1139  { "NDI_DK_ORANGE", NDI_DK_ORANGE },
1140  { "NDI_GREEN", NDI_GREEN },
1141  { "NDI_LT_GREEN", NDI_LT_GREEN },
1142  { "NDI_GREY", NDI_GREY },
1143  { "NDI_BROWN", NDI_BROWN },
1144  { "NDI_GOLD", NDI_GOLD },
1145  { "NDI_TAN", NDI_TAN },
1146  { "NDI_UNIQUE", NDI_UNIQUE },
1147  { "NDI_ALL", NDI_ALL },
1148  { "NDI_ALL_DMS", NDI_ALL_DMS },
1149  { NULL, 0 }
1150  };
1151 
1152  static const CFConstant cstAttackType[] = {
1153  { "PHYSICAL", AT_PHYSICAL },
1154  { "MAGIC", AT_MAGIC },
1155  { "FIRE", AT_FIRE },
1156  { "ELECTRICITY", AT_ELECTRICITY },
1157  { "COLD", AT_COLD },
1158  { "CONFUSION", AT_CONFUSION },
1159  { "ACID", AT_ACID },
1160  { "DRAIN", AT_DRAIN },
1161  { "WEAPONMAGIC", AT_WEAPONMAGIC },
1162  { "GHOSTHIT", AT_GHOSTHIT },
1163  { "POISON", AT_POISON },
1164  { "SLOW", AT_SLOW },
1165  { "PARALYZE", AT_PARALYZE },
1166  { "TURN_UNDEAD", AT_TURN_UNDEAD },
1167  { "FEAR", AT_FEAR },
1168  { "CANCELLATION", AT_CANCELLATION },
1169  { "DEPLETE", AT_DEPLETE },
1170  { "DEATH", AT_DEATH },
1171  { "CHAOS", AT_CHAOS },
1172  { "COUNTERSPELL", AT_COUNTERSPELL },
1173  { "GODPOWER", AT_GODPOWER },
1174  { "HOLYWORD", AT_HOLYWORD },
1175  { "BLIND", AT_BLIND },
1176  { "INTERNAL", AT_INTERNAL },
1177  { "LIFE_STEALING", AT_LIFE_STEALING },
1178  { "DISEASE", AT_DISEASE },
1179  { NULL, 0 }
1180  };
1181 
1182  static const CFConstant cstAttackTypeNumber[] = {
1183  { "PHYSICAL", ATNR_PHYSICAL },
1184  { "MAGIC", ATNR_MAGIC },
1185  { "FIRE", ATNR_FIRE },
1186  { "ELECTRICITY", ATNR_ELECTRICITY },
1187  { "COLD", ATNR_COLD },
1188  { "CONFUSION", ATNR_CONFUSION },
1189  { "ACID", ATNR_ACID },
1190  { "DRAIN", ATNR_DRAIN },
1191  { "WEAPONMAGIC", ATNR_WEAPONMAGIC },
1192  { "GHOSTHIT", ATNR_GHOSTHIT },
1193  { "POISON", ATNR_POISON },
1194  { "SLOW", ATNR_SLOW },
1195  { "PARALYZE", ATNR_PARALYZE },
1196  { "TURN_UNDEAD", ATNR_TURN_UNDEAD },
1197  { "FEAR", ATNR_FEAR },
1198  { "CANCELLATION", ATNR_CANCELLATION },
1199  { "DEPLETE", ATNR_DEPLETE },
1200  { "DEATH", ATNR_DEATH },
1201  { "CHAOS", ATNR_CHAOS },
1202  { "COUNTERSPELL", ATNR_COUNTERSPELL },
1203  { "GODPOWER", ATNR_GODPOWER },
1204  { "HOLYWORD", ATNR_HOLYWORD },
1205  { "BLIND", ATNR_BLIND },
1206  { "INTERNAL", ATNR_INTERNAL },
1207  { "LIFE_STEALING", ATNR_LIFE_STEALING },
1208  { "DISEASE", ATNR_DISEASE },
1209  { NULL, 0 }
1210  };
1211 
1212  static const CFConstant cstEventType[] = {
1213  { "APPLY", EVENT_APPLY },
1214  { "ATTACK", EVENT_ATTACKED },
1215  { "ATTACKS", EVENT_ATTACKS },
1216  { "DEATH", EVENT_DEATH },
1217  { "DROP", EVENT_DROP },
1218  { "PICKUP", EVENT_PICKUP },
1219  { "SAY", EVENT_SAY },
1220  { "STOP", EVENT_STOP },
1221  { "TIME", EVENT_TIME },
1222  { "THROW", EVENT_THROW },
1223  { "TRIGGER", EVENT_TRIGGER },
1224  { "CLOSE", EVENT_CLOSE },
1225  { "TIMER", EVENT_TIMER },
1226  { "DESTROY", EVENT_DESTROY },
1227  { "BORN", EVENT_BORN },
1228  { "CLOCK", EVENT_CLOCK },
1229  { "CRASH", EVENT_CRASH },
1230  { "PLAYER_DEATH", EVENT_PLAYER_DEATH },
1231  { "GKILL", EVENT_GKILL },
1232  { "LOGIN", EVENT_LOGIN },
1233  { "LOGOUT", EVENT_LOGOUT },
1234  { "MAPENTER", EVENT_MAPENTER },
1235  { "MAPLEAVE", EVENT_MAPLEAVE },
1236  { "MAPRESET", EVENT_MAPRESET },
1237  { "REMOVE", EVENT_REMOVE },
1238  { "SHOUT", EVENT_SHOUT },
1239  { "TELL", EVENT_TELL },
1240  { "MUZZLE", EVENT_MUZZLE },
1241  { "KICK", EVENT_KICK },
1242  { "MAPUNLOAD", EVENT_MAPUNLOAD },
1243  { "MAPLOAD", EVENT_MAPLOAD },
1244  { "USER", EVENT_USER },
1245  { NULL, 0 }
1246  };
1247 
1248  static const CFConstant cstTime[] = {
1249  { "HOURS_PER_DAY", HOURS_PER_DAY },
1250  { "DAYS_PER_WEEK", DAYS_PER_WEEK },
1251  { "WEEKS_PER_MONTH", WEEKS_PER_MONTH },
1252  { "MONTHS_PER_YEAR", MONTHS_PER_YEAR },
1253  { "SEASONS_PER_YEAR", SEASONS_PER_YEAR },
1254  { "PERIODS_PER_DAY", PERIODS_PER_DAY },
1255  { NULL, 0 }
1256  };
1257 
1258  static const CFConstant cstReplyTypes[] = {
1259  { "SAY", rt_say },
1260  { "REPLY", rt_reply },
1261  { "QUESTION", rt_question },
1262  { NULL, 0 }
1263  };
1264 
1265  static const CFConstant cstAttackMovement[] = {
1266  { "DISTATT", DISTATT },
1267  { "RUNATT", RUNATT },
1268  { "HITRUN", HITRUN },
1269  { "WAITATT", WAITATT },
1270  { "RUSH", RUSH },
1271  { "ALLRUN", ALLRUN },
1272  { "DISTHIT", DISTHIT },
1273  { "WAIT2", WAIT2 },
1274  { "PETMOVE", PETMOVE },
1275  { "CIRCLE1", CIRCLE1 },
1276  { "CIRCLE2", CIRCLE2 },
1277  { "PACEH", PACEH },
1278  { "PACEH2", PACEH2 },
1279  { "RANDO", RANDO },
1280  { "RANDO2", RANDO2 },
1281  { "PACEV", PACEV },
1282  { "PACEV2", PACEV2 },
1283  { NULL, 0 }
1284  };
1285 
1286  addConstants(module, "Direction", cstDirection);
1287  addConstants(module, "Type", cstType);
1288  addConstants(module, "Move", cstMove);
1289  addConstants(module, "MessageFlag", cstMessageFlag);
1290  addConstants(module, "AttackType", cstAttackType);
1291  addConstants(module, "AttackTypeNumber", cstAttackTypeNumber);
1292  addConstants(module, "EventType", cstEventType);
1293  addSimpleConstants(module, "Time", cstTime);
1294  addSimpleConstants(module, "ReplyType", cstReplyTypes);
1295  addSimpleConstants(module, "AttackMovement", cstAttackMovement);
1296 }
1297 
1298 /*
1299  * Set up the main module and handle misc plugin loading stuff and such.
1300  */
1301 
1306 static void cfpython_init_types(PyObject* m) {
1307  PyObject *d = PyModule_GetDict(m);
1308 
1309  Crossfire_ObjectType.tp_new = PyType_GenericNew;
1310  Crossfire_MapType.tp_new = PyType_GenericNew;
1311  Crossfire_PlayerType.tp_new = PyType_GenericNew;
1312  Crossfire_ArchetypeType.tp_new = PyType_GenericNew;
1313  Crossfire_PartyType.tp_new = PyType_GenericNew;
1314  Crossfire_RegionType.tp_new = PyType_GenericNew;
1315  PyType_Ready(&Crossfire_ObjectType);
1316  PyType_Ready(&Crossfire_MapType);
1317  PyType_Ready(&Crossfire_PlayerType);
1318  PyType_Ready(&Crossfire_ArchetypeType);
1319  PyType_Ready(&Crossfire_PartyType);
1320  PyType_Ready(&Crossfire_RegionType);
1321 
1322  Py_INCREF(&Crossfire_ObjectType);
1323  Py_INCREF(&Crossfire_MapType);
1324  Py_INCREF(&Crossfire_PlayerType);
1325  Py_INCREF(&Crossfire_ArchetypeType);
1326  Py_INCREF(&Crossfire_PartyType);
1327  Py_INCREF(&Crossfire_RegionType);
1328 
1329  PyModule_AddObject(m, "Object", (PyObject *)&Crossfire_ObjectType);
1330  PyModule_AddObject(m, "Map", (PyObject *)&Crossfire_MapType);
1331  PyModule_AddObject(m, "Player", (PyObject *)&Crossfire_PlayerType);
1332  PyModule_AddObject(m, "Archetype", (PyObject *)&Crossfire_ArchetypeType);
1333  PyModule_AddObject(m, "Party", (PyObject *)&Crossfire_PartyType);
1334  PyModule_AddObject(m, "Region", (PyObject *)&Crossfire_RegionType);
1335 
1336  PyModule_AddObject(m, "LogError", Py_BuildValue("i", llevError));
1337  PyModule_AddObject(m, "LogInfo", Py_BuildValue("i", llevInfo));
1338  PyModule_AddObject(m, "LogDebug", Py_BuildValue("i", llevDebug));
1339  PyModule_AddObject(m, "LogMonster", Py_BuildValue("i", llevMonster));
1340 
1341  CFPythonError = PyErr_NewException("Crossfire.error", NULL, NULL);
1342  PyDict_SetItemString(d, "error", CFPythonError);
1343 }
1344 
1345 #ifdef IS_PY3K
1346 extern PyObject* PyInit_cjson(void);
1347 #else
1348 extern PyMODINIT_FUNC initcjson(void);
1349 #endif
1350 
1351 #ifdef IS_PY3K
1352 static PyModuleDef CrossfireModule = {
1353  PyModuleDef_HEAD_INIT,
1354  "Crossfire", /* m_name */
1355  NULL, /* m_doc */
1356  -1, /* m_size */
1357  CFPythonMethods, /* m_methods */
1358  NULL, /* m_reload */
1359  NULL, /* m_traverse */
1360  NULL, /* m_clear */
1361  NULL /* m_free */
1362 };
1363 
1364 static PyObject* PyInit_Crossfire(void)
1365 {
1366  PyObject *m = PyModule_Create(&CrossfireModule);
1367  Py_INCREF(m);
1368  return m;
1369 }
1370 #endif
1371 
1372 CF_PLUGIN int initPlugin(const char *iversion, f_plug_api gethooksptr) {
1373  PyObject *m;
1374  int i;
1375  /* Python code to redirect stdouts/stderr. */
1376  const char *stdOutErr =
1377 "import sys\n\
1378 class CatchOutErr:\n\
1379  def __init__(self):\n\
1380  self.value = ''\n\
1381  def write(self, txt):\n\
1382  self.value += txt\n\
1383 catchOutErr = CatchOutErr()\n\
1384 sys.stdout = catchOutErr\n\
1385 sys.stderr = catchOutErr\n\
1386 ";
1387 
1388  cf_init_plugin(gethooksptr);
1389  cf_log(llevDebug, "CFPython 2.0a init\n");
1390 
1393 
1394 #ifdef IS_PY26
1395  Py_Py3kWarningFlag++;
1396 #endif
1397 
1398 #ifdef IS_PY3K
1399  PyImport_AppendInittab("Crossfire", &PyInit_Crossfire);
1400  PyImport_AppendInittab("cjson", &PyInit_cjson);
1401 #endif
1402 
1403  Py_Initialize();
1404 
1405 #ifdef IS_PY3K
1406  m = PyImport_ImportModule("Crossfire");
1407 #else
1408  m = Py_InitModule("Crossfire", CFPythonMethods);
1409 #endif
1410 
1412 
1413  for (i = 0; i < NR_CUSTOM_CMD; i++) {
1414  CustomCommand[i].name = NULL;
1415  CustomCommand[i].script = NULL;
1416  CustomCommand[i].speed = 0.0;
1417  }
1418  initConstants(m);
1419  private_data = PyDict_New();
1420  shared_data = PyDict_New();
1421 
1422  /* Redirect Python's stderr to a special object so it can be put to
1423  * the Crossfire log. */
1424  m = PyImport_AddModule("__main__");
1425  PyRun_SimpleString(stdOutErr);
1426  catcher = PyObject_GetAttrString(m, "catchOutErr");
1427 
1428 #ifndef IS_PY3K
1429  /* add cjson module*/
1430  initcjson();
1431 #endif
1432  return 0;
1433 }
1434 
1435 CF_PLUGIN void *getPluginProperty(int *type, ...) {
1436  va_list args;
1437  const char *propname;
1438  int i, size;
1439  command_array_struct *rtn_cmd;
1440  char *buf;
1441 
1442  va_start(args, type);
1443  propname = va_arg(args, const char *);
1444 
1445  if (!strcmp(propname, "command?")) {
1446  const char *cmdname;
1447  cmdname = va_arg(args, const char *);
1448  rtn_cmd = va_arg(args, command_array_struct *);
1449  va_end(args);
1450 
1451  for (i = 0; i < NR_CUSTOM_CMD; i++) {
1452  if (CustomCommand[i].name != NULL) {
1453  if (!strcmp(CustomCommand[i].name, cmdname)) {
1454  rtn_cmd->name = CustomCommand[i].name;
1455  rtn_cmd->time = (float)CustomCommand[i].speed;
1456  rtn_cmd->func = cfpython_runPluginCommand;
1457  current_command = i;
1458  return rtn_cmd;
1459  }
1460  }
1461  }
1462  return NULL;
1463  } else if (!strcmp(propname, "Identification")) {
1464  buf = va_arg(args, char *);
1465  size = va_arg(args, int);
1466  va_end(args);
1467  snprintf(buf, size, PLUGIN_NAME);
1468  return NULL;
1469  } else if (!strcmp(propname, "FullName")) {
1470  buf = va_arg(args, char *);
1471  size = va_arg(args, int);
1472  va_end(args);
1473  snprintf(buf, size, PLUGIN_VERSION);
1474  return NULL;
1475  }
1476  va_end(args);
1477  return NULL;
1478 }
1479 
1480 CF_PLUGIN void cfpython_runPluginCommand(object *op, const char *params) {
1481  char buf[1024], path[1024];
1482  CFPContext *context;
1483 
1484  if (current_command < 0) {
1485  cf_log(llevError, "Illegal call of cfpython_runPluginCommand, call find_plugin_command first.\n");
1486  return;
1487  }
1488  snprintf(buf, sizeof(buf), "%s.py", cf_get_maps_directory(CustomCommand[current_command].script, path, sizeof(path)));
1489 
1490  context = malloc(sizeof(CFPContext));
1491  context->message[0] = 0;
1492 
1493  context->who = Crossfire_Object_wrap(op);
1494  context->activator = NULL;
1495  context->third = NULL;
1496  context->fix = 0;
1497  snprintf(context->script, sizeof(context->script), "%s", buf);
1498  if (params)
1499  snprintf(context->options, sizeof(context->options), "%s", params);
1500  else
1501  context->options[0] = 0;
1502  context->returnvalue = 1; /* Default is "command successful" */
1503 
1504  current_command = -999;
1505  if (!do_script(context, 0)) {
1506  freeContext(context);
1507  return;
1508  }
1509 
1510  context = popContext();
1511  freeContext(context);
1512 /* printf("Execution complete"); */
1513 }
1514 
1516  PyObject *scriptfile;
1517  char path[1024];
1518  int i;
1519 
1520  cf_log(llevDebug, "CFPython 2.0a post init\n");
1521  initContextStack();
1524  /*registerGlobalEvent(NULL, EVENT_CRASH, PLUGIN_NAME, cfpython_globalEventListener);*/
1539 
1540  scriptfile = cfpython_openpyfile(cf_get_maps_directory("python/events/python_init.py", path, sizeof(path)));
1541  if (scriptfile != NULL) {
1542  FILE* pyfile = cfpython_pyfile_asfile(scriptfile);
1543  PyRun_SimpleFile(pyfile, cf_get_maps_directory("python/events/python_init.py", path, sizeof(path)));
1544  Py_DECREF(scriptfile);
1545  }
1546 
1547  for (i = 0; i < PYTHON_CACHE_SIZE; i++) {
1548  pycode_cache[i].code = NULL;
1549  pycode_cache[i].file = NULL;
1550  pycode_cache[i].cached_time = 0;
1551  pycode_cache[i].used_time = 0;
1552  }
1553 
1554  return 0;
1555 }
1556 
1558  va_list args;
1559  int rv = 0;
1560  CFPContext *context;
1561  char *buf;
1562  player *pl;
1563  object *op;
1564  context = malloc(sizeof(CFPContext));
1565 
1566  va_start(args, type);
1567  context->event_code = va_arg(args, int);
1568 
1569  context->message[0] = 0;
1570 
1571  context->who = NULL;
1572  context->activator = NULL;
1573  context->third = NULL;
1574  context->event = NULL;
1575  context->talk = NULL;
1576  rv = context->returnvalue = 0;
1577  cf_get_maps_directory("python/events/python_event.py", context->script, sizeof(context->script));
1578  strcpy(context->options, "");
1579  switch (context->event_code) {
1580  case EVENT_CRASH:
1581  cf_log(llevDebug, "Unimplemented for now\n");
1582  break;
1583 
1584  case EVENT_BORN:
1585  op = va_arg(args, object *);
1586  context->activator = Crossfire_Object_wrap(op);
1587  snprintf(context->options, sizeof(context->options), "born");
1588  break;
1589 
1590  case EVENT_PLAYER_DEATH:
1591  op = va_arg(args, object *);
1592  context->who = Crossfire_Object_wrap(op);
1593  op = va_arg(args, object *);
1594  context->activator = Crossfire_Object_wrap(op);
1595  snprintf(context->options, sizeof(context->options), "death");
1596  break;
1597 
1598  case EVENT_GKILL:
1599  op = va_arg(args, object *);
1600  object* hitter = va_arg(args, object *);
1601  context->who = Crossfire_Object_wrap(op);
1602  context->activator = Crossfire_Object_wrap(hitter);
1603  snprintf(context->options, sizeof(context->options), "gkill");
1604  break;
1605 
1606  case EVENT_LOGIN:
1607  pl = va_arg(args, player *);
1608  context->activator = Crossfire_Object_wrap(pl->ob);
1609  buf = va_arg(args, char *);
1610  if (buf != NULL)
1611  snprintf(context->message, sizeof(context->message), "%s", buf);
1612  snprintf(context->options, sizeof(context->options), "login");
1613  break;
1614 
1615  case EVENT_LOGOUT:
1616  pl = va_arg(args, player *);
1617  context->activator = Crossfire_Object_wrap(pl->ob);
1618  buf = va_arg(args, char *);
1619  if (buf != NULL)
1620  snprintf(context->message, sizeof(context->message), "%s", buf);
1621  snprintf(context->options, sizeof(context->options), "logout");
1622  break;
1623 
1624  case EVENT_REMOVE:
1625  op = va_arg(args, object *);
1626  context->activator = Crossfire_Object_wrap(op);
1627  snprintf(context->options, sizeof(context->options), "remove");
1628  break;
1629 
1630  case EVENT_SHOUT:
1631  op = va_arg(args, object *);
1632  context->activator = Crossfire_Object_wrap(op);
1633  buf = va_arg(args, char *);
1634  if (buf != NULL)
1635  snprintf(context->message, sizeof(context->message), "%s", buf);
1636  snprintf(context->options, sizeof(context->options), "shout");
1637  break;
1638 
1639  case EVENT_MUZZLE:
1640  op = va_arg(args, object *);
1641  context->activator = Crossfire_Object_wrap(op);
1642  buf = va_arg(args, char *);
1643  if (buf != NULL)
1644  snprintf(context->message, sizeof(context->message), "%s", buf);
1645  snprintf(context->options, sizeof(context->options), "muzzle");
1646  break;
1647 
1648  case EVENT_KICK:
1649  op = va_arg(args, object *);
1650  context->activator = Crossfire_Object_wrap(op);
1651  buf = va_arg(args, char *);
1652  if (buf != NULL)
1653  snprintf(context->message, sizeof(context->message), "%s", buf);
1654  snprintf(context->options, sizeof(context->options), "kick");
1655  break;
1656 
1657  case EVENT_MAPENTER:
1658  op = va_arg(args, object *);
1659  context->activator = Crossfire_Object_wrap(op);
1660  context->who = Crossfire_Map_wrap(va_arg(args, mapstruct *));
1661  snprintf(context->options, sizeof(context->options), "mapenter");
1662  break;
1663 
1664  case EVENT_MAPLEAVE:
1665  op = va_arg(args, object *);
1666  context->activator = Crossfire_Object_wrap(op);
1667  context->who = Crossfire_Map_wrap(va_arg(args, mapstruct *));
1668  snprintf(context->options, sizeof(context->options), "mapleave");
1669  break;
1670 
1671  case EVENT_CLOCK:
1672  snprintf(context->options, sizeof(context->options), "clock");
1673  break;
1674 
1675  case EVENT_MAPRESET:
1676  context->who = Crossfire_Map_wrap(va_arg(args, mapstruct *));
1677  snprintf(context->options, sizeof(context->options), "mapreset");
1678  break;
1679 
1680  case EVENT_TELL:
1681  op = va_arg(args, object *);
1682  buf = va_arg(args, char *);
1683  context->activator = Crossfire_Object_wrap(op);
1684  if (buf != NULL)
1685  snprintf(context->message, sizeof(context->message), "%s", buf);
1686  op = va_arg(args, object *);
1687  context->third = Crossfire_Object_wrap(op);
1688  snprintf(context->options, sizeof(context->options), "tell");
1689  break;
1690 
1691  case EVENT_MAPUNLOAD:
1692  context->who = Crossfire_Map_wrap(va_arg(args, mapstruct *));
1693  snprintf(context->options, sizeof(context->options), "mapunload");
1694  break;
1695 
1696  case EVENT_MAPLOAD:
1697  context->who = Crossfire_Map_wrap(va_arg(args, mapstruct *));
1698  snprintf(context->options, sizeof(context->options), "mapload");
1699  break;
1700  }
1701  va_end(args);
1702  context->returnvalue = 0;
1703 
1704  if (!do_script(context, 1)) {
1705  freeContext(context);
1706  return rv;
1707  }
1708 
1709  context = popContext();
1710  rv = context->returnvalue;
1711 
1712  /* Invalidate freed map wrapper. */
1713  if (context->event_code == EVENT_MAPUNLOAD)
1715 
1716  freeContext(context);
1717 
1718  return rv;
1719 }
1720 
1721 CF_PLUGIN int eventListener(int *type, ...) {
1722  int rv = 0;
1723  va_list args;
1724  char *buf;
1725  CFPContext *context;
1726  object *event;
1727 
1728  context = malloc(sizeof(CFPContext));
1729 
1730  context->message[0] = 0;
1731 
1732  va_start(args, type);
1733 
1734  context->who = Crossfire_Object_wrap(va_arg(args, object *));
1735  context->activator = Crossfire_Object_wrap(va_arg(args, object *));
1736  context->third = Crossfire_Object_wrap(va_arg(args, object *));
1737  buf = va_arg(args, char *);
1738  if (buf != NULL)
1739  snprintf(context->message, sizeof(context->message), "%s", buf);
1740  context->fix = va_arg(args, int);
1741  event = va_arg(args, object *);
1742  context->talk = va_arg(args, talk_info *);
1743  context->event_code = event->subtype;
1744  context->event = Crossfire_Object_wrap(event);
1745  cf_get_maps_directory(event->slaying, context->script, sizeof(context->script));
1746  snprintf(context->options, sizeof(context->options), "%s", event->name);
1747  context->returnvalue = 0;
1748 
1749  va_end(args);
1750 
1751  if (!do_script(context, 0)) {
1752  freeContext(context);
1753  return rv;
1754  }
1755 
1756  context = popContext();
1757  rv = context->returnvalue;
1758  freeContext(context);
1759  return rv;
1760 }
1761 
1763  int i;
1764 
1765  cf_log(llevDebug, "CFPython 2.0a closing\n");
1766 
1767  for (i = 0; i < NR_CUSTOM_CMD; i++) {
1768  if (CustomCommand[i].name != NULL)
1769  cf_free_string(CustomCommand[i].name);
1770  if (CustomCommand[i].script != NULL)
1771  cf_free_string(CustomCommand[i].script);
1772  }
1773 
1774  for (i = 0; i < PYTHON_CACHE_SIZE; i++) {
1775  Py_XDECREF(pycode_cache[i].code);
1776  if (pycode_cache[i].file != NULL)
1777  cf_free_string(pycode_cache[i].file);
1778  }
1779 
1780  Py_Finalize();
1781 
1782  return 0;
1783 }
#define DAYS_PER_WEEK
Definition: tod.h:13
See Pedestal.
Definition: object.h:121
Error, serious thing.
Definition: logger.h:11
#define AT_HOLYWORD
Definition: attack.h:97
Main Crossfire structure, one ingame object.
Definition: object.h:274
One player.
Definition: player.h:92
int npc_msg_count
How many NPCs reacted to the text being said.
Definition: dialog.h:59
#define MOVE_WALK
Object walks.
Definition: define.h:407
PyObject_HEAD object * obj
see doc/Developers/objects
Definition: object.h:108
CFPContext * current_context
Definition: cfpython.c:112
static PyObject * private_data
Definition: cfpython.c:118
PyObject * third
Definition: cfpython.h:118
#define EVENT_REMOVE
A Player character has been removed.
Definition: plugin.h:93
#define AT_ELECTRICITY
Definition: attack.h:79
sstring name
Definition: cfpython.c:82
static void cfpython_init_types(PyObject *m)
Set up the various types (map, object, archetype and so on) as well as some constants, and Crossfire.error.
Definition: cfpython.c:1306
#define AT_COUNTERSPELL
Definition: attack.h:95
#define EVENT_SHOUT
A player &#39;shout&#39; something.
Definition: plugin.h:94
static PyObject * getRegions(PyObject *self, PyObject *args)
Definition: cfpython.c:394
See Key.
Definition: object.h:127
static PyObject * cfpython_openpyfile(char *filename)
Open a file in the way we need it for compilePython() and postInitPlugin().
Definition: cfpython.c:755
See Ring.
Definition: object.h:185
Information.
Definition: logger.h:12
PyObject * Crossfire_Party_wrap(partylist *what)
#define AT_DEPLETE
Definition: attack.h:92
#define RANDO
The monster will go in a random direction until it is stopped by an obstacle, then it chooses another...
Definition: define.h:550
#define AT_GHOSTHIT
Definition: attack.h:85
Definition: object.h:125
object * cf_create_object(void)
Wrapper for object_new().
void cf_get_time(timeofday_t *tod)
static PyObject * getUniqueDirectory(PyObject *self, PyObject *args)
Definition: cfpython.c:245
int event_code
Definition: cfpython.h:122
#define ATNR_TURN_UNDEAD
Definition: attack.h:62
static PyObject * getReturnValue(PyObject *self, PyObject *args)
Definition: cfpython.c:170
Eneq((at)csd.uu.se): Id for close_container archetype.
Definition: object.h:229
void cf_free_string(sstring str)
Wrapper for free_string().
const char * cf_get_month_name(int index)
PyObject * Crossfire_Region_wrap(region *what)
See Bracers.
Definition: object.h:217
#define EVENT_KICK
A player was Kicked by a DM.
Definition: plugin.h:97
PyObject * Crossfire_Map_wrap(mapstruct *what)
Definition: cfpython_map.c:427
static void addConstants(PyObject *module, const char *name, const CFConstant *constants)
Definition: cfpython.c:947
#define NDI_ALL
Inform all players of this message.
Definition: newclient.h:246
static PyObject * getParties(PyObject *self, PyObject *args)
Definition: cfpython.c:381
CF_PLUGIN int cfpython_globalEventListener(int *type,...)
Definition: cfpython.c:1557
See Scroll.
Definition: object.h:221
static PyObject * getSeasonName(PyObject *self, PyObject *args)
Definition: cfpython.c:543
#define EVENT_DEATH
Player or monster dead.
Definition: plugin.h:67
static PyObject * getTime(PyObject *self, PyObject *args)
Definition: cfpython.c:455
#define NDI_WHITE
Definition: newclient.h:222
static void initContextStack(void)
Definition: cfpython.c:717
static void initConstants(PyObject *module)
Definition: cfpython.c:995
#define AT_INTERNAL
Definition: attack.h:99
int message_type
A reply_type value for message.
Definition: dialog.h:55
mapstruct * cf_get_empty_map(int sizex, int sizey)
Wrapper for get_empty_map().
#define EVENT_DESTROY
Object destroyed (includes map reset/swapout)
Definition: plugin.h:77
See Cloak.
Definition: object.h:204
One party.
Definition: party.h:10
static PyObject * getMapHasBeenLoaded(PyObject *self, PyObject *args)
Definition: cfpython.c:483
See Food.
Definition: object.h:112
#define EVENT_LOGIN
Player login.
Definition: plugin.h:88
PyObject * activator
Definition: cfpython.h:117
#define NDI_ORANGE
Definition: newclient.h:225
See Projectile.
Definition: object.h:117
#define PACEV2
The monster will pace as above but the length of the pace area is longer and the monster stops before...
Definition: define.h:559
sstring message
If not NULL, what the player will be displayed as said.
Definition: dialog.h:54
static PyObject * setPlayerMessage(PyObject *self, PyObject *args)
Definition: cfpython.c:602
#define NDI_BROWN
Sienna.
Definition: newclient.h:233
static PyObject * getSharedDictionary(PyObject *self, PyObject *args)
Definition: cfpython.c:337
#define ATNR_DEPLETE
Definition: attack.h:65
CF_PLUGIN int postInitPlugin(void)
Plugin was initialized, now to finish.
Definition: cfpython.c:1515
const int value
Definition: cfpython.c:944
See Holy Altar.
Definition: object.h:161
#define AT_CANCELLATION
Definition: attack.h:91
static PyObject * getArchetypes(PyObject *self, PyObject *args)
Definition: cfpython.c:342
int minute
Definition: tod.h:38
partylist * cf_party_get_first(void)
Get first party.
#define EVENT_MAPLEAVE
A player left a map.
Definition: plugin.h:91
See Spellbook.
Definition: object.h:203
See Money.
Definition: object.h:137
#define NDI_BLUE
Actually, it is Dodger Blue.
Definition: newclient.h:226
sstring replies[MAX_REPLIES]
Description for replies_words.
Definition: dialog.h:58
region * cf_region_get_first(void)
Get first region in region list.
char options[1024]
Definition: cfpython.h:124
#define ATNR_SLOW
Definition: attack.h:60
static PyObject * getLocalDirectory(PyObject *self, PyObject *args)
Definition: cfpython.c:257
static PyObject * registerGEvent(PyObject *self, PyObject *args)
Definition: cfpython.c:120
#define EVENT_LOGOUT
Player logout.
Definition: plugin.h:89
See Rune.
Definition: object.h:240
See Creator.
Definition: object.h:142
See Weapon.
Definition: object.h:119
static PyObject * findPlayer(PyObject *self, PyObject *args)
Definition: cfpython.c:199
See Helmet.
Definition: object.h:136
const char * slaying
Which race to do double damage to.
Definition: object.h:319
Object for applying character class modifications to someone.
Definition: object.h:138
PyTypeObject Crossfire_PartyType
sstring replies_words[MAX_REPLIES]
Available reply words.
Definition: dialog.h:57
See Altar Trigger.
Definition: object.h:133
See Rod.
Definition: object.h:109
#define AT_COLD
Definition: attack.h:80
static PyObject * npcSay(PyObject *self, PyObject *args)
Definition: cfpython.c:624
static PyObject * CFPythonError
Definition: cfpython.c:96
char message[1024]
Definition: cfpython.h:120
See Power Crystal.
Definition: object.h:242
mapstruct * cf_map_get_map_property(mapstruct *map, int propcode)
See Button Trigger.
Definition: object.h:132
void init_map_assoc_table(void)
Definition: cfpython_map.c:37
archetype * cf_archetype_get_next(archetype *arch)
Get next archetype in linked list.
int cf_timer_destroy(int id)
Destroys specified timer, equivalent of calling cftimer_destroy().
static PyObject * setReturnValue(PyObject *self, PyObject *args)
Definition: cfpython.c:174
static PyObject * getTempDirectory(PyObject *self, PyObject *args)
Definition: cfpython.c:249
See Drink.
Definition: object.h:157
#define EVENT_TIMER
Timer connected triggered it.
Definition: plugin.h:76
See Girdle.
Definition: object.h:223
region * cf_region_get_next(region *reg)
Get next region in region list.
See Amulet.
Definition: object.h:139
Wall.
Definition: object.h:191
Duplicator/multiplier object.
Definition: object.h:202
#define CIRCLE1
If the upper four bits of move_type / attack_movement are set to this number, the monster will move i...
Definition: define.h:532
#define AT_BLIND
Definition: attack.h:98
const char * name
Definition: cfpython.c:943
#define NDI_BLACK
Definition: newclient.h:221
#define EVENT_TELL
A player &#39;tell&#39; something.
Definition: plugin.h:95
player * cf_player_find(const char *plname)
Wrapper for find_player_partial_name().
#define EVENT_CRASH
Triggered when the server crashes.
Definition: plugin.h:85
static int do_script(CFPContext *context, int silent)
Definition: cfpython.c:906
#define EVENT_ATTACKS
Weapon or arrow hitting something.
Definition: plugin.h:66
#define CFAPI_MAP_PROP_NEXT
Definition: plugin.h:310
sstring cf_add_string(const char *str)
Wrapper for add_string().
animal &#39;body parts&#39; -b.t.
Definition: object.h:187
#define PETMOVE
If the upper four bits of attack_movement are set to this number, the monster follows a player until ...
Definition: define.h:517
See Boots.
Definition: object.h:212
#define PERIODS_PER_DAY
Definition: tod.h:17
#define HITRUN
Run to then hit player then run away cyclicly.
Definition: define.h:509
static PyMethodDef CFPythonMethods[]
Definition: cfpython.c:665
#define ATNR_CONFUSION
Definition: attack.h:54
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:465
peterm: detector is an object which notices the presense of another object and is triggered like butt...
Definition: object.h:149
#define NDI_DK_ORANGE
DarkOrange2.
Definition: newclient.h:227
See Mover.
Definition: object.h:140
static pycode_cache_entry pycode_cache[PYTHON_CACHE_SIZE]
Cached compiled scripts.
Definition: cfpython.c:94
See Wand & Staff.
Definition: object.h:220
int periodofday
Definition: tod.h:41
static PyObject * getPrivateDictionary(PyObject *self, PyObject *args)
Definition: cfpython.c:324
const char * cf_get_weekday_name(int index)
int year
Definition: tod.h:33
static PyObject * getDataDirectory(PyObject *self, PyObject *args)
Definition: cfpython.c:265
int cf_find_animation(const char *txt)
Wrapper for find_animation().
partylist * cf_party_get_next(partylist *party)
Get next party in party list.
void cf_log(LogLevel logLevel, const char *format,...)
Wrapper for LOG().
See Magic Ear.
Definition: object.h:131
#define NDI_NAVY
Definition: newclient.h:223
#define AT_TURN_UNDEAD
Definition: attack.h:89
static PyObject * getWhoIsActivator(PyObject *self, PyObject *args)
Definition: cfpython.c:278
PyCodeObject * code
Compiled code, NULL if there was an error.
Definition: cfpython.c:75
#define MOVE_ALL
Mask of all movement types.
Definition: define.h:413
#define AT_LIFE_STEALING
Definition: attack.h:100
Allows the use of a skill.
Definition: object.h:189
void cf_log_plain(LogLevel logLevel, const char *message)
Wrapper for LOG() that uses directly a buffer, without format.
static PyObject * getMapDirectory(PyObject *self, PyObject *args)
Definition: cfpython.c:241
static PyObject * getMonthName(PyObject *self, PyObject *args)
Definition: cfpython.c:551
#define AT_CHAOS
Definition: attack.h:94
Generic item builder, see subtypes below.
Definition: object.h:246
See Trap.
Definition: object.h:241
const char * cf_get_season_name(int index)
object * cf_friendlylist_get_first(void)
Get first object on friendly list.
#define ATNR_WEAPONMAGIC
Definition: attack.h:57
#define RUNATT
Run but attack if player catches up to object.
Definition: define.h:508
#define NDI_RED
Definition: newclient.h:224
static void set_exception(const char *fmt,...)
Set up an Python exception object.
Definition: cfpython.c:99
#define AT_GODPOWER
Definition: attack.h:96
static PyObject * createMap(PyObject *self, PyObject *args)
Definition: cfpython.c:229
Floor tile -> native layer 0.
Definition: object.h:186
See Shooting Weapon.
Definition: object.h:118
static PyObject * getPlayerDirectory(PyObject *self, PyObject *args)
Definition: cfpython.c:261
#define EVENT_MAPENTER
A player entered a map.
Definition: plugin.h:90
See Sign & Magic Mouth.
Definition: object.h:211
Asking a question.
Definition: dialog.h:12
int replies_count
How many items in replies_words and replies.
Definition: dialog.h:56
char script[1024]
Definition: cfpython.h:123
#define ATNR_CANCELLATION
Definition: attack.h:64
int cf_init_plugin(f_plug_api getHooks)
CF_PLUGIN int initPlugin(const char *iversion, f_plug_api gethooksptr)
Plugin initialisation function.
Definition: cfpython.c:1372
See Book.
Definition: object.h:114
static void addSimpleConstants(PyObject *module, const char *name, const CFConstant *constants)
Do half the job of addConstants.
Definition: cfpython.c:979
CF_PLUGIN char SvnRevPlugin[]
Definition: cfpython.c:65
See Exit.
Definition: object.h:181
double speed
Definition: cfpython.c:84
#define EVENT_STOP
Thrown object stopped.
Definition: plugin.h:71
#define MAX_REPLIES
How many NPC replies maximum to tell the player.
Definition: dialog.h:44
#define AT_DISEASE
Definition: attack.h:102
PyTypeObject Crossfire_ObjectType
time_t cached_time
Time this cache entry was created.
Definition: cfpython.c:76
battleground, by Andreas Vogl
Definition: object.h:163
inserts an invisible, weightless force into a player with a specified string.
Definition: object.h:158
#define ATNR_PARALYZE
Definition: attack.h:61
#define MOVE_SWIM
Swimming object.
Definition: define.h:411
void Handle_Map_Unload_Hook(Crossfire_Map *map)
Definition: cfpython_map.c:422
See Treasure.
Definition: object.h:110
See Special Key.
Definition: object.h:124
#define NDI_ALL_DMS
Inform all logged in DMs.
Definition: newclient.h:247
#define MOVE_FLY_LOW
Low flying object.
Definition: define.h:408
Structure used to build up dialog information when a player says something.
Definition: dialog.h:51
A real, living creature.
Definition: object.h:200
static PyObject * getWhatIsMessage(PyObject *self, PyObject *args)
Definition: cfpython.c:296
inserts an invisible, weightless force into a player with a specified string WHEN TRIGGERED...
Definition: object.h:153
See Clock.
Definition: object.h:115
This is a game region.
Definition: map.h:276
static PyObject * getCFPythonVersion(PyObject *self, PyObject *args)
Definition: cfpython.c:164
PyMODINIT_FUNC initcjson(void)
Definition: cjson.c:1389
#define ATNR_LIFE_STEALING
Definition: attack.h:73
#define ATNR_DEATH
Definition: attack.h:66
#define snprintf
Definition: win32.h:46
PyTypeObject Crossfire_ArchetypeType
void cf_cost_string_from_value(uint64_t cost, int largest_coin, char *buffer, int length)
Wrapper for cost_string_from_value modified to take a char* and length instead of a StringBuffer...
static PyObject * findFace(PyObject *self, PyObject *args)
Definition: cfpython.c:491
static PyObject * getConfigDirectory(PyObject *self, PyObject *args)
Definition: cfpython.c:253
mapstruct * cf_map_get_map(const char *name, int flags)
Wrapper for ready_map_name().
#define EVENT_CLOSE
Container closed.
Definition: plugin.h:75
#define MOVE_FLYING
Combo of fly_low and fly_high.
Definition: define.h:410
static FILE * cfpython_pyfile_asfile(PyObject *obj)
Return a file object from a Python file (as needed for compilePython() and postInitPlugin()) ...
Definition: cfpython.c:777
Reply to something.
Definition: dialog.h:11
int month
Definition: tod.h:34
PyObject * event
Definition: cfpython.h:119
#define ATNR_BLIND
Definition: attack.h:71
Definition: object.h:145
#define PLUGIN_NAME
Definition: cfanim.h:32
#define PACEH
The monster will pace back and forth until attacked.
Definition: define.h:540
#define NDI_GREEN
SeaGreen.
Definition: newclient.h:228
const char * name
The name of the object, obviously...
Definition: object.h:311
#define NR_CUSTOM_CMD
Definition: cfpython.c:88
CF_PLUGIN void cfpython_runPluginCommand(object *op, const char *params)
Definition: cfpython.c:1480
#define EVENT_PICKUP
Object picked up.
Definition: plugin.h:69
static PyObject * getMaps(PyObject *self, PyObject *args)
Definition: cfpython.c:368
#define ATNR_INTERNAL
Definition: attack.h:72
#define ATNR_FEAR
Definition: attack.h:63
static PythonCmd CustomCommand[NR_CUSTOM_CMD]
Commands defined by scripts.
Definition: cfpython.c:91
void cf_system_register_global_event(int event, const char *name, f_plug_event hook)
CF_PLUGIN void * getPluginProperty(int *type,...)
Gets a plugin property.
Definition: cfpython.c:1435
int weekofmonth
Definition: tod.h:39
static int current_command
Definition: cfpython.c:114
PyObject * Crossfire_Object_wrap(object *what)
Python initialized.
int day
Definition: tod.h:35
#define ATNR_MAGIC
Definition: attack.h:50
int dayofweek
Definition: tod.h:36
See Trapdoor.
Definition: object.h:210
See Potion.
Definition: object.h:111
static PyObject * unregisterGEvent(PyObject *self, PyObject *args)
Definition: cfpython.c:132
static PyObject * getPeriodofdayName(PyObject *self, PyObject *args)
Definition: cfpython.c:567
#define AT_FIRE
Definition: attack.h:78
CF_PLUGIN int closePlugin(void)
Close the plugin.
Definition: cfpython.c:1762
#define ATNR_HOLYWORD
Definition: attack.h:70
void cf_system_unregister_global_event(int event, const char *name)
See Shop Mat.
Definition: object.h:184
#define NDI_LT_GREEN
DarkSeaGreen, which is actually paler than seagreen - also background color.
Definition: newclient.h:229
time_t used_time
Last use of this cache entry.
Definition: cfpython.c:76
#define ATNR_PHYSICAL
Definition: attack.h:49
const char * name
Command name.
Definition: commands.h:38
char * cf_get_maps_directory(const char *name, char *buf, int size)
Wrapper for create_pathname().
#define EVENT_SAY
Someone speaks.
Definition: plugin.h:70
#define ATNR_ELECTRICITY
Definition: attack.h:52
#define ATNR_POISON
Definition: attack.h:59
#define AT_PHYSICAL
Definition: attack.h:76
unsigned __int64 uint64_t
Definition: win32.h:167
See Spell.
Definition: object.h:214
static PyObject * matchString(PyObject *self, PyObject *args)
Definition: cfpython.c:184
object * cf_friendlylist_get_next(object *ob)
Get next object on friendly list.
See Handle Trigger.
Definition: object.h:129
#define EVENT_MAPLOAD
A map is loaded.
Definition: plugin.h:99
See Locked Door.
Definition: object.h:123
#define EVENT_THROW
Object is thrown.
Definition: plugin.h:73
See Spinner.
Definition: object.h:205
static PyObject * getPlayers(PyObject *self, PyObject *args)
Definition: cfpython.c:355
See Door.
Definition: object.h:126
CF_PLUGIN int eventListener(int *type,...)
Handles an object-related event.
Definition: cfpython.c:1721
Many many details.
Definition: logger.h:14
sstring script
Definition: cfpython.c:83
LogLevel
Log levels for the LOG() function.
Definition: logger.h:10
See Magic Wall.
Definition: object.h:168
Object owned by a player which can convert a monster into a peaceful being incapable of attack...
Definition: object.h:164
See Converter.
Definition: object.h:216
#define EVENT_MAPUNLOAD
A map is freed (includes swapping out)
Definition: plugin.h:98
int cf_find_face(const char *name, int error)
Wrapper for find_face().
can add a skill to player&#39;s inventory -bt.
Definition: object.h:234
static PyObject * shared_data
Definition: cfpython.c:116
#define ATNR_COUNTERSPELL
Definition: attack.h:68
#define WAITATT
Wait for player to approach then hit, move if hit.
Definition: define.h:510
struct PythonCmdStruct PythonCmd
PyObject * who
Definition: cfpython.h:116
#define ATNR_DRAIN
Definition: attack.h:56
#define ATNR_GHOSTHIT
Definition: attack.h:58
Basic sentence.
Definition: dialog.h:10
#define EVENT_MAPRESET
A map is resetting.
Definition: plugin.h:92
Lamp.
Definition: object.h:201
static PyObject * getScriptParameters(PyObject *self, PyObject *args)
Definition: cfpython.c:307
#define ALLRUN
Always run, never attack good for sim.
Definition: define.h:512
#define EVENT_DROP
Object dropped on the floor.
Definition: plugin.h:68
See Container.
Definition: object.h:231
#define AT_DRAIN
Definition: attack.h:83
static const flag_definition flags[]
Flag mapping.
#define EVENT_GKILL
Triggered when anything got killed by anyone.
Definition: plugin.h:87
static PyObject * getWeekdayName(PyObject *self, PyObject *args)
Definition: cfpython.c:559
#define EVENT_TIME
Triggered each time the object can react/move.
Definition: plugin.h:72
static PyObject * getScriptName(PyObject *self, PyObject *args)
Definition: cfpython.c:303
object * ob
The object representing the player.
Definition: player.h:158
const char * cf_re_cmp(const char *str, const char *regexp)
Wrapper for re_cmp().
object * cf_object_get_object_property(object *op, int propcode)
const char * sstring
Strings that should be manipulated through add_string() and free_string().
Definition: global.h:40
PyTypeObject Crossfire_PlayerType
static PyObject * registerCommand(PyObject *self, PyObject *args)
Definition: cfpython.c:420
static PyObject * readyMap(PyObject *self, PyObject *args)
Definition: cfpython.c:216
#define EVENT_CLOCK
Global time event.
Definition: plugin.h:84
See Player.
Definition: object.h:107
#define CFAPI_PLAYER_PROP_NEXT
Definition: plugin.h:287
PyTypeObject Crossfire_RegionType
archetype * cf_archetype_get_first(void)
Get first archetype.
See Poison Food.
Definition: object.h:113
static void log_python_error(void)
Trace a Python error to the Crossfire log.
Definition: cfpython.c:797
See Shield.
Definition: object.h:135
See Floor (Encounter).
Definition: object.h:182
static PyObject * getWhoAmI(PyObject *self, PyObject *args)
Definition: cfpython.c:269
#define RUSH
Rush toward player blindly, similiar to dumb monster.
Definition: define.h:511
#define AT_POISON
Definition: attack.h:86
command_function func
Pointer to command function.
Definition: commands.h:39
#define vsnprintf
Definition: win32.h:61
Represents one command.
Definition: commands.h:37
void replace(const char *src, const char *key, const char *replacement, char *result, size_t resultsize)
Replace in string src all occurrences of key by replacement.
Definition: utils.c:353
#define WAIT2
Monster does not try to move towards player if far.
Definition: define.h:514
Mark Wedel (mark@pyramid.com) Shop inventories.
Definition: object.h:238
See Altar.
Definition: object.h:122
#define CF_PLUGIN
Definition: plugin_common.h:38
#define MAX_NPC
How many NPCs maximum will reply to the player.
Definition: dialog.h:46
#define PYTHON_CACHE_SIZE
Definition: cfpython.c:68
Definition: object.h:243
Also see SKILL_TOOL (74) below.
Definition: object.h:143
static PyCodeObject * compilePython(char *filename)
Outputs the compiled bytecode for a given python file, using in-memory caching of bytecode...
Definition: cfpython.c:817
sstring file
Script full path.
Definition: cfpython.c:74
#define EVENT_APPLY
Object applied-unapplied.
Definition: plugin.h:64
See Shop Floor.
Definition: object.h:183
#define HOURS_PER_DAY
Definition: tod.h:12
See Button.
Definition: object.h:207
Only for debugging purposes.
Definition: logger.h:13
See Gate.
Definition: object.h:206
float time
How long it takes to execute this command.
Definition: commands.h:40
#define RANDO2
Constantly move in a different random direction.
Definition: define.h:555
#define AT_FEAR
Definition: attack.h:90
#define ATNR_GODPOWER
Definition: attack.h:69
misc.
Definition: object.h:193
#define AT_SLOW
Definition: attack.h:87
Represents the ingame time.
Definition: tod.h:32
Material for building.
Definition: object.h:248
struct talk_info * talk
Definition: cfpython.h:127
See Handle.
Definition: object.h:208
static PyObject * getEvent(PyObject *self, PyObject *args)
Definition: cfpython.c:315
See Timed Gate.
Definition: object.h:128
#define WEEKS_PER_MONTH
Definition: tod.h:14
mapstruct * cf_map_has_been_loaded(const char *name)
Wrapper for has_been_loaded().
int hour
Definition: tod.h:37
static CFPContext * popContext(void)
Definition: cfpython.c:732
const char * cf_get_directory(int id)
Gets a directory Crossfire uses.
mapstruct * cf_map_get_first(void)
Gives access to first_map.
#define NDI_GREY
Definition: newclient.h:232
#define AT_MAGIC
Definition: attack.h:77
void init_object_assoc_table(void)
Lauwenmark: an invisible object holding a plugin event hook.
Definition: object.h:227
static void pushContext(CFPContext *context)
Definition: cfpython.c:722
struct _cfpcontext * down
Definition: cfpython.h:115
#define DISTATT
Move toward a player if far, but maintain some space, attack from a distance - good for missile users...
Definition: define.h:505
static PyObject * destroyTimer(PyObject *self, PyObject *args)
Definition: cfpython.c:475
static PyObject * createCFObject(PyObject *self, PyObject *args)
Definition: cfpython.c:144
#define EVENT_PLAYER_DEATH
Global Death event.
Definition: plugin.h:86
static PyObject * getWhoIsThird(PyObject *self, PyObject *args)
Definition: cfpython.c:287
See Monster (Grimreaper).
Definition: object.h:130
#define NDI_UNIQUE
Print immediately, don&#39;t buffer.
Definition: newclient.h:245
PyObject * Crossfire_Archetype_wrap(archetype *what)
See Gloves.
Definition: object.h:213
#define MOVE_FLY_HIGH
High flying object.
Definition: define.h:409
int returnvalue
Definition: cfpython.h:125
sstring npc_msgs[MAX_NPC]
What the NPCs will say.
Definition: dialog.h:60
A force, holding the effect of a resistance potion.
Definition: object.h:225
#define PACEH2
The monster will pace as above but the length of the pace area is longer and the monster stops before...
Definition: define.h:543
metals, minerals, dragon scales
Definition: object.h:188
#define NDI_TAN
Khaki.
Definition: newclient.h:235
See Pit.
Definition: object.h:209
#define ATNR_COLD
Definition: attack.h:53
#define PACEV
The monster will pace back and forth until attacked.
Definition: define.h:556
#define MONTHS_PER_YEAR
Definition: tod.h:15
#define CIRCLE2
Same as CIRCLE1 but a larger circle is used.
Definition: define.h:539
static PyObject * log_message(PyObject *self, PyObject *args)
Definition: cfpython.c:499
static PyObject * addReply(PyObject *self, PyObject *args)
Definition: cfpython.c:575
See Swamp.
Definition: object.h:236
static PyObject * findAnimation(PyObject *self, PyObject *args)
Definition: cfpython.c:535
This is a game-map.
Definition: map.h:325
static PyObject * catcher
A Python object receiving the contents of Python&#39;s stderr, and used to output to the Crossfire log in...
Definition: cfpython.c:789
const char * cf_get_periodofday_name(int index)
PyTypeObject Crossfire_MapType
Definition: cfpython_map.c:551
#define AT_CONFUSION
Definition: attack.h:81
static void freeContext(CFPContext *context)
Definition: cfpython.c:744
See Jewel.
Definition: object.h:167
static PyObject * costStringFromValue(PyObject *self, PyObject *args)
Definition: cfpython.c:653
#define MOVE_BOAT
Boats/sailing.
Definition: define.h:412
One compiled script, cached in memory.
Definition: cfpython.c:73
#define NDI_GOLD
Definition: newclient.h:234
See Breastplate Armor.
Definition: object.h:120
#define AT_ACID
Definition: attack.h:82
static PyObject * getFriendlyList(PyObject *self, PyObject *args)
Definition: cfpython.c:407
#define ATNR_CHAOS
Definition: attack.h:67
#define ATNR_ACID
Definition: attack.h:55
Definition: object.h:148
#define AT_DEATH
Definition: attack.h:93
See Disease.
Definition: object.h:244
static PyObject * createCFObjectByName(PyObject *self, PyObject *args)
Definition: cfpython.c:152
#define EVENT_ATTACKED
Object attacked, with weapon or spell.
Definition: plugin.h:65
object * cf_create_object_by_name(const char *name)
Wrapper for create_archetype() and create_archetype_by_object_name().
See Director.
Definition: object.h:222
See Teleporter.
Definition: object.h:141
#define AT_WEAPONMAGIC
Definition: attack.h:84
#define EVENT_BORN
A new character has been created.
Definition: plugin.h:83
#define SEASONS_PER_YEAR
Definition: tod.h:16
CFPContext * context_stack
Definition: cfpython.c:110
#define EVENT_TRIGGER
Button pushed, lever pulled, etc.
Definition: plugin.h:74
void(* f_plug_api)(int *type,...)
General API function.
Definition: plugin.h:125
b.t.
Definition: object.h:170
#define EVENT_USER
User-defined event.
Definition: plugin.h:78
#define DISTHIT
Attack from a distance if hit as recommended by Frank.
Definition: define.h:513
int season
Definition: tod.h:40
#define AT_PARALYZE
Definition: attack.h:88
See Savebed.
Definition: object.h:219
#define EVENT_MUZZLE
A player was Muzzled (no_shout set).
Definition: plugin.h:96
b.t.
Definition: object.h:169
Definition: object.h:224
#define ATNR_FIRE
Definition: attack.h:51
#define ATNR_DISEASE
Definition: attack.h:74
#define PLUGIN_VERSION
Definition: cfanim.h:33