Atrinik Server  4.0
atrinik_player.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
32 #include <plugin_python.h>
33 #include <toolkit/packet.h>
34 #include <faction.h>
35 #include <player.h>
36 #include <object.h>
37 
41 static fields_struct fields[] = {
42  {"next", FIELDTYPE_PLAYER, offsetof(player, next), FIELDFLAG_READONLY, 0,
43  "Next player in a list.; Atrinik.Player.Player or None (readonly)"},
44  {"prev", FIELDTYPE_PLAYER, offsetof(player, prev), FIELDFLAG_READONLY, 0,
45  "Previous player in a list.; Atrinik.Player.Player or None "
46  "(readonly)"},
47 
48  {"party", FIELDTYPE_PARTY, offsetof(player, party), FIELDFLAG_READONLY, 0,
49  "Party the player is a member of.; Atrinik.Party.Party or None "
50  "(readonly)"},
51  {"savebed_map", FIELDTYPE_CARY, offsetof(player, savebed_map), 0,
52  sizeof(((player *) NULL)->savebed_map),
53  "Path to the player's savebed map.; str"},
54  {"bed_x", FIELDTYPE_INT16, offsetof(player, bed_x), 0, 0,
55  "X coordinate of the player's savebed.; int"},
56  {"bed_y", FIELDTYPE_INT16, offsetof(player, bed_y), 0, 0,
57  "Y coordinate of the player's savebed.; int"},
58  {"ob", FIELDTYPE_OBJECT, offsetof(player, ob), FIELDFLAG_READONLY, 0,
59  "The :class:`Atrinik.Object.Object` representing the player.; "
60  "Atrinik.Object.Object (readonly)"},
61  {"quest_container", FIELDTYPE_OBJECT, offsetof(player, quest_container),
62  FIELDFLAG_READONLY, 0, "Player's quest container.; "
63  "Atrinik.Object.Object (readonly)"},
64  {"target_object", FIELDTYPE_OBJECTREF, offsetof(player, target_object), 0,
65  offsetof(player, target_object_count), "Currently targeted "
66  "NPC/monster.; Atrinik.Object.Object or None"},
67  {"no_chat", FIELDTYPE_BOOLEAN, offsetof(player, no_chat), 0, 0,
68  "If true, the player is not allowed to chat.; bool"},
69  {"tcl", FIELDTYPE_BOOLEAN, offsetof(player, tcl), 0, 0,
70  "If true, the player ignores collision with terrain.; bool"},
71  {"tgm", FIELDTYPE_BOOLEAN, offsetof(player, tgm), 0, 0,
72  "If true, the player is in god-mode and cannot die or take damage "
73  "from any source.; bool"},
74  {"tli", FIELDTYPE_BOOLEAN, offsetof(player, tli), 0, 0,
75  "If true, the player has lighting disabled.; bool"},
76  {"tls", FIELDTYPE_BOOLEAN, offsetof(player, tls), 0, 0,
77  "If true, the player ignores line of sight.; bool"},
78  {"tsi", FIELDTYPE_BOOLEAN, offsetof(player, tsi), 0, 0,
79  "If true, the player can see system-invisible objects.; bool"},
80  {"cmd_permissions", FIELDTYPE_LIST, offsetof(player, cmd_permissions), 0,
81  FIELDTYPE_CMD_PERMISSIONS, "Player's command permissions.; "
82  "Atrinik.AttrList.AttrList"},
83  {"factions", FIELDTYPE_LIST, offsetof(player, factions), 0,
84  FIELDTYPE_FACTIONS, "Player's factions.; "
85  "Atrinik.AttrList.AttrList"},
86  {"fame", FIELDTYPE_INT64, offsetof(player, fame), 0, 0,
87  "Fame (or infamy) of the player.; int"},
88  {"container", FIELDTYPE_OBJECT, offsetof(player, container),
89  FIELDFLAG_READONLY, 0, "Container the player has open.; "
90  "Atrinik.Object.Object or None (readonly)"},
91  {"combat", FIELDTYPE_BOOLEAN, offsetof(player, combat),
92  0, 0, "Whether the player is ready to engage in combat and should "
93  "swing their weapon at targeted enemies.; bool"},
94  {"combat_force", FIELDTYPE_BOOLEAN, offsetof(player, combat_force),
95  0, 0, "Whether the player should swing their weapon at their "
96  "target, be it friend or foe.; bool"},
97 
98  {"s_ext_title_flag", FIELDTYPE_BOOLEAN,
99  offsetof(player, cs), 0,
100  offsetof(socket_struct, ext_title_flag),
101  "If True, will force updating the player's map name.; bool"},
102  {"s_socket_version", FIELDTYPE_UINT32,
103  offsetof(player, cs), FIELDFLAG_READONLY,
104  offsetof(socket_struct, socket_version),
105  "Socket version of the player's client.; int (readonly)"},
106  {"s_packets", FIELDTYPE_LIST,
107  offsetof(player, cs), 0,
108  offsetof(socket_struct, packets),
109  "Packets that have been enqueued to the player's client.; "
110  "Atrinik.AttrList.AttrList"},
111  {"s_packet_recv_cmd", FIELDTYPE_PACKET,
112  offsetof(player, cs), 0,
113  offsetof(socket_struct, packet_recv_cmd),
114  "Commands received from the player's client.; bytes"},
115 };
116 
118 static const char doc_Atrinik_Player_GetEquipment[] =
119 ".. method:: GetEquipment(slot).\n\n"
120 "Get player's current equipment object for a given slot.\n\n"
121 ":param slot: The slot number; one of PLAYER_EQUIP_xxx, eg, :attr:"
122 "`~Atrinik.PLAYER_EQUIP_LEGGINGS`.\n"
123 ":type slot: int\n"
124 ":returns: The equipment object for the given slot, None if there's no object "
125 "in the slot.\n"
126 ":rtype: :class:`Atrinik.Object.Object` or None\n"
127 ":throws ValueError: If the *slot* number is invalid.";
128 
134  PyObject *args)
135 {
136  int slot;
137 
138  if (!PyArg_ParseTuple(args, "i", &slot)) {
139  return NULL;
140  }
141 
142  if (slot < 0 || slot >= PLAYER_EQUIP_MAX) {
143  PyErr_SetString(PyExc_ValueError, "Invalid slot number.");
144  return NULL;
145  }
146 
147  return wrap_object(self->pl->equipment[slot]);
148 }
149 
151 static const char doc_Atrinik_Player_CanCarry[] =
152 ".. method:: CanCarry(what).\n\n"
153 "Check whether the player can carry *what*, taking weight limit into "
154 "consideration.\n\n"
155 ":param what: Object that player wants to get. This can be the exact weight to "
156 "check instead of calculating the object's weight.\n"
157 ":type what: :class:`Atrinik.Object.Object` or int\n"
158 ":returns: Whether the player can carry the *what*.\n"
159 ":rtype: bool";
160 
165 static PyObject *Atrinik_Player_CanCarry(Atrinik_Player *self, PyObject *what)
166 {
167  uint32_t weight;
168 
169  if (PyObject_TypeCheck(what, &Atrinik_ObjectType)) {
170  OBJEXISTCHECK((Atrinik_Object *) what);
171  weight = WEIGHT_NROF(((Atrinik_Object *) what)->obj,
172  ((Atrinik_Object *) what)->obj->nrof);
173  } else if (PyInt_Check(what)) {
174  weight = PyInt_AsLong(what);
175  } else {
176  PyErr_SetString(PyExc_TypeError,
177  "Invalid object type for 'what' parameter.");
178  return NULL;
179  }
180 
181  return Py_BuildBoolean(hooks->player_can_carry(self->pl->ob, weight));
182 }
183 
185 static const char doc_Atrinik_Player_AddExp[] =
186 ".. method:: AddExp(skill, exp, exact=False, level=False).\n\n"
187 "Add (or subtract) experience.\n\n"
188 ":param skill: ID or name of the skill to receive/lose exp in.\n"
189 ":type skill: int or str\n"
190 ":param exp: How much exp to gain/lose. If *level* is true, this is the number "
191 "of levels to gain/lose in the specified skill.\n"
192 ":type exp: int\n"
193 ":param exact: If True, the given exp will not be capped.\n"
194 ":type exact: bool\n"
195 ":param level: If True, will calculate exact experience needed for next (or "
196 "previous) level.\n"
197 ":type level: bool\n"
198 ":raises ValueError: If the skill ID/name is invalid.\n"
199 ":raises Atrinik.AtrinikError: If the player doesn't have the specified skill.";
200 
205 static PyObject *Atrinik_Player_AddExp(Atrinik_Player *self, PyObject *args)
206 {
207  PyObject *skill;
208  uint32_t skill_nr;
209  int64_t exp_gain;
210  int exact = 0, level = 0;
211 
212  if (!PyArg_ParseTuple(args, "OL|ii", &skill, &exp_gain, &exact, &level)) {
213  return NULL;
214  }
215 
216  if (PyInt_Check(skill)) {
217  skill_nr = PyInt_AsLong(skill);
218 
219  if (skill_nr >= NROFSKILLS) {
220  PyErr_Format(PyExc_ValueError,
221  "Skill ID '%d' is invalid; 0-%d should be used.", skill_nr,
222  NROFSKILLS - 1);
223  return NULL;
224  }
225  } else if (PyString_Check(skill)) {
226  const char *skill_name = PyString_AsString(skill);
227 
228  for (skill_nr = 0; skill_nr < NROFSKILLS; skill_nr++) {
229  if (strcmp(hooks->skills[skill_nr].name, skill_name) == 0) {
230  break;
231  }
232  }
233 
234  if (skill_nr == NROFSKILLS) {
235  PyErr_Format(PyExc_ValueError, "Skill '%s' does not exist.",
236  skill_name);
237  return NULL;
238  }
239  } else {
240  PyErr_SetString(PyExc_TypeError,
241  "Invalid object type for 'skill' parameter.");
242  return NULL;
243  }
244 
245  if (self->pl->skill_ptr[skill_nr] == NULL) {
246  PyErr_Format(AtrinikError, "Player %s does not have the skill '%s'.",
247  self->pl->ob->name, hooks->skills[skill_nr].name);
248  return NULL;
249  }
250 
251  if (level) {
252  int level_reach = self->pl->skill_ptr[skill_nr]->level + exp_gain;
253  level_reach = MAX(1, MIN(MAXLEVEL, level_reach));
254  exp_gain = hooks->level_exp(level_reach, 1.0) -
255  self->pl->skill_ptr[skill_nr]->stats.exp;
256  }
257 
258  hooks->add_exp(self->pl->ob, exp_gain, skill_nr, exact);
259 
260  Py_INCREF(Py_None);
261  return Py_None;
262 }
263 
265 static const char doc_Atrinik_Player_BankDeposit[] =
266 ".. method:: BankDeposit(text).\n\n"
267 "Deposit money to bank.\n\n"
268 ":param text: How much money to deposit, in string representation.\n"
269 ":type text: str\n"
270 ":returns: Tuple containing the status code (one of the BANK_xxx constants, "
271 "eg, :attr:`~Atrinik.BANK_SUCCESS`) and amount of money deposited as "
272 "integer.\n"
273 ":rtype: tuple";
274 
280  PyObject *args)
281 {
282  const char *text;
283 
284  if (!PyArg_ParseTuple(args, "s", &text)) {
285  return NULL;
286  }
287 
288  int64_t value;
289  int ret = hooks->bank_deposit(self->pl->ob, text, &value);
290 
291  return Py_BuildValue("(iL)", ret, value);
292 }
293 
295 static const char doc_Atrinik_Player_BankWithdraw[] =
296 ".. method:: BankWithdraw(text).\n\n"
297 "Withdraw money from bank.\n\n"
298 ":param text: How much money to withdraw, in string representation.\n"
299 ":type text: str\n"
300 ":returns: Tuple containing the status code (one of the BANK_xxx constants, "
301 "eg, :attr:`~Atrinik.BANK_SUCCESS`) and amount of money withdrawn as "
302 "integer.\n"
303 ":rtype: tuple";
304 
310  PyObject *args)
311 {
312  const char *text;
313 
314  if (!PyArg_ParseTuple(args, "s", &text)) {
315  return NULL;
316  }
317 
318  int64_t value;
319  int ret = hooks->bank_withdraw(self->pl->ob, text, &value);
320 
321  return Py_BuildValue("(iL)", ret, value);
322 }
323 
325 static const char doc_Atrinik_Player_BankBalance[] =
326 ".. method:: BankBalance().\n\n"
327 "Figure out how much money player has in bank.\n\n"
328 ":returns: Integer value of the money in bank.\n"
329 ":rtype: int";
330 
336 {
337  return Py_BuildValue("L", hooks->bank_get_balance(self->pl->ob));
338 }
339 
342 ".. method:: SwapApartments(oldmap, newmap, x, y).\n\n"
343 "Swaps *oldmap* apartment with *newmap* one.\n\nCopies old items from *oldmap* "
344 "to *newmap* at *x*, *y* and saves the map.\n\n"
345 ":param oldmap: The old apartment map.\n"
346 ":type oldmap: str\n"
347 ":param newmap: The new apartment map.\n"
348 ":type newmap: str\n"
349 ":param x: X coordinate to copy the items to.\n"
350 ":type x: int\n"
351 ":param y: Y coordinate to copy the items to.\n"
352 ":type y: int\n"
353 ":returns: Whether the operation was successful or not.\n"
354 ":rtype: bool";
355 
361  PyObject *args)
362 {
363  const char *mapold, *mapnew;
364  int x, y;
365 
366  if (!PyArg_ParseTuple(args, "ssii", &mapold, &mapnew, &x, &y)) {
367  return NULL;
368  }
369 
370  return Py_BuildBoolean(hooks->swap_apartments(mapold, mapnew, x, y,
371  self->pl->ob));
372 }
373 
376 ".. method:: ExecuteCommand(command).\n\n"
377 "Make player execute a command.\n\n"
378 ":param command: Command to execute.\n"
379 ":type command: str\n"
380 ":returns: Return value of the command.\n"
381 ":rtype: int\n"
382 ":raises Atrinik.AtrinikError: If player is not in a state to execute "
383 "commands.";
384 
390  PyObject *args)
391 {
392  const char *command;
393 
394  if (!PyArg_ParseTuple(args, "s", &command)) {
395  return NULL;
396  }
397 
398  if (self->pl->cs->state != ST_PLAYING) {
399  PyErr_SetString(AtrinikError,
400  "Player is not in a state to execute commands.");
401  return NULL;
402  }
403 
404  /* Make a copy of the command, since commands_handle() modifies the
405  * string. */
406  char *cp = strdup(command);
407  hooks->commands_handle(self->pl->ob, cp);
408  free(cp);
409 
410  Py_INCREF(Py_None);
411  return Py_None;
412 }
413 
416 ".. method:: FindMarkedObject().\n\n"
417 "Find marked object in player's inventory.\n\n"
418 ":returns: The marked object, or None if no object is marked\n"
419 ":rtype: :class:`Atrinik.Object.Object` or None";
420 
426 {
427  return wrap_object(hooks->find_marked_object(self->pl->ob));
428 }
429 
431 static const char doc_Atrinik_Player_Sound[] =
432 ".. method:: Sound(filename, type=Atrinik.CMD_SOUND_EFFECT, x=0, y=0, loop=0, "
433 "volume=0).\n\n"
434 "Play a sound to the specified player.\n\n"
435 ":param filename: Sound file to play.\n"
436 ":type filename: str\n"
437 ":param type: Sound type to play, one of the CMD_SOUND_xxx constants, eg, "
438 ":attr:`~Atrinik.CMD_SOUND_BACKGROUND`.\n"
439 ":type type: int\n"
440 ":param x: X position where the sound is playing from.\n"
441 ":type x: int\n"
442 ":param y: Y position where the sound is playing from.\n"
443 ":type y: int\n"
444 ":param loop: How many times to loop the sound, -1 to loop infinitely.\n"
445 ":type loop: int\n"
446 ":param volume: Volume adjustment.\n"
447 ":type volume: int";
448 
453 static PyObject *Atrinik_Player_Sound(Atrinik_Player *self, PyObject *args,
454  PyObject *keywds)
455 {
456  static char *kwlist[] = {
457  "filename", "type", "x", "y", "loop", "volume", NULL
458  };
459  const char *filename;
460  int type = CMD_SOUND_EFFECT, x = 0, y = 0, loop = 0, volume = 0;
461 
462  if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|iiiii", kwlist,
463  &filename, &type, &x, &y, &loop, &volume)) {
464  return NULL;
465  }
466 
467  hooks->play_sound_player_only(self->pl, type, filename, x, y, loop, volume);
468 
469  Py_INCREF(Py_None);
470  return Py_None;
471 }
472 
474 static const char doc_Atrinik_Player_Examine[] =
475 ".. method:: Examine(obj, ret=False).\n\n"
476 "Makes player examine the specified object.\n\n"
477 ":param obj: Object to examine.\n"
478 ":type obj: :class:`Atrinik.Object.Object`\n"
479 ":param ret: If True, instead of printing out the examine text to the player,"
480 "the examine text is returned as a string.\n"
481 ":type ret: bool\n"
482 ":returns: None, examine string in case *ret* was True.\n"
483 ":rtype: None or str";
484 
489 static PyObject *Atrinik_Player_Examine(Atrinik_Player *self, PyObject *args)
490 {
492  int ret = 0;
493 
494  if (!PyArg_ParseTuple(args, "O!|i", &Atrinik_ObjectType, &obj, &ret)) {
495  return NULL;
496  }
497 
498  StringBuffer *sb_capture = NULL;
499  if (ret) {
500  sb_capture = hooks->stringbuffer_new();
501  }
502 
503  hooks->examine(self->pl->ob, obj->obj, sb_capture);
504 
505  if (ret) {
506  char *cp = hooks->stringbuffer_finish(sb_capture);
507  PyObject *retval = Py_BuildValue("s", cp);
508  efree(cp);
509 
510  return retval;
511  }
512 
513  Py_INCREF(Py_None);
514  return Py_None;
515 }
516 
518 static const char doc_Atrinik_Player_SendPacket[] =
519 ".. method:: SendPacket(command, format, *args).\n\n"
520 "Constructs and sends a packet to the player's client.\n\n"
521 ":param command: The command ID.\n"
522 ":type command: int\n"
523 ":param format: Format specifier. For example, 'Bs' would imply uint8_t + "
524 "string data, and the format specifier would need to be followed by an integer "
525 "that is within uint8_t data range and a string. Allowed format specifiers "
526 "are:\n\n"
527 " * **b**: 8-bit signed int (int8_t)\n"
528 " * **B**: 8-bit unsigned int (uint8_t)\n"
529 " * **h**: 16-bit signed int (int16_t)\n"
530 " * **H**: 16-bit unsigned int (uint16_t)\n"
531 " * **i**: 32-bit signed int (int32_t)\n"
532 " * **I**: 32-bit unsigned int (uint32_t)\n"
533 " * **l**: 64-bit signed int (int64_t)\n"
534 " * **L**: 64-bit unsigned int (uint64_t)\n"
535 " * **s**: String (automatically NUL-terminated)\n"
536 " * **x**: Bytes (NOT NUL-terminated)\n"
537 ":type format: str\n"
538 ":param \\*args: Rest of the arguments is converted into the packet data as "
539 "specified by the format string.\n"
540 ":raises OverflowError: If *command* is not within a valid range.\n"
541 ":raises ValueError: If an unrecognized character is encountered in "
542 "*format*.\n"
543 ":raises TypeError: If an object's type in *args* does not match what was "
544 "specified in *format*.\n"
545 ":raises OverflowError: If an integer specified in *args* is not within a "
546 "valid range.";
547 
552 static PyObject *Atrinik_Player_SendPacket(Atrinik_Player *self, PyObject *args)
553 {
554  /* Must have at least 3 arguments. */
555  if (PyTuple_Size(args) < 3) {
556  PyErr_SetString(PyExc_TypeError, "Insufficient number of arguments.");
557  return NULL;
558  }
559 
560  /* The first argument must be an integer. */
561  if (!PyInt_Check(PyTuple_GET_ITEM(args, 0))) {
562  PyErr_SetString(PyExc_TypeError,
563  "Illegal object type for 'cmd' function argument.");
564  return NULL;
565  }
566 
567  long cmd = PyLong_AsLong(PyTuple_GET_ITEM(args, 0));
568  /* It also must be uint8. */
569  if (cmd < 0 || cmd > UINT8_MAX) {
570  PyErr_SetString(PyExc_OverflowError,
571  "Invalid value for 'cmd' function argument.");
572  return NULL;
573  }
574 
575  /* Get the format specifier. */
576  char *format = PyString_AsString(PyTuple_GET_ITEM(args, 1));
577 
578  packet_struct *packet = hooks->packet_new(cmd, 256, 512);
579 
580 #define CHECK_INT_RANGE(min, max) \
581  if (PyErr_Occurred()) { \
582  PyErr_Format(PyExc_OverflowError, \
583  "Invalid integer value for '%c' format specifier.", \
584  format[i]); \
585  goto error; \
586  } else if (val < min || val > max) { \
587  PyErr_Format(PyExc_OverflowError, \
588  "Invalid integer value for '%c' format specifier.", \
589  format[i]); \
590  goto error; \
591  }
592 #define CHECK_UINT_RANGE(max) \
593  if (PyErr_Occurred()) { \
594  PyErr_Format(PyExc_OverflowError, \
595  "Invalid integer value for '%c' format specifier.", \
596  format[i]); \
597  goto error; \
598  } else if (val > max) { \
599  PyErr_Format(PyExc_OverflowError, \
600  "Invalid integer value for '%c' format specifier.", \
601  format[i]); \
602  goto error; \
603  }
604 
605  for (size_t i = 0; format[i] != '\0'; i++) {
606  PyObject *value = PyTuple_GetItem(args, 2 + i);
607  if (value == NULL) {
608  PyErr_SetString(PyExc_ValueError,
609  "Insufficient number of arguments.");
610  goto error;
611  }
612 
613  if (format[i] == 'b') {
614  if (PyInt_Check(value)) {
615  long val = PyLong_AsLong(value);
616  CHECK_INT_RANGE(INT8_MIN, INT8_MAX);
617  hooks->packet_append_int8(packet, val);
618  continue;
619  }
620  } else if (format[i] == 'B') {
621  if (PyInt_Check(value)) {
622  long val = PyLong_AsLong(value);
623  CHECK_INT_RANGE(0, UINT8_MAX);
624  hooks->packet_append_uint8(packet, val);
625  continue;
626  }
627  } else if (format[i] == 'h') {
628  if (PyInt_Check(value)) {
629  long val = PyLong_AsLong(value);
630  CHECK_INT_RANGE(INT16_MIN, INT16_MAX);
631  hooks->packet_append_int16(packet, val);
632  continue;
633  }
634  } else if (format[i] == 'H') {
635  if (PyInt_Check(value)) {
636  long val = PyLong_AsLong(value);
637  CHECK_INT_RANGE(0, UINT16_MAX);
638  hooks->packet_append_uint16(packet, val);
639  continue;
640  }
641  } else if (format[i] == 'i') {
642  if (PyInt_Check(value)) {
643  PY_LONG_LONG val = PyLong_AsLongLong(value);
644  CHECK_INT_RANGE(INT32_MIN, INT32_MAX);
645  hooks->packet_append_int32(packet, val);
646  continue;
647  }
648  } else if (format[i] == 'I') {
649  if (PyInt_Check(value)) {
650  unsigned PY_LONG_LONG val = PyLong_AsUnsignedLongLong(value);
651  CHECK_UINT_RANGE(UINT32_MAX);
652  hooks->packet_append_uint32(packet, val);
653  continue;
654  }
655  } else if (format[i] == 'l') {
656  if (PyInt_Check(value)) {
657  PY_LONG_LONG val = PyLong_AsLongLong(value);
658  if (PyErr_Occurred()) {
659  PyErr_Format(PyExc_OverflowError,
660  "Invalid integer value for '%c' format specifier.",
661  format[i]);
662  goto error;
663  }
664 
665  hooks->packet_append_int64(packet, val);
666  continue;
667  }
668  } else if (format[i] == 'L') {
669  if (PyInt_Check(value)) {
670  unsigned PY_LONG_LONG val = PyLong_AsUnsignedLongLong(value);
671  if (PyErr_Occurred()) {
672  PyErr_Format(PyExc_OverflowError,
673  "Invalid integer value for '%c' format specifier.",
674  format[i]);
675  goto error;
676  }
677 
678  hooks->packet_append_uint64(packet, val);
679  continue;
680  }
681  } else if (format[i] == 's') {
682  if (PyString_Check(value)) {
683  Py_ssize_t size;
684 #ifdef IS_PY3K
685  char *data = PyUnicode_AsUTF8AndSize(value, &size);
686 #else
687  char *data = NULL;
688  if (PyString_AsStringAndSize(value, &data, &size) == -1 ||
689  data == NULL) {
690  PyErr_Format(PyExc_ValueError,
691  "PyString_AsStringAndSize() failed for "
692  "format '%c'.", format[i]);
693  }
694 #endif
695  hooks->packet_append_string_len_terminated(packet, data, size);
696  continue;
697  }
698  } else if (format[i] == 'x') {
699  if (PyBytes_Check(value)) {
700  hooks->packet_append_data_len(packet,
701  (uint8_t *) PyBytes_AsString(value),
702  PyBytes_Size(value));
703  continue;
704  }
705  } else {
706  PyErr_Format(PyExc_ValueError,
707  "Illegal format specifier '%c'.", format[i]);
708  goto error;
709  }
710 
711  PyErr_Format(PyExc_TypeError,
712  "Illegal object type for '%c' format specifier.", format[i]);
713  goto error;
714  }
715 
716 #undef CHECK_INT_RANGE
717 #undef CHECK_UINT_RANGE
718 
719  hooks->socket_send_packet(self->pl->cs, packet);
720 
721  Py_INCREF(Py_None);
722  return Py_None;
723 
724 error:
725  hooks->packet_free(packet);
726  return NULL;
727 }
728 
730 static const char doc_Atrinik_Player_DrawInfo[] =
731 ".. method:: DrawInfo(message, color=Atrinik.COLOR_ORANGE, "
732 "type=Atrinik.CHAT_TYPE_GAME, broadcast=False, name=None).\n\n"
733 "Sends a message to the player.\n\n"
734 ":param message: The message to send.\n"
735 ":type message: str\n"
736 ":param color: Color to use for the message. Can be one of the COLOR_xxx "
737 "constants (eg, :attr:`~Atrinik.COLOR_RED`) or a regular HTML color notation "
738 "(eg, '00ff00')\n"
739 ":type color: str\n"
740 ":param type: One of the CHAT_TYPE_xxx constants, eg, :attr:"
741 "`~Atrinik.CHAT_TYPE_CHAT`.\n"
742 ":type type: int\n"
743 ":param broadcast: If True, the message will be broadcast to all players.\n"
744 ":type broadcast: bool\n"
745 ":param name: Player name that is the source of this message, if applicable. "
746 "If None and *type* is not :attr:`~Atrinik.CHAT_TYPE_GAME`, :attr:"
747 "`Atrinik.Player.Player.ob.name` will be used.\n"
748 ":type name: str or None";
749 
754 static PyObject *Atrinik_Player_DrawInfo(Atrinik_Player *self, PyObject *args,
755  PyObject *keywds)
756 {
757  static char *kwlist[] = {
758  "message", "color", "type", "broadcast", "name", NULL
759  };
760  const char *message, *color, *name;
761  uint8_t type, broadcast;
762 
763  color = COLOR_ORANGE;
764  type = CHAT_TYPE_GAME;
765  broadcast = 0;
766  name = NULL;
767 
768  if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|sbbz", kwlist,
769  &message, &color, &type, &broadcast, &name)) {
770  return NULL;
771  }
772 
773  if (name == NULL && type != CHAT_TYPE_GAME) {
774  name = self->pl->ob->name;
775  }
776 
777  hooks->draw_info_type(type, name, color, broadcast ? NULL : self->pl->ob,
778  message);
779 
780  Py_INCREF(Py_None);
781  return Py_None;
782 }
783 
786 ".. method:: FactionGetBounty(faction).\n\n"
787 "Acquires player's bounty for the specified faction.\n\n"
788 ":param faction: The faction name.\n"
789 ":type faction: str\n"
790 ":returns: Player's bounty in the specified faction.\n"
791 ":rtype: float\n"
792 ":raises Atrinik.AtrinikError: If the specified faction doesn't exist.";
793 
799  PyObject *args)
800 {
801  const char *name;
802 
803  if (!PyArg_ParseTuple(args, "s", &name)) {
804  return NULL;
805  }
806 
807  shstr *sh_name = hooks->find_string(name);
808  if (sh_name == NULL) {
809  PyErr_Format(AtrinikError, "No such faction: %s", name);
810  return NULL;
811  }
812 
813  faction_t faction = hooks->faction_find(sh_name);
814  if (faction == NULL) {
815  PyErr_Format(AtrinikError, "No such faction: %s", name);
816  return NULL;
817  }
818 
819  return Py_BuildValue("d", hooks->faction_get_bounty(faction, self->pl));
820 }
821 
824 ".. method:: FactionClearBounty(faction).\n\n"
825 "Clear player's bounty for the specified faction.\n\n"
826 ":param faction: The faction name.\n"
827 ":type faction: str\n"
828 ":raises Atrinik.AtrinikError: If the specified faction doesn't exist";
829 
835  PyObject *args)
836 {
837  const char *name;
838 
839  if (!PyArg_ParseTuple(args, "s", &name)) {
840  return NULL;
841  }
842 
843  shstr *sh_name = hooks->find_string(name);
844  if (sh_name == NULL) {
845  PyErr_Format(AtrinikError, "No such faction: %s", name);
846  return NULL;
847  }
848 
849  faction_t faction = hooks->faction_find(sh_name);
850  if (faction == NULL) {
851  PyErr_Format(AtrinikError, "No such faction: %s", name);
852  return NULL;
853  }
854 
855  hooks->faction_clear_bounty(faction, self->pl);
856 
857  Py_INCREF(Py_None);
858  return Py_None;
859 }
860 
862 static const char doc_Atrinik_Player_InsertCoins[] =
863 ".. method:: InsertCoins(value).\n\n"
864 "Gives coins of the specified value to the player.\n\n"
865 ":param value: The value.\n"
866 ":type value: int";
867 
873  PyObject *args)
874 {
875  int64_t value;
876 
877  if (!PyArg_ParseTuple(args, "L", &value)) {
878  return NULL;
879  }
880 
881  hooks->shop_insert_coins(self->pl->ob, value);
882 
883  Py_INCREF(Py_None);
884  return Py_None;
885 }
886 
888 static const char doc_Atrinik_Player_Save[] =
889 ".. method:: Save().\n\n"
890 "Saves the player.\n\n";
891 
896 static PyObject *Atrinik_Player_Save(Atrinik_Player *self)
897 {
898  hooks->player_save(self->pl->ob);
899 
900  Py_INCREF(Py_None);
901  return Py_None;
902 }
903 
905 static const char doc_Atrinik_Player_Address[] =
906 ".. method:: Address(verbose=False).\n\n"
907 "Acquires the player's IP address.\n\n"
908 ":param verbose: If True, will contain the port as well.\n"
909 ":type verbose: bool\n"
910 ":returns: The player's IP address.\n"
911 ":rtype: str";
912 
917 static PyObject *Atrinik_Player_Address(Atrinik_Player *self, PyObject *args)
918 {
919  int verbose = 0;
920  if (!PyArg_ParseTuple(args, "|i", &verbose)) {
921  return NULL;
922  }
923 
924  if (!verbose) {
925  return Py_BuildValue("s", hooks->socket_get_addr(self->pl->cs->sc));
926  }
927 
928  return Py_BuildValue("s", hooks->socket_get_str(self->pl->cs->sc));
929 }
930 
932 static PyMethodDef methods[] = {
933  {"GetEquipment", (PyCFunction) Atrinik_Player_GetEquipment, METH_VARARGS,
934  doc_Atrinik_Player_GetEquipment},
935  {"CanCarry", (PyCFunction) Atrinik_Player_CanCarry, METH_O,
936  doc_Atrinik_Player_CanCarry},
937  {"AddExp", (PyCFunction) Atrinik_Player_AddExp, METH_VARARGS,
938  doc_Atrinik_Player_AddExp},
939  {"BankDeposit", (PyCFunction) Atrinik_Player_BankDeposit, METH_VARARGS,
940  doc_Atrinik_Player_BankDeposit},
941  {"BankWithdraw", (PyCFunction) Atrinik_Player_BankWithdraw, METH_VARARGS,
942  doc_Atrinik_Player_BankWithdraw},
943  {"BankBalance", (PyCFunction) Atrinik_Player_BankBalance, METH_NOARGS,
944  doc_Atrinik_Player_BankBalance},
945  {"SwapApartments", (PyCFunction) Atrinik_Player_SwapApartments,
946  METH_VARARGS, doc_Atrinik_Player_SwapApartments},
947  {"ExecuteCommand", (PyCFunction) Atrinik_Player_ExecuteCommand,
948  METH_VARARGS, doc_Atrinik_Player_ExecuteCommand},
949  {"FindMarkedObject", (PyCFunction) Atrinik_Player_FindMarkedObject,
950  METH_NOARGS, doc_Atrinik_Player_FindMarkedObject},
951  {"Sound", (PyCFunction) Atrinik_Player_Sound, METH_VARARGS | METH_KEYWORDS,
952  doc_Atrinik_Player_Sound},
953  {"Examine", (PyCFunction) Atrinik_Player_Examine, METH_VARARGS,
954  doc_Atrinik_Player_Examine},
955  {"SendPacket", (PyCFunction) Atrinik_Player_SendPacket, METH_VARARGS,
956  doc_Atrinik_Player_SendPacket},
957  {"DrawInfo", (PyCFunction) Atrinik_Player_DrawInfo,
958  METH_VARARGS | METH_KEYWORDS, doc_Atrinik_Player_DrawInfo},
959  {"FactionGetBounty", (PyCFunction) Atrinik_Player_FactionGetBounty,
960  METH_VARARGS, doc_Atrinik_Player_FactionGetBounty},
961  {"FactionClearBounty", (PyCFunction) Atrinik_Player_FactionClearBounty,
962  METH_VARARGS, doc_Atrinik_Player_FactionClearBounty},
963  {"InsertCoins", (PyCFunction) Atrinik_Player_InsertCoins, METH_VARARGS,
964  doc_Atrinik_Player_InsertCoins},
965  {"Save", (PyCFunction) Atrinik_Player_Save, METH_NOARGS,
966  doc_Atrinik_Player_Save},
967  {"Address", (PyCFunction) Atrinik_Player_Address, METH_VARARGS,
968  doc_Atrinik_Player_Address},
969 
970  {NULL, NULL, 0, NULL}
971 };
972 
985 static void
987  const fields_struct *field_orig)
988 {
989  field->offset = field_orig->extra_data;
990 
991  if (field->offset == offsetof(socket_struct, packets)) {
992  field->extra_data = FIELDTYPE_PACKETS;
993  } else {
994  field->extra_data = 0;
995  }
996 }
997 
1007 static PyObject *get_attribute(Atrinik_Player *pl, void *context)
1008 {
1009  fields_struct *field = context;
1010 
1011  if (field->offset == offsetof(player, cs)) {
1012  fields_struct field_tmp = *field;
1013  resolve_client_socket_field(&field_tmp, field);
1014  return generic_field_getter(&field_tmp, pl->pl->cs);
1015  }
1016 
1017  return generic_field_getter(field, pl->pl);
1018 }
1019 
1031 static int set_attribute(Atrinik_Player *pl, PyObject *value, void *context)
1032 {
1033  fields_struct *field = context;
1034 
1035  if (field->offset == offsetof(player, cs)) {
1036  fields_struct field_tmp = *field;
1037  resolve_client_socket_field(&field_tmp, field);
1038  return generic_field_setter(&field_tmp, pl->pl->cs, value);
1039  }
1040 
1041  if (generic_field_setter(field, pl->pl, value) == -1) {
1042  return -1;
1043  }
1044 
1045  if (field->offset == offsetof(player, target_object)) {
1046  hooks->send_target_command(pl->pl);
1047  }
1048 
1049  return 0;
1050 }
1051 
1063 static PyObject *Atrinik_Player_new(PyTypeObject *type, PyObject *args,
1064  PyObject *kwds)
1065 {
1066  Atrinik_Player *pl = (Atrinik_Player *) type->tp_alloc(type, 0);
1067  if (pl != NULL) {
1068  pl->pl = NULL;
1069  }
1070 
1071  return (PyObject *) pl;
1072 }
1073 
1080 {
1081  pl->pl = NULL;
1082 #ifndef IS_PY_LEGACY
1083  Py_TYPE(pl)->tp_free((PyObject *) pl);
1084 #else
1085  pl->ob_type->tp_free((PyObject *) pl);
1086 #endif
1087 }
1088 
1096 static PyObject *Atrinik_Player_str(Atrinik_Player *pl)
1097 {
1098  return Py_BuildValue("s", pl->pl->ob->name);
1099 }
1100 
1101 static int Atrinik_Player_InternalCompare(Atrinik_Player *left,
1102  Atrinik_Player *right)
1103 {
1104  return (left->pl < right->pl ? -1 : (left->pl == right->pl ? 0 : 1));
1105 }
1106 
1107 static PyObject *Atrinik_Player_RichCompare(Atrinik_Player *left,
1108  Atrinik_Player *right, int op)
1109 {
1110  if (left == NULL || right == NULL ||
1111  !PyObject_TypeCheck((PyObject *) left, &Atrinik_PlayerType) ||
1112  !PyObject_TypeCheck((PyObject *) right, &Atrinik_PlayerType)) {
1113  Py_INCREF(Py_NotImplemented);
1114  return Py_NotImplemented;
1115  }
1116 
1117  return generic_rich_compare(op,
1118  Atrinik_Player_InternalCompare(left, right));
1119 }
1120 
1124 static PyGetSetDef getseters[NUM_FIELDS + 1];
1125 
1129 PyTypeObject Atrinik_PlayerType = {
1130 #ifdef IS_PY3K
1131  PyVarObject_HEAD_INIT(NULL, 0)
1132 #else
1133  PyObject_HEAD_INIT(NULL)
1134  0,
1135 #endif
1136  "Atrinik.Player",
1137  sizeof(Atrinik_Player),
1138  0,
1139  (destructor) Atrinik_Player_dealloc,
1140  NULL, NULL, NULL,
1141 #ifdef IS_PY3K
1142  NULL,
1143 #else
1144  (cmpfunc) Atrinik_Player_InternalCompare,
1145 #endif
1146  0, 0, 0, 0, 0, 0,
1147  (reprfunc) Atrinik_Player_str,
1148  0, 0, 0,
1149  Py_TPFLAGS_DEFAULT,
1150  "Atrinik Player class.\n\n"
1151  "To access object's player controller, you can use something like::\n\n"
1152  " activator = Atrinik.WhoIsActivator()\n"
1153  " player = activator.Controller()\n\n"
1154  "In the above example, player points to the player structure (which Python "
1155  "is wrapping) that is controlling the object *activator*. In this way, you "
1156  "can, for example, use something like this to get player's save bed, among "
1157  "other things::\n\n"
1158  " print(Atrinik.WhoIsActivator().Controller().savebed_map)\n\n",
1159  NULL, NULL,
1160  (richcmpfunc) Atrinik_Player_RichCompare,
1161  0, 0, 0,
1162  methods,
1163  0,
1164  getseters,
1165  0, 0, 0, 0, 0, 0, 0,
1167  0, 0, 0, 0, 0, 0, 0, 0
1168 #ifndef IS_PY_LEGACY
1169  , 0
1170 #endif
1171 #ifdef Py_TPFLAGS_HAVE_FINALIZE
1172  , NULL
1173 #endif
1174 };
1175 
1183 int Atrinik_Player_init(PyObject *module)
1184 {
1185  size_t i;
1186 
1187  /* Field getters */
1188  for (i = 0; i < NUM_FIELDS; i++) {
1189  PyGetSetDef *def = &getseters[i];
1190 
1191  def->name = fields[i].name;
1192  def->get = (getter) get_attribute;
1193  def->set = (setter) set_attribute;
1194  def->doc = fields[i].doc;
1195  def->closure = &fields[i];
1196  }
1197 
1198  getseters[i].name = NULL;
1199 
1200  Atrinik_PlayerType.tp_new = PyType_GenericNew;
1201 
1202  if (PyType_Ready(&Atrinik_PlayerType) < 0) {
1203  return 0;
1204  }
1205 
1206  Py_INCREF(&Atrinik_PlayerType);
1207  PyModule_AddObject(module, "Player", (PyObject *) &Atrinik_PlayerType);
1208 
1209  return 1;
1210 }
1211 
1219 PyObject *wrap_player(player *pl)
1220 {
1221  /* Return None if no player was to be wrapped. */
1222  if (pl == NULL) {
1223  Py_INCREF(Py_None);
1224  return Py_None;
1225  }
1226 
1227  Atrinik_Player *wrapper = PyObject_NEW(Atrinik_Player, &Atrinik_PlayerType);
1228  if (wrapper != NULL) {
1229  wrapper->pl = pl;
1230  }
1231 
1232  return (PyObject *) wrapper;
1233 }
PyTypeObject Atrinik_PlayerType
PyTypeObject Atrinik_ObjectType
Definition: object.h:94
static PyObject * Atrinik_Player_Sound(Atrinik_Player *self, PyObject *args, PyObject *keywds)
static fields_struct fields[]
static PyObject * Atrinik_Player_FactionGetBounty(Atrinik_Player *self, PyObject *args)
static const char doc_Atrinik_Player_Save[]
static PyObject * Atrinik_Player_CanCarry(Atrinik_Player *self, PyObject *what)
static const char doc_Atrinik_Player_InsertCoins[]
static PyObject * Atrinik_Player_FindMarkedObject(Atrinik_Player *self)
struct plugin_hooklist * hooks
Definition: plugin_arena.c:160
uint8_t type
One of operation types.
Definition: sound_ambient.c:45
#define Py_BuildBoolean(val)
static const char doc_Atrinik_Player_BankDeposit[]
static const char doc_Atrinik_Player_AddExp[]
static PyObject * get_attribute(Atrinik_Player *pl, void *context)
object * ob
Definition: player.h:185
int generic_field_setter(fields_struct *field, void *ptr, PyObject *value)
static PyObject * Atrinik_Player_AddExp(Atrinik_Player *self, PyObject *args)
PyObject * generic_rich_compare(int op, int result)
static int set_attribute(Atrinik_Player *pl, PyObject *value, void *context)
static PyObject * Atrinik_Player_SendPacket(Atrinik_Player *self, PyObject *args)
PyObject * wrap_object(object *what)
static PyObject * Atrinik_Player_str(Atrinik_Player *pl)
static PyObject * Atrinik_Player_SwapApartments(Atrinik_Player *self, PyObject *args)
static PyObject * Atrinik_Player_BankDeposit(Atrinik_Player *self, PyObject *args)
#define IS_PY_LEGACY
Definition: plugin_python.h:55
static PyMethodDef methods[]
PyObject * AtrinikError
Definition: plugin_python.c:55
static PyObject * Atrinik_Player_InsertCoins(Atrinik_Player *self, PyObject *args)
uint32_t extra_data
struct sound_ambient_match * next
Next match rule in a linked list.
Definition: sound_ambient.c:39
static const char doc_Atrinik_Player_BankWithdraw[]
static const char doc_Atrinik_Player_Sound[]
#define FIELDFLAG_READONLY
#define NUM_FIELDS
union @21 data
Data about the rule.
const char * name
Definition: object.h:168
static const char doc_Atrinik_Player_SwapApartments[]
uint32_t nrof
Definition: object.h:264
static PyObject * Atrinik_Player_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static const char doc_Atrinik_Player_FactionClearBounty[]
static PyObject * Atrinik_Player_GetEquipment(Atrinik_Player *self, PyObject *args)
int Atrinik_Player_init(PyObject *module)
static const char doc_Atrinik_Player_FindMarkedObject[]
static const char doc_Atrinik_Player_BankBalance[]
PyObject_HEAD player * pl
static void Atrinik_Player_dealloc(Atrinik_Player *pl)
#define MAXLEVEL
Definition: global.h:221
static const char doc_Atrinik_Player_ExecuteCommand[]
static PyObject * Atrinik_Player_FactionClearBounty(Atrinik_Player *self, PyObject *args)
static const char doc_Atrinik_Player_Address[]
PyObject * generic_field_getter(fields_struct *field, void *ptr)
static PyObject * Atrinik_Player_BankBalance(Atrinik_Player *self)
static PyObject * Atrinik_Player_DrawInfo(Atrinik_Player *self, PyObject *args, PyObject *keywds)
socket_struct * cs
Definition: player.h:148
static PyGetSetDef getseters[NUM_FIELDS+1]
static const char doc_Atrinik_Player_GetEquipment[]
static PyObject * Atrinik_Player_Address(Atrinik_Player *self, PyObject *args)
static faction_t factions
Definition: faction.c:132
static const char doc_Atrinik_Player_DrawInfo[]
PyObject * wrap_player(player *pl)
static const char doc_Atrinik_Player_Examine[]
static const char doc_Atrinik_Player_CanCarry[]
static PyObject * Atrinik_Player_BankWithdraw(Atrinik_Player *self, PyObject *args)
static PyObject * Atrinik_Player_Examine(Atrinik_Player *self, PyObject *args)
static const char doc_Atrinik_Player_FactionGetBounty[]
static const char doc_Atrinik_Player_SendPacket[]
static PyObject * Atrinik_Player_Save(Atrinik_Player *self)
static PyObject * Atrinik_Player_ExecuteCommand(Atrinik_Player *self, PyObject *args)
PyObject_HEAD object * obj
static void resolve_client_socket_field(fields_struct *field, const fields_struct *field_orig)