CRAW now runs on Windows 7 too - the problem was that Windows 7 has moved some functi...
[craw.git] / craw / python.cpp
blob63296c37331688cfca0ed741301770991e61f7ef
1 //http://docs.python.org/extending/extending.html
2 //http://starship.python.net/crew/mwh/toext/node20.html
4 #include <ail/string.hpp>
5 #include <ail/array.hpp>
6 #include <ail/file.hpp>
7 #include "python.hpp"
8 #include "d2_functions.hpp"
9 #include "arguments.hpp"
10 #include "utility.hpp"
12 namespace python
14 PyObject
15 * automap_handler = 0,
16 * packet_handler = 0,
17 * command_handler = 0,
18 * keyboard_handler = 0,
19 * bncs_packet_handler = 0,
20 * item_handler = 0;
22 PyTypeObject
23 monster_data_type,
24 player_data_type,
25 item_data_type;
27 namespace
29 std::string const
30 module_name = "craw",
31 error_name = "error";
33 PyObject * module_error;
35 PyMethodDef module_methods[] =
37 {"set_automap_handler", &set_automap_handler, METH_VARARGS, "This function allows you to specify an automap unit handler which is called whenever a unit on the automap is being processed."},
38 {"set_packet_handler", &set_packet_handler, METH_VARARGS, "This function allows you to specify a packet handler which can react to incoming packets from the game server."},
39 {"set_command_handler", &set_command_handler, METH_VARARGS, "This function allows you to specify a console command handler which is called when the user has entered a command in the module's console."},
40 {"set_keyboard_handler", &set_keyboard_handler, METH_VARARGS, "This function allows you to specify a keyboard handler which is called whenever a key is pressed in game when the player is not chatting."},
41 {"set_bncs_packet_handler", &set_bncs_packet_handler, METH_VARARGS, "This function allows you to specify a packet handler for incoming Battle.net Chat Server packets."},
42 {"set_item_handler", &set_item_handler, METH_VARARGS, "This function allows you to specify an item handler which is called when new items are dropped."},
44 {"draw_line", &draw_line, METH_VARARGS, "Draws a single line."},
45 {"draw_text", &draw_text, METH_VARARGS, "Draws text on the screen."},
47 {"send_packet", &send_packet, METH_VARARGS, "Sends a packet to the game server."},
48 {"leave_game", &leave_game, METH_VARARGS, "Leaves the current game."},
49 {"get_life", &get_life, METH_VARARGS, "Returns a (current life, maximum life) tuple or None if the character is not in game yet."},
50 {"get_mana", &get_mana, METH_VARARGS, "Returns a (current mana, maximum mana) tuple or None if the character is not in game yet."},
51 {"get_player_level", &get_player_level, METH_VARARGS, "Returns the numeric identifier of the area the player currently resides in or None if it failed to retrieve the identifier."},
52 {"get_player_id", &get_player_id, METH_VARARGS, "Returns the numeric identifier of the player unit or None if it fails to retrieve it."},
53 {"get_name_by_id", &get_name_by_id, METH_VARARGS, "Returns the name of a player given their numeric identifier or None if it fails to do so."},
54 {"get_player_location", &get_player_location, METH_VARARGS, "Returns the player's location as a tuple (x, y) or None if the player is currently not in a game."},
55 {"get_tp_tome_id", &get_tp_tome_id, METH_VARARGS, "Returns the ID of the first non-empty Tome of Town Portal in the player's inventory or None if it failed to find one."},
56 {"get_players", &get_players, METH_VARARGS, "Returns a list of players currently in the game."},
57 {"reveal_act", &reveal_act, METH_VARARGS, "Reveal the current act on the automap."},
58 {"receive_packet", &receive_packet, METH_VARARGS, "Artificially receives a packet on the client side."},
59 {"send_bncs_packet", &send_bncs_packet, METH_VARARGS, "Sends a packet to the Battle.net Chat Server."},
60 {"move_click", &move_click, METH_VARARGS, "Move to the specified x, y coordinate."},
61 {"get_skill_level", &get_skill_level, METH_VARARGS, "Takes a skill identifier as its sole argument. Returns None if the level of the skill could not be retrieved or, in the case of success, the skill level (including bonuses from items and such)."},
62 {"get_minions", &get_minions, METH_VARARGS, "Takes a player ID as its sole argument. Returns a list of tuples of the form (id, type) of the minions associated with this player or None in case of failure."},
63 {"print_text", &print_text, METH_VARARGS, "Print text to the chat (client-side)."},
64 {"get_experience", &get_experience, METH_VARARGS, "Returns the experience of the character or None in case of an error."},
65 {"get_unit", &get_unit, METH_VARARGS, "Takes the ID of a unit and the type of the unit as arguments. Returns the unit data associated with the ID or None in case of failure."},
67 {0, 0, 0, 0}
70 PyTypeObject default_type =
72 PyObject_HEAD_INIT(NULL)
113 void print_python_type(PyTypeObject & input)
115 write_line(std::string("tp_name ") + (input.tp_name ? input.tp_name : "NULL"));
116 write_line("tp_basicsize " + ail::number_to_string(input.tp_basicsize));
117 write_line("tp_itemsize " + ail::number_to_string(input.tp_itemsize));
119 write_line("tp_dealloc " + ail::hex_string_32((unsigned)input.tp_dealloc));
120 write_line("tp_print " + ail::hex_string_32((unsigned)input.tp_print));
121 write_line("tp_getattr " + ail::hex_string_32((unsigned)input.tp_getattr));
122 write_line("tp_getattr " + ail::hex_string_32((unsigned)input.tp_getattr));
123 write_line("tp_setattr " + ail::hex_string_32((unsigned)input.tp_setattr));
124 write_line("tp_compare " + ail::hex_string_32((unsigned)input.tp_compare));
125 write_line("tp_repr " + ail::hex_string_32((unsigned)input.tp_repr));
127 write_line("tp_as_number " + ail::hex_string_32((unsigned)input.tp_as_number));
128 write_line("tp_as_sequence " + ail::hex_string_32((unsigned)input.tp_as_sequence));
129 write_line("tp_as_mapping " + ail::hex_string_32((unsigned)input.tp_as_mapping));
131 write_line("tp_hash " + ail::hex_string_32((unsigned)input.tp_hash));
132 write_line("tp_call " + ail::hex_string_32((unsigned)input.tp_call));
133 write_line("tp_str " + ail::hex_string_32((unsigned)input.tp_str));
134 write_line("tp_getattro " + ail::hex_string_32((unsigned)input.tp_getattro));
135 write_line("tp_setattro " + ail::hex_string_32((unsigned)input.tp_setattro));
137 write_line("tp_as_buffer " + ail::hex_string_32((unsigned)input.tp_as_buffer));
139 write_line("tp_flags " + ail::number_to_string(input.tp_flags));
141 write_line(std::string("tp_doc ") + (input.tp_doc ? input.tp_doc : "NULL"));
143 write_line("tp_traverse " + ail::hex_string_32((unsigned)input.tp_traverse));
145 write_line("tp_clear " + ail::hex_string_32((unsigned)input.tp_clear));
147 write_line("tp_richcompare " + ail::hex_string_32((unsigned)input.tp_richcompare));
149 write_line("tp_weaklistoffset " + ail::number_to_string(input.tp_weaklistoffset));
151 write_line("tp_iter " + ail::hex_string_32((unsigned)input.tp_iter));
152 write_line("tp_iternext " + ail::hex_string_32((unsigned)input.tp_iternext));
154 write_line("tp_methods " + ail::hex_string_32((unsigned)input.tp_methods));
155 write_line("tp_members " + ail::hex_string_32((unsigned)input.tp_members));
156 write_line("tp_getset " + ail::hex_string_32((unsigned)input.tp_getset));
157 write_line("tp_base " + ail::hex_string_32((unsigned)input.tp_base));
159 write_line("tp_dict " + ail::hex_string_32((unsigned)input.tp_dict));
160 write_line("tp_descr_get " + ail::hex_string_32((unsigned)input.tp_descr_get));
161 write_line("tp_descr_set " + ail::hex_string_32((unsigned)input.tp_descr_set));
163 write_line("tp_dictoffset " + ail::number_to_string(input.tp_dictoffset));
165 write_line("tp_init " + ail::hex_string_32((unsigned)input.tp_init));
166 write_line("tp_alloc " + ail::hex_string_32((unsigned)input.tp_alloc));
167 write_line("tp_new " + ail::hex_string_32((unsigned)input.tp_new));
168 write_line("tp_free " + ail::hex_string_32((unsigned)input.tp_free));
169 write_line("tp_is_gc " + ail::hex_string_32((unsigned)input.tp_is_gc));
170 write_line("tp_bases " + ail::hex_string_32((unsigned)input.tp_bases));
171 write_line("tp_mro " + ail::hex_string_32((unsigned)input.tp_mro));
172 write_line("tp_cache " + ail::hex_string_32((unsigned)input.tp_cache));
173 write_line("tp_subclasses " + ail::hex_string_32((unsigned)input.tp_subclasses));
174 write_line("tp_weaklist " + ail::hex_string_32((unsigned)input.tp_weaklist));
175 write_line("tp_del " + ail::hex_string_32((unsigned)input.tp_del));
177 write_line("tp_version_tag " + ail::number_to_string(input.tp_version_tag) + "\n\n");
180 void initialise_python_type(PyTypeObject & output, std::string const & class_name, std::size_t object_size, PyMemberDef * members, PyObject * module)
182 std::string full_name = module_name + "." + class_name;
184 if(verbose)
185 write_line("Adding type " + full_name + " to the Python module");
187 char * name = new char[full_name.size() + 1];
188 std::strcpy(name, full_name.c_str());
190 output = default_type;
191 output.tp_flags = Py_TPFLAGS_DEFAULT;
192 output.tp_basicsize = object_size;
193 output.tp_new = PyType_GenericNew;
194 output.tp_name = name;
195 output.tp_members = members;
196 output.tp_doc = "No documentation available.";
198 if(PyType_Ready(&output) < 0)
200 error("Failed to initialise Python data type \"" + class_name + "\"");
201 exit_process();
204 Py_INCREF(&output);
205 if(PyModule_AddObject(module, class_name.c_str(), reinterpret_cast<PyObject *>(&output)) != 0)
207 error("Failed to add type \"" + class_name + "\" to the Python module");
208 exit_process();
212 PyMODINIT_FUNC initialise_module()
214 PyObject * module = Py_InitModule(module_name.c_str(), module_methods);
215 if(module == 0)
216 return;
218 initialise_python_type(monster_data_type, "monster_data", sizeof(python_monster_data), monster_data_members, module);
219 initialise_python_type(player_data_type, "player_data", sizeof(python_player_data), player_data_members, module);
220 initialise_python_type(item_data_type, "item_data", sizeof(python_item_data), item_data_members, module);
222 module_error = PyErr_NewException(const_cast<char *>((module_name + "." + error_name).c_str()), 0, 0);
223 Py_INCREF(module_error);
224 PyModule_AddObject(module, error_name.c_str(), module_error);
227 bool get_base_name(std::string const & input, std::string & output)
229 std::size_t offset = input.rfind('\\');
230 if(offset == std::string::npos)
231 return false;
232 output = input.substr(0, offset);
233 return true;
236 bool initialise_python()
238 if(python_script.empty())
239 return true;
241 std::string content;
242 if(!ail::read_file(python_script, content))
244 error("Failed to load Python script \"" + python_script + "\"");
245 return false;
248 if(prompt_mode)
249 initialise_console();
251 content = ail::replace_string(content, "\r", "");
253 Py_Initialize();
254 initialise_module();
256 std::string script_directory;
257 if(get_base_name(python_script, script_directory))
258 PyRun_SimpleString(("import sys\nsys.path.append('" + script_directory + "')\n").c_str());
260 if(PyRun_SimpleString(content.c_str()) != 0)
262 write_line("The main Python script contained errors.");
263 return false;
266 //PyRun_SimpleString(("execfile('" + ail::replace_string(python_script, "\\", "\\\\") + "')").c_str());
268 return true;
271 PyObject * create_list(std::size_t size)
273 PyObject * output = PyList_New(size);
274 if(output == 0)
276 error("Failed to create a Python list");
277 exit_process();
279 return output;
282 void set_list_item(PyObject * list, std::size_t index, PyObject * value)
284 if(PyList_SetItem(list, index, value) < 0)
286 error("Failed to set a Python list item");
287 exit_process();
291 PyObject * create_string(std::string const & input)
293 PyObject * output = PyString_FromStringAndSize(input.c_str(), input.length());
294 if(output == 0)
296 error("Failed to create a Python string for string \"" + input + "\"");
297 exit_process();
299 return output;