Symphony Of Empires
pyvm.cpp
Go to the documentation of this file.
1 // Eng3D - General purpouse game engine
2 // Copyright (C) 2021, Eng3D contributors
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16 //
17 // ----------------------------------------------------------------------------
18 // Name:
19 // pyvm.cpp
20 //
21 // Abstract:
22 // Does some important stuff.
23 // ----------------------------------------------------------------------------
24 
25 #include <unordered_map>
26 #include "eng3d/utils.hpp"
27 #include "eng3d/log.hpp"
28 #include "eng3d/ui/components.hpp"
29 
30 #ifdef E3D_FEATURE_PYTHON
31 
32 #include "eng3d/pyvm.hpp"
33 #define PY_SSIZE_T_CLEAN
34 #define Py_LIMITED_API
35 #include <Python.h>
36 
37 std::unordered_map<int, UI::Widget*> python_widgets;
38 std::unordered_map<int, std::shared_ptr<Eng3D::Texture>> python_textures;
39 std::unordered_map<std::string, int> python_ui_callbacks;
40 
42  : s{ _s }
43 {
44  if(PyImport_AppendInittab("eng3d", &[]() -> PyObject* {
45  static PyMethodDef eng3d_methods[] = {
46  { "numargs", [](PyObject* self, PyObject* args) -> PyObject* {
47  return PyLong_FromLong(10);
48  }, METH_VARARGS, "Return the number of arguments received by the process." },
49  {nullptr, nullptr, 0, nullptr}
50  };
51  static PyModuleDef eng3d_module = {
52  PyModuleDef_HEAD_INIT, "eng3d", nullptr, -1, eng3d_methods,
53  nullptr, nullptr, nullptr, nullptr
54  };
55  auto* module_obj = PyModule_Create(&eng3d_module);
56  auto* module_dict = PyImport_GetModuleDict();
57 
58  static PyMethodDef eng3d_core_methods[] = {
59  { "numargs", [](PyObject* self, PyObject* args) -> PyObject* {
60  return PyLong_FromLong(10);
61  }, METH_VARARGS, "Return the number of arguments received by the process." },
62  {nullptr, nullptr, 0, nullptr}
63  };
64  static PyModuleDef eng3d_core_module = {
65  PyModuleDef_HEAD_INIT, "eng3d", nullptr, -1, eng3d_core_methods,
66  nullptr, nullptr, nullptr, nullptr
67  };
68  auto* core_module_obj = PyModule_Create(&eng3d_core_module);
69  PyDict_SetItemString(module_dict, "eng3d.core", core_module_obj);
70 
71  static PyMethodDef eng3d_ui_methods[] = {
72  { "numargs", [](PyObject* self, PyObject* args) -> PyObject* {
73  return PyLong_FromLong(10);
74  }, METH_VARARGS, "Return the number of arguments received by the process." },
75  {nullptr, nullptr, 0, nullptr}
76  };
77  static PyModuleDef eng3d_ui_module = {
78  PyModuleDef_HEAD_INIT, "eng3d", nullptr, -1, eng3d_ui_methods,
79  nullptr, nullptr, nullptr, nullptr
80  };
81  auto* ui_module_obj = PyModule_Create(&eng3d_ui_module);
82  PyDict_SetItemString(module_dict, "eng3d.ui", ui_module_obj);
83  return module_obj;
84  }) < 0) {
85  PyErr_Print();
86  CXX_THROW(Eng3D::PythonException, translate("Failed to add eng3d core module"));
87  }
88 
89  Py_Initialize();
90 }
91 
92 void Eng3D::PythonVM::run_string(const std::string_view name, const std::string_view body) {
93  auto* code = Py_CompileString(body.data(), "test", Py_file_input);
94  auto* main_module = PyImport_AddModule("__main__");
95  auto* global_dict = PyModule_GetDict(main_module);
96  auto* local_dict = PyDict_New();
97  auto* obj = PyEval_EvalCode(code, global_dict, local_dict);
98  if(obj == nullptr) {
99  PyErr_Print();
100  CXX_THROW(Eng3D::PythonException, translate_format("Failed to execute \"%s\"", name.data()));
101  }
102 }
103 
104 void Eng3D::PythonVM::add_module(const std::string_view path) {
105  auto* name = PyUnicode_DecodeFSDefault(path.data());
106  // Error checking of pName left out
107  auto* py_module = PyImport_Import(name);
108  Py_XDECREF(name);
109  if(py_module == nullptr) {
110  PyErr_Print();
111  CXX_THROW(Eng3D::PythonException, translate_format("Failed to load \"%s\"", path.data()));
112  }
113  modules.emplace_back(py_module);
114 
115  auto py_func = PyObject_GetAttrString(py_module, "__init__");
116  // func is a new reference
117  if(py_func && PyCallable_Check(py_func)) {
118  auto py_value = PyObject_CallObject(py_func, nullptr);
119  Py_XDECREF(py_value);
120  } else {
121  if(PyErr_Occurred())
122  PyErr_Print();
123  CXX_THROW(Eng3D::PythonException, translate_format("Cannot find function \"%s\"", "__init__"));
124  }
125  Py_XDECREF(py_func);
126 }
127 
129  Py_Finalize();
130 }
131 
132 Eng3D::PythonObj::PythonObj(void* _obj)
133  : obj{ _obj }
134 {
135  Py_XINCREF(this->obj);
136 }
137 
138 Eng3D::PythonObj::PythonObj(const PythonObj& rhs) {
139  this->obj = rhs.obj;
140  Py_XINCREF(this->obj);
141 }
142 
143 Eng3D::PythonObj::PythonObj(PythonObj&& rhs) noexcept {
144  this->obj = rhs.obj;
145  rhs.obj = nullptr;
146 }
147 
149 {
150  Py_XDECREF(this->obj);
151 }
152 
153 #endif
PythonObj()=default
void add_module(const std::string_view path)
void run_string(const std::string_view name, const std::string_view path)
PythonVM(Eng3D::State &_s)
std::string translate(const std::string_view str)
Definition: string.cpp:76
std::string translate_format(const std::string_view format, Args &&... args)
String formatter, with translation.
Definition: string.hpp:128
#define CXX_THROW(class,...)
Definition: utils.hpp:98