Enable all warnings
[tennix.git] / src / tennixpy.cc
blob29b4a5a9404a8fe8ab5dda01c296285c9fbc7377
3 /**
5 * Tennix! SDL Port
6 * Copyright (C) 2003, 2007, 2008, 2009 Thomas Perl <thp@thpinfo.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
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.
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., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
23 **/
25 #include "config.h"
27 #ifdef HAVE_PYTHON
29 #include <Python.h>
31 #include "game.h"
32 #include "archive.h"
33 #include "tennixpy.h"
35 static void tennixpy_get_bot_name(InputDevice *device, char *dest, int maxlen);
36 static PyObject* tennixpy_register_bot(PyObject* self, PyObject* bot_class);
37 static PyObject* tennixpy_get_ball_pos(PyObject* self, PyObject* gamestate);
38 static PyObject* tennixpy_get_power(PyObject* self, PyObject* args);
39 static PyObject* tennixpy_get_position(PyObject* self, PyObject* args);
40 static PyObject* tennixpy_create_module(void);
42 struct InputDevicePython {
43 PyObject* py_bot_class;
44 PyObject* py_bot;
48 /* This saves our current Python thread state */
49 static PyThreadState *_py_save;
51 static PyMethodDef TennixMethods[] = {
52 {"register_bot", tennixpy_register_bot, METH_O, "Register a new bot"},
53 {"get_ball_pos", tennixpy_get_ball_pos, METH_O, "Gets the ball position"},
54 {"get_position", tennixpy_get_position, METH_VARARGS, "Get position of player"},
55 {"get_power", tennixpy_get_power, METH_VARARGS, "Get power of player"},
56 {NULL, NULL, 0, NULL} /* Sentinel */
59 PyObject* tennixpy_create_module(void)
61 PyObject* module;
62 module = Py_InitModule("tennix", TennixMethods);
63 PyModule_AddIntConstant(module, "INPUT_AXIS_X", INPUT_AXIS_X);
64 PyModule_AddIntConstant(module, "INPUT_AXIS_Y", INPUT_AXIS_Y);
65 PyModule_AddIntConstant(module, "INPUT_KEY_HIT", INPUT_KEY_HIT);
66 PyModule_AddIntConstant(module, "INPUT_KEY_TOPSPIN", INPUT_KEY_TOPSPIN);
67 PyModule_AddIntConstant(module, "INPUT_KEY_SMASH", INPUT_KEY_SMASH);
68 return module;
71 void
72 tennixpy_create_bot(InputDevice *device, GameState *s, int player_id)
74 InputDevicePython *pydevice = (InputDevicePython*)device->user_data;
75 PyObject *bot_class = pydevice->py_bot_class;
77 PyObject* o = NULL;
78 PyObject* args;
79 PyObject* gamestate;
81 PyEval_RestoreThread(_py_save);
82 if (PyType_Check(bot_class)) {
83 gamestate = PyCObject_FromVoidPtr((void*)s, NULL);
84 assert(gamestate != NULL);
85 args = Py_BuildValue("(Oi)", gamestate, player_id);
86 assert(args != NULL);
87 Py_DECREF(gamestate);
88 o = PyObject_CallObject(bot_class, args);
89 Py_DECREF(args);
90 if (o == NULL) {
91 fprintf(stderr, "cannot create instance\n");
92 PyErr_Print();
94 } else {
95 fprintf(stderr, "Typecheck failed\n");
97 _py_save = PyEval_SaveThread();
99 pydevice->py_bot = o;
102 void tennixpy_destroy_bot(InputDevice *device)
104 InputDevicePython *pydevice = (InputDevicePython*)device->user_data;
105 PyObject *bot = pydevice->py_bot;
107 PyObject* finish_func;
108 PyEval_RestoreThread(_py_save);
109 finish_func = PyObject_GetAttrString(bot, "finish");
110 assert(finish_func != NULL);
111 Py_DECREF(PyObject_CallObject(finish_func, NULL));
112 Py_DECREF(finish_func);
113 Py_DECREF(bot);
114 _py_save = PyEval_SaveThread();
117 float tennixpy_bot_get_axis(InputDevice *device, int axis)
119 InputDevicePython *pydevice = (InputDevicePython*)device->user_data;
120 PyObject *bot = pydevice->py_bot;
122 PyObject* args;
123 PyObject* get_axis_func;
124 PyObject* r;
125 float result = 0.0;
127 PyEval_RestoreThread(_py_save);
129 get_axis_func = PyObject_GetAttrString(bot, "get_axis");
130 assert(get_axis_func != NULL);
132 args = Py_BuildValue("(i)", axis);
133 r = PyObject_CallObject(get_axis_func, args);
134 Py_DECREF(get_axis_func);
135 Py_DECREF(args);
137 if (r == NULL) {
138 PyErr_Print();
139 } else {
140 if (PyFloat_Check(r)) {
141 result = (float)PyFloat_AsDouble(r);
142 } else {
143 fprintf(stderr, "Unexpected value from python\n");
145 Py_DECREF(r);
148 _py_save = PyEval_SaveThread();
149 return result;
152 char tennixpy_bot_get_key(InputDevice *device, int key)
154 InputDevicePython *pydevice = (InputDevicePython*)device->user_data;
155 PyObject *bot = pydevice->py_bot;
157 PyObject* args;
158 PyObject* get_key_func;
159 PyObject* r;
160 char result = 0;
162 PyEval_RestoreThread(_py_save);
164 get_key_func = PyObject_GetAttrString(bot, "get_key");
165 assert(get_key_func != NULL);
167 args = Py_BuildValue("(i)", key);
168 r = PyObject_CallObject(get_key_func, args);
169 Py_DECREF(get_key_func);
170 Py_DECREF(args);
172 if (r == NULL) {
173 PyErr_Print();
174 } else {
175 if (r == Py_True) {
176 result = 1;
177 } else if (r == Py_False) {
178 result = 0;
179 } else {
180 fprintf(stderr, "Unexpected value from python\n");
182 Py_DECREF(r);
185 _py_save = PyEval_SaveThread();
186 return result;
189 static void
190 input_add_python_bot(PyObject* bot_class)
192 InputDevice *device = input_add_device();
194 if (device == NULL) {
195 fprintf(stderr, "Warning: Cannot add any more Python bots.\n");
196 // We carry a ref of bot_class, so give it up here
197 Py_DECREF(bot_class);
198 return;
201 device->type = INPUT_TYPE_AI_PYTHON;
202 device->icon = GR_INPUT_AI;
204 InputDevicePython *pydevice = new InputDevicePython;
205 pydevice->py_bot_class = bot_class;
206 pydevice->py_bot = NULL;
207 device->user_data = (void*)pydevice;
208 tennixpy_get_bot_name(device, device->name, INPUT_DEVICE_NAME_MAX);
211 PyObject* tennixpy_register_bot(PyObject* self, PyObject* bot_class)
213 assert(self == NULL);
214 if (PyType_Check(bot_class)) {
215 /* FIXME: check if all required attrs are here */
216 input_add_python_bot(bot_class);
217 Py_INCREF(bot_class);
218 } else {
219 return PyErr_Format(PyExc_TypeError, "This function needs a bot class to work");
222 Py_RETURN_NONE;
225 void tennixpy_get_bot_name(InputDevice *device, char* dest, int maxlen)
227 InputDevicePython *pydevice = (InputDevicePython*)device->user_data;
228 PyObject *bot_class = pydevice->py_bot_class;
229 PyObject *name;
231 name = PyObject_GetAttrString(bot_class, "name");
233 if (PyString_Check(name) && name != NULL) {
234 strncpy(dest, PyString_AsString(name), maxlen);
235 } else {
236 strncpy(dest, "<unknown pybot>", maxlen);
239 Py_XDECREF(name);
242 PyObject* tennixpy_get_ball_pos(PyObject* self, PyObject* gamestate)
244 PyObject* r;
245 GameState* s;
247 assert(self == NULL);
249 if (PyCObject_Check(gamestate)) {
250 s = (GameState*)PyCObject_AsVoidPtr(gamestate);
251 r = Py_BuildValue("(dd)", (double)(s->ball.x), (double)(s->ball.y - s->ball.z));
252 return r;
253 } else {
254 return PyErr_Format(PyExc_TypeError, "This function needs a GameState to work on");
258 PyObject* tennixpy_get_power(PyObject* self, PyObject* args)
260 PyObject* gamestate;
261 PyObject* player_id;
263 GameState* s;
264 int player;
266 assert(self == NULL);
268 assert(PyTuple_Check(args));
270 if (PyObject_Length(args) != 2) {
271 return PyErr_Format(PyExc_TypeError, "This function takes exactly 2 parameters");
274 gamestate = PyTuple_GetItem(args, 0);
275 player_id = PyTuple_GetItem(args, 1);
277 if (PyInt_Check(player_id)) {
278 player = (int)PyInt_AsLong(player_id);
279 } else {
280 return PyErr_Format(PyExc_TypeError, "Invalid player_id in second argument");
282 if (PyCObject_Check(gamestate)) {
283 s = (GameState*)PyCObject_AsVoidPtr(gamestate);
284 return PyFloat_FromDouble((double)(PLAYER(s, player).power));
285 } else {
286 return PyErr_Format(PyExc_TypeError, "First argument is not a GameState");
290 PyObject* tennixpy_get_position(PyObject* self, PyObject* args)
292 PyObject* gamestate;
293 PyObject* player_id;
295 GameState* s;
296 int player;
298 assert(self == NULL);
299 assert(PyTuple_Check(args));
301 if (PyObject_Length(args) != 2) {
302 return PyErr_Format(PyExc_TypeError, "This function takes exactly 2 parameters");
305 gamestate = PyTuple_GetItem(args, 0);
306 player_id = PyTuple_GetItem(args, 1);
308 if (PyInt_Check(player_id)) {
309 player = (int)PyInt_AsLong(player_id);
310 } else {
311 return PyErr_Format(PyExc_TypeError, "Invalid player_id in second argument");
313 if (PyCObject_Check(gamestate)) {
314 s = (GameState*)PyCObject_AsVoidPtr(gamestate);
315 return Py_BuildValue("(dd)", (double)(PLAYER(s, player).x), (double)(PLAYER(s, player).y));
316 } else {
317 return PyErr_Format(PyExc_TypeError, "First argument is not a GameState");
321 void tennixpy_init(TennixArchive& tnxar)
323 PyObject* tennix_module;
324 char* data;
326 setenv("PYTHONPATH", ".", 1);
327 PyEval_InitThreads();
328 Py_Initialize();
329 tennix_module = tennixpy_create_module();
331 tnxar.rewind();
332 while (!tnxar.endOfFile()) {
333 const char *filename = tnxar.getItemFilename();
334 if (strcmp(".py", filename + strlen(filename) - 3) == 0) {
335 data = tnxar.getItemBytes();
336 if (PyRun_SimpleString(data) != 0) {
337 PyErr_Print();
339 free(data);
341 tnxar.next();
344 Py_DECREF(tennix_module);
345 _py_save = PyEval_SaveThread();
348 void tennixpy_unregister_bot(InputDevice *device)
350 InputDevicePython *pydevice = (InputDevicePython*)device->user_data;
351 PyEval_RestoreThread(_py_save);
352 Py_XDECREF(pydevice->py_bot);
353 _py_save = PyEval_SaveThread();
354 delete pydevice;
357 void tennixpy_uninit()
359 PyEval_RestoreThread(_py_save);
361 * FIXME: We _should_ run Py_Finalize here, but it makes
362 * Tennix crash, so we just tear it down without calling it.
364 /*Py_Finalize();*/
367 #endif /* HAVE_PYTHON */