Version 1.2.0 with new build/configure system
[tennix.git] / src / tennixpy.cc
blob8aea0080dd20c811ff7db76f470fece7efdd8ee9
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 PyObject* tennixpy_register_bot(PyObject* self, PyObject* bot_class);
36 PyObject* tennixpy_get_ball_pos(PyObject* self, PyObject* gamestate);
37 PyObject* tennixpy_get_power(PyObject* self, PyObject* args);
38 PyObject* tennixpy_get_position(PyObject* self, PyObject* args);
39 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 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);
209 fprintf(stderr, " %s", device->name);
212 PyObject* tennixpy_register_bot(PyObject* self, PyObject* bot_class)
214 assert(self == NULL);
215 if (PyType_Check(bot_class)) {
216 /* FIXME: check if all required attrs are here */
217 input_add_python_bot(bot_class);
218 Py_INCREF(bot_class);
219 } else {
220 return PyErr_Format(PyExc_TypeError, "This function needs a bot class to work");
223 Py_RETURN_NONE;
226 void tennixpy_get_bot_name(InputDevice *device, char* dest, int maxlen)
228 InputDevicePython *pydevice = (InputDevicePython*)device->user_data;
229 PyObject *bot_class = pydevice->py_bot_class;
230 PyObject *name;
232 name = PyObject_GetAttrString(bot_class, "name");
234 if (PyString_Check(name) && name != NULL) {
235 strncpy(dest, PyString_AsString(name), maxlen);
236 } else {
237 strncpy(dest, "<unknown pybot>", maxlen);
240 Py_XDECREF(name);
243 PyObject* tennixpy_get_ball_pos(PyObject* self, PyObject* gamestate)
245 PyObject* r;
246 GameState* s;
248 assert(self == NULL);
250 if (PyCObject_Check(gamestate)) {
251 s = (GameState*)PyCObject_AsVoidPtr(gamestate);
252 r = Py_BuildValue("(dd)", (double)(s->ball.x), (double)(s->ball.y - s->ball.z));
253 return r;
254 } else {
255 return PyErr_Format(PyExc_TypeError, "This function needs a GameState to work on");
259 PyObject* tennixpy_get_power(PyObject* self, PyObject* args)
261 PyObject* gamestate;
262 PyObject* player_id;
264 GameState* s;
265 int player;
267 assert(self == NULL);
269 assert(PyTuple_Check(args));
271 if (PyObject_Length(args) != 2) {
272 return PyErr_Format(PyExc_TypeError, "This function takes exactly 2 parameters");
275 gamestate = PyTuple_GetItem(args, 0);
276 player_id = PyTuple_GetItem(args, 1);
278 if (PyInt_Check(player_id)) {
279 player = (int)PyInt_AsLong(player_id);
280 } else {
281 return PyErr_Format(PyExc_TypeError, "Invalid player_id in second argument");
283 if (PyCObject_Check(gamestate)) {
284 s = (GameState*)PyCObject_AsVoidPtr(gamestate);
285 return PyFloat_FromDouble((double)(PLAYER(s, player).power));
286 } else {
287 return PyErr_Format(PyExc_TypeError, "First argument is not a GameState");
291 PyObject* tennixpy_get_position(PyObject* self, PyObject* args)
293 PyObject* gamestate;
294 PyObject* player_id;
296 GameState* s;
297 int player;
299 assert(self == NULL);
300 assert(PyTuple_Check(args));
302 if (PyObject_Length(args) != 2) {
303 return PyErr_Format(PyExc_TypeError, "This function takes exactly 2 parameters");
306 gamestate = PyTuple_GetItem(args, 0);
307 player_id = PyTuple_GetItem(args, 1);
309 if (PyInt_Check(player_id)) {
310 player = (int)PyInt_AsLong(player_id);
311 } else {
312 return PyErr_Format(PyExc_TypeError, "Invalid player_id in second argument");
314 if (PyCObject_Check(gamestate)) {
315 s = (GameState*)PyCObject_AsVoidPtr(gamestate);
316 return Py_BuildValue("(dd)", (double)(PLAYER(s, player).x), (double)(PLAYER(s, player).y));
317 } else {
318 return PyErr_Format(PyExc_TypeError, "First argument is not a GameState");
322 void tennixpy_init(TennixArchive& tnxar)
324 PyObject* tennix_module;
325 /*PyObject* bot_module;*/
326 /*FILE* fp;*/
327 char* data;
328 /*const char* argv[] = {""};*/
330 /* Search for modules in CWD */
331 setenv("PYTHONPATH", ".", 1);
332 PyEval_InitThreads();
333 Py_Initialize();
334 tennix_module = tennixpy_create_module();
335 /*Py_Main(1, argv);*/
336 /*bot_module = PyImport_ImportModule("defaultbot");
337 if (bot_module == NULL) {
338 PyErr_Print();
340 Py_DECREF(bot_module);*/
342 if (tnxar.setItemFilename("defaultbot.py") != 0) {
343 fprintf(stderr, "Loading computer players:");
344 data = tnxar.getItemBytes();
345 if (PyRun_SimpleString(data) != 0) {
346 PyErr_Print();
347 } else {
348 fprintf(stderr, ".\n");
350 free(data);
353 /*fp = fopen("defaultbot.py", "r");
354 PyRun_SimpleFile(fp, "defaultbot.py");
355 fclose(fp);*/
357 Py_DECREF(tennix_module);
358 _py_save = PyEval_SaveThread();
361 void tennixpy_unregister_bot(InputDevice *device)
363 InputDevicePython *pydevice = (InputDevicePython*)device->user_data;
364 PyEval_RestoreThread(_py_save);
365 Py_XDECREF(pydevice->py_bot);
366 _py_save = PyEval_SaveThread();
367 delete pydevice;
370 void tennixpy_uninit()
372 PyEval_RestoreThread(_py_save);
374 * FIXME: We _should_ run Py_Finalize here, but it makes
375 * Tennix crash, so we just tear it down without calling it.
377 /*Py_Finalize();*/
380 #endif /* HAVE_PYTHON */