Atrinik Server 2.5
plugins/plugin_python/attr_list.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00062 #include <plugin_python.h>
00063 
00070 static void *attr_list_len_ptr(Atrinik_AttrList *al)
00071 {
00072     if (al->field == FIELDTYPE_KNOWN_SPELLS)
00073     {
00074         return (uint16 *) ((void *) ((char *) al->ptr + offsetof(player, nrofknownspells)));
00075     }
00076     else if (al->field == FIELDTYPE_CMD_PERMISSIONS)
00077     {
00078         return (int *) ((void *) ((char *) al->ptr + offsetof(player, num_cmd_permissions)));
00079     }
00080     else if (al->field == FIELDTYPE_FACTIONS)
00081     {
00082         return (int *) ((void *) ((char *) al->ptr + offsetof(player, num_faction_ids)));
00083     }
00084 
00085     /* Not reached. */
00086     return NULL;
00087 }
00088 
00093 static unsigned PY_LONG_LONG attr_list_len(Atrinik_AttrList *al)
00094 {
00095     if (al->field == FIELDTYPE_KNOWN_SPELLS)
00096     {
00097         return *(uint16 *) attr_list_len_ptr(al);
00098     }
00099     else if (al->field == FIELDTYPE_CMD_PERMISSIONS || al->field == FIELDTYPE_FACTIONS)
00100     {
00101         return *(int *) attr_list_len_ptr(al);
00102     }
00103 
00104     return 0;
00105 }
00106 
00112 static PyObject *attr_list_get(Atrinik_AttrList *al, void *idx)
00113 {
00114     void *ptr;
00115     fields_struct field = {"xxx", 0, 0, 0, 0};
00116 
00117     ptr = (void *) ((char *) al->ptr + al->offset);
00118 
00119     /* Known spells; cast to sint16. */
00120     if (al->field == FIELDTYPE_KNOWN_SPELLS)
00121     {
00122         field.type = FIELDTYPE_SINT16;
00123         ptr = &((sint16 *) ptr)[*(unsigned PY_LONG_LONG *) idx];
00124     }
00125     else if (al->field == FIELDTYPE_CMD_PERMISSIONS)
00126     {
00127         field.type = FIELDTYPE_CSTR;
00128         ptr = &(*(char ***) ptr)[*(unsigned PY_LONG_LONG *) idx];
00129     }
00130     else if (al->field == FIELDTYPE_FACTIONS)
00131     {
00132         unsigned PY_LONG_LONG len, i;
00133 
00134         len = attr_list_len(al);
00135         field.type = FIELDTYPE_SINT64;
00136 
00137         for (i = 0; i < len; i++)
00138         {
00139             if (!strcmp((const char *) (char *) idx, *(const char **) (&(*(shstr ***) ptr)[i])))
00140             {
00141                 ptr = &(*(sint64 **) ((void *) ((char *) al->ptr + offsetof(player, faction_reputation))))[i];
00142                 return generic_field_getter(&field, ptr);
00143             }
00144         }
00145 
00146         Py_INCREF(Py_None);
00147         return Py_None;
00148     }
00149 
00150     return generic_field_getter(&field, ptr);
00151 }
00152 
00158 static int attr_list_contains(Atrinik_AttrList *al, PyObject *value)
00159 {
00160     unsigned PY_LONG_LONG i, len;
00161     PyObject *check;
00162 
00163     if (al->field == FIELDTYPE_FACTIONS)
00164     {
00165         PyErr_SetString(PyExc_NotImplementedError, "Attribute list does not implement contains method.");
00166         return -1;
00167     }
00168 
00169     len = attr_list_len(al);
00170 
00171     for (i = 0; i < len; i++)
00172     {
00173         /* attr_list_get() creates a new reference, so make sure to decrease
00174          * it later. */
00175         check = attr_list_get(al, &i);
00176 
00177         /* Compare the two objects... */
00178         if (PyObject_RichCompareBool(check, value, Py_EQ) == 1)
00179         {
00180             Py_DECREF(check);
00181             return 1;
00182         }
00183 
00184         Py_DECREF(check);
00185     }
00186 
00187     return 0;
00188 }
00189 
00196 static int attr_list_set(Atrinik_AttrList *al, void *idx, PyObject *value)
00197 {
00198     unsigned PY_LONG_LONG len;
00199     void *ptr;
00200     fields_struct field = {"xxx", 0, 0, 0, 0};
00201     int ret;
00202     unsigned PY_LONG_LONG i;
00203 
00204     /* Get the current length of the list. */
00205     len = attr_list_len(al);
00206     ptr = (void *) ((char *) al->ptr + al->offset);
00207 
00208     /* Known spells array. */
00209     if (al->field == FIELDTYPE_KNOWN_SPELLS)
00210     {
00211         i = *(unsigned PY_LONG_LONG *) idx;
00212 
00213         /* Known spells array is fixed size; cannot go over the maximum. */
00214         if (len >= sizeof(((player *) NULL)->known_spells))
00215         {
00216             PyErr_SetString(PyExc_OverflowError, "Overflow error.");
00217             return -1;
00218         }
00219 
00220         /* Make sure the value is not already in the array, to prevent the
00221          * the array from filling up with duplicate entries. */
00222         if (attr_list_contains(al, value))
00223         {
00224             PyErr_SetString(PyExc_ValueError, "Value is already inside the array.");
00225             return -1;
00226         }
00227 
00228         field.type = FIELDTYPE_SINT16;
00229         ptr = &((sint16 *) ptr)[i];
00230     }
00231     /* Command permissions. */
00232     else if (al->field == FIELDTYPE_CMD_PERMISSIONS)
00233     {
00234         i = *(unsigned PY_LONG_LONG *) idx;
00235 
00236         /* Over the maximum size; resize the array, as it's dynamic. */
00237         if (i >= len)
00238         {
00239             /* Increase the number of commands... */
00240             (*(int *) attr_list_len_ptr(al))++;
00241             /* And resize it. */
00242             *(char ***) ((void *) ((char *) al->ptr + al->offset)) = realloc(*(char ***) ((void *) ((char *) al->ptr + al->offset)), sizeof(char *) * attr_list_len(al));
00243             /* Make sure ptr points to the right memory... */
00244             ptr = (void *) ((char *) al->ptr + al->offset);
00245             /* NULL the new member. */
00246             (*(char ***) ptr)[i] = NULL;
00247         }
00248 
00249         field.type = FIELDTYPE_CSTR;
00250         ptr = &(*(char ***) ptr)[i];
00251     }
00252     /* Factions. */
00253     else if (al->field == FIELDTYPE_FACTIONS)
00254     {
00255         field.type = FIELDTYPE_SINT64;
00256 
00257         /* Try to find an existing entry. */
00258         for (i = 0; i < len; i++)
00259         {
00260             if (!strcmp((const char *) (char *) idx, *(const char **) (&(*(shstr ***) ptr)[i])))
00261             {
00262                 break;
00263             }
00264         }
00265 
00266         /* Doesn't exist, create it. */
00267         if (i == len)
00268         {
00269             (*(int *) attr_list_len_ptr(al))++;
00270             *(shstr ***) ((void *) ((char *) al->ptr + al->offset)) = realloc(*(shstr ***) ((void *) ((char *) al->ptr + al->offset)), sizeof(shstr *) * attr_list_len(al));
00271             *(shstr **) (&(*(shstr ***) ptr)[i]) = hooks->add_string((const char *) (char *) idx);
00272             *(sint64 **) ((void *) ((char *) al->ptr + offsetof(player, faction_reputation))) = realloc(*(sint64 **) ((void *) ((char *) al->ptr + offsetof(player, faction_reputation))), sizeof(sint64) * attr_list_len(al));
00273             /* Make sure ptr points to the right memory... */
00274             ptr = (void *) ((char *) al->ptr + offsetof(player, faction_reputation));
00275             /* NULL the new member. */
00276             (*(sint64 **) ptr)[i] = 0;
00277         }
00278 
00279         ptr = &(*(sint64 **) ((void *) ((char *) al->ptr + offsetof(player, faction_reputation))))[i];
00280     }
00281     else
00282     {
00283         PyErr_SetString(PyExc_NotImplementedError, "The attribute list does not implement support for write operations.");
00284         return -1;
00285     }
00286 
00287     ret = generic_field_setter(&field, ptr, value);
00288 
00289     /* Success! */
00290     if (ret == 0)
00291     {
00292         /* Increased known spells successfully, so increase the total
00293          * number of known spells as well (if we were adding a new one,
00294          * that is). */
00295         if (al->field == FIELDTYPE_KNOWN_SPELLS && i >= len)
00296         {
00297             (*(uint16 *) attr_list_len_ptr(al))++;
00298         }
00299     }
00300     /* Failure; overflow, invalid value or some other kind of error. */
00301     else if (ret == -1)
00302     {
00303         if (i >= len)
00304         {
00305             /* We tried to add a new command permission and we have already
00306              * resized the array, so shrink it back now, as we failed. */
00307             if (al->field == FIELDTYPE_CMD_PERMISSIONS)
00308             {
00309                 /* Decrease the number of commands... */
00310                 (*(int *) attr_list_len_ptr(al))--;
00311                 /* And resize it. */
00312                 *(char ***) ((void *) ((char *) al->ptr + al->offset)) = realloc(*(char ***) ((void *) ((char *) al->ptr + al->offset)), sizeof(char *) * attr_list_len(al));
00313             }
00314             else if (al->field == FIELDTYPE_FACTIONS)
00315             {
00316                 (*(int *) attr_list_len_ptr(al))--;
00317                 *(shstr ***) ((void *) ((char *) al->ptr + al->offset)) = realloc(*(shstr ***) ((void *) ((char *) al->ptr + al->offset)), sizeof(shstr *) * attr_list_len(al));
00318                 *(sint64 **) ((void *) ((char *) al->ptr + offsetof(player, faction_reputation))) = realloc(*(sint64 **) ((void *) ((char *) al->ptr + offsetof(player, faction_reputation))), sizeof(sint64) * attr_list_len(al));
00319             }
00320         }
00321     }
00322 
00323     return ret;
00324 }
00325 
00332 static PyObject *__getitem__(Atrinik_AttrList *al, PyObject *key)
00333 {
00334     void *idx;
00335 
00336     if (al->field == FIELDTYPE_FACTIONS)
00337     {
00338         char *cstr;
00339 
00340         if (!PyString_Check(key))
00341         {
00342             PyErr_SetString(PyExc_ValueError, "__getitem__() failed; key must be a string.");
00343             return NULL;
00344         }
00345 
00346         cstr = PyString_AsString(key);
00347         idx = cstr;
00348     }
00349     else
00350     {
00351         unsigned PY_LONG_LONG i, len;
00352 
00353         /* The key must be an integer. */
00354         if (!PyInt_Check(key))
00355         {
00356             PyErr_SetString(PyExc_ValueError, "__getitem__() failed; key must be an integer.");
00357             return NULL;
00358         }
00359 
00360         i = PyLong_AsUnsignedLongLong(key);
00361 
00362         if (PyErr_Occurred())
00363         {
00364             PyErr_SetString(PyExc_OverflowError, "__getitem__() failed; key's integer value is too large.");
00365             return NULL;
00366         }
00367 
00368         len = attr_list_len(al);
00369 
00370         if (i > len)
00371         {
00372             PyErr_Format(PyExc_ValueError, "__getitem__() failed; requested index (%"FMT64U") too big (len: %"FMT64U").", (uint64) i, (uint64) len);
00373             return NULL;
00374         }
00375 
00376         idx = &i;
00377     }
00378 
00379     return attr_list_get(al, idx);
00380 }
00381 
00390 static PyObject *append(Atrinik_AttrList *al, PyObject *value)
00391 {
00392     unsigned PY_LONG_LONG i;
00393 
00394     if (al->field == FIELDTYPE_FACTIONS)
00395     {
00396         PyErr_SetString(PyExc_NotImplementedError, "This attribute list does not implement append method.");
00397         return NULL;
00398     }
00399 
00400     i = attr_list_len(al);
00401     attr_list_set(al, &i, value);
00402 
00403     Py_INCREF(Py_None);
00404     return Py_None;
00405 }
00406 
00408 static PyMethodDef methods[] =
00409 {
00410     {"__getitem__", (PyCFunction) __getitem__, METH_O | METH_COEXIST, 0},
00411     {"append", (PyCFunction) append, METH_O, 0},
00412     {NULL, NULL, 0, 0}
00413 };
00414 
00415 static int InternalCompare(Atrinik_AttrList *left, Atrinik_AttrList *right)
00416 {
00417     return (left->field < right->field ? -1 : (left->field == right->field ? 0 : 1));
00418 }
00419 
00420 static PyObject *RichCompare(Atrinik_AttrList *left, Atrinik_AttrList *right, int op)
00421 {
00422     if (!left || !right || !PyObject_TypeCheck((PyObject *) left, &Atrinik_AttrListType) || !PyObject_TypeCheck((PyObject *) right, &Atrinik_AttrListType))
00423     {
00424         Py_INCREF(Py_NotImplemented);
00425         return Py_NotImplemented;
00426     }
00427 
00428     return generic_rich_compare(op, InternalCompare(left, right));
00429 }
00430 
00435 static PyObject *iter(PyObject *seq)
00436 {
00437     Atrinik_AttrList *al, *orig_al = (Atrinik_AttrList *) seq;
00438 
00439     al = PyObject_NEW(Atrinik_AttrList, &Atrinik_AttrListType);
00440     al->iter = 0;
00441     al->ptr = orig_al->ptr;
00442     al->offset = orig_al->offset;
00443     al->field = orig_al->field;
00444 
00445     return (PyObject *) al;
00446 }
00447 
00452 static PyObject *iternext(Atrinik_AttrList *al)
00453 {
00454     /* Possible to continue iteration? */
00455     if (al->iter < attr_list_len(al))
00456     {
00457         void *idx;
00458 
00459         al->iter++;
00460 
00461         if (al->field == FIELDTYPE_FACTIONS)
00462         {
00463             char *key = *(char **) (&(*(shstr ***) (void *) ((char *) al->ptr + al->offset))[al->iter - 1]);
00464 
00465             idx = key;
00466         }
00467         else
00468         {
00469             unsigned PY_LONG_LONG i = al->iter - 1;
00470 
00471             idx = &i;
00472         }
00473 
00474         return attr_list_get(al, idx);
00475     }
00476 
00477     /* Stop iteration. */
00478     return NULL;
00479 }
00480 
00487 static Py_ssize_t __len__(Atrinik_AttrList *al)
00488 {
00489     return attr_list_len(al);
00490 }
00491 
00496 static int __setitem__(Atrinik_AttrList *al, PyObject *key, PyObject *value)
00497 {
00498     void *idx;
00499 
00500     if (al->field == FIELDTYPE_FACTIONS)
00501     {
00502         idx = PyString_AsString(key);
00503     }
00504     else
00505     {
00506         unsigned PY_LONG_LONG i = PyLong_AsUnsignedLongLong(key);
00507         idx = &i;
00508     }
00509 
00510     return attr_list_set(al, idx, value);
00511 }
00512 
00517 static int __contains__(Atrinik_AttrList *al, PyObject *value)
00518 {
00519     return attr_list_contains(al, value);
00520 }
00521 
00523 static PySequenceMethods SequenceMethods =
00524 {
00525     (lenfunc) __len__,
00526     NULL, NULL, NULL, NULL, NULL, NULL,
00527     (objobjproc) __contains__,
00528     NULL, NULL
00529 };
00530 
00534 static PyMappingMethods MappingMethods =
00535 {
00536     (lenfunc) __len__,
00537     (binaryfunc) __getitem__,
00538     (objobjargproc) __setitem__,
00539 };
00540 
00542 PyTypeObject Atrinik_AttrListType =
00543 {
00544 #ifdef IS_PY3K
00545     PyVarObject_HEAD_INIT(NULL, 0)
00546 #else
00547     PyObject_HEAD_INIT(NULL)
00548     0,
00549 #endif
00550     "Atrinik.AttrList",
00551     sizeof(Atrinik_AttrList),
00552     0,
00553     NULL,
00554     NULL, NULL, NULL,
00555 #ifdef IS_PY3K
00556     NULL,
00557 #else
00558     (cmpfunc) InternalCompare,
00559 #endif
00560     NULL,
00561     0,
00562     &SequenceMethods,
00563     &MappingMethods,
00564     0, 0,
00565     NULL,
00566     0, 0, 0,
00567     Py_TPFLAGS_DEFAULT,
00568     "Atrinik attr lists",
00569     NULL, NULL,
00570     (richcmpfunc) RichCompare,
00571     0,
00572     (getiterfunc) iter,
00573     (iternextfunc) iternext,
00574     methods,
00575     0,
00576     NULL,
00577     0, 0, 0, 0, 0, 0, 0,
00578     NULL,
00579     0, 0, 0, 0, 0, 0, 0, 0
00580 #ifndef IS_PY_LEGACY
00581     , 0
00582 #endif
00583 };
00584 
00589 int Atrinik_AttrList_init(PyObject *module)
00590 {
00591     Atrinik_AttrListType.tp_new = PyType_GenericNew;
00592 
00593     if (PyType_Ready(&Atrinik_AttrListType) < 0)
00594     {
00595         return 0;
00596     }
00597 
00598     Py_INCREF(&Atrinik_AttrListType);
00599     PyModule_AddObject(module, "AtrrList", (PyObject *) &Atrinik_AttrListType);
00600 
00601     return 1;
00602 }
00603 
00611 PyObject *wrap_attr_list(void *ptr, size_t offset, field_type field)
00612 {
00613     Atrinik_AttrList *wrapper;
00614 
00615     wrapper = PyObject_NEW(Atrinik_AttrList, &Atrinik_AttrListType);
00616 
00617     if (wrapper)
00618     {
00619         wrapper->ptr = ptr;
00620         wrapper->offset = offset;
00621         wrapper->field = field;
00622     }
00623 
00624     return (PyObject *) wrapper;
00625 }