#include "python_host_node.h" #include #include #include "python_stdlib.h" #ifdef MTGODOT_HAVE_PYTHON // Standard types only — PythonBoot.h keeps and the Win32 shims out of this translation // unit, which has to stay compilable next to godot-cpp. #include "platform/ScriptLib/PythonBoot.h" #include "platform/EterLib/UIRenderCommands.h" #include "platform/EterLib/RenderCommands3D.h" #include "platform/MilesLib/AudioCommands.h" #include "platform/PackBackend.h" #include "../../native_render/draw_capture.h" #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include using namespace godot; namespace mtgodot { #ifdef MTGODOT_HAVE_PYTHON String Metin2PythonHost::start(const String &stdlib_path) { String path = stdlib_path; if (path.is_empty()) { path = Metin2Python::stdlib_path(false); if (path.is_empty()) { return String("python27.zip unavailable: ") + Metin2Python::last_error(); } } std::string error; if (!PythonBoot::Start(path.utf8().get_data(), &error)) { return String::utf8(error.c_str()); } return String(); } bool Metin2PythonHost::is_running() { return PythonBoot::IsRunning(); } String Metin2PythonHost::run_main_script(const String &command_line) { std::string error; if (!PythonBoot::RunMainScript(command_line.utf8().get_data(), &error)) { return String::utf8(error.c_str()); } return String(); } String Metin2PythonHost::run_line(const String &source) { std::string error; if (!PythonBoot::RunLine(source.utf8().get_data(), &error)) { return String::utf8(error.c_str()); } return String(); } // str(expression) from __main__, or "" when it raised (the error goes to the log like run_line's). String Metin2PythonHost::evaluate(const String &expression) { std::string result, error; if (!PythonBoot::Evaluate(expression.utf8().get_data(), &result, &error)) { UtilityFunctions::push_error(String::utf8(error.c_str())); return String(); } return String::utf8(result.c_str()); } bool Metin2PythonHost::is_app_looping() { return PythonBoot::IsAppLooping(); } void Metin2PythonHost::set_ui_size(int width, int height) { PythonBoot::SetUISize(width, height); } void Metin2PythonHost::ui_mouse_move(int x, int y) { PythonBoot::UIMouseMove(x, y); } void Metin2PythonHost::ui_mouse_button(int button, bool pressed, int x, int y) { PythonBoot::UIMouseButton(button, pressed, x, y); } void Metin2PythonHost::ui_mouse_wheel(int delta) { PythonBoot::UIMouseWheel(delta); } void Metin2PythonHost::ui_key(int key, bool pressed) { PythonBoot::UIKey(key, pressed); } void Metin2PythonHost::ui_char(int codepoint) { PythonBoot::UIChar(unsigned(codepoint)); } void Metin2PythonHost::ui_ime_key(int vkey) { PythonBoot::UIIMEKeyDown(vkey); } void Metin2PythonHost::ui_update() { PythonBoot::UIUpdate(); } namespace { Dictionary ui_command_item(const UIRenderCommand &command) { static const StringName s_kind("kind"); static const StringName s_x1("x1"); static const StringName s_y1("y1"); static const StringName s_x2("x2"); static const StringName s_y2("y2"); static const StringName s_argb("argb"); static const StringName s_end_argb("end_argb"); static const StringName s_clip_x1("clip_x1"); static const StringName s_clip_y1("clip_y1"); static const StringName s_clip_x2("clip_x2"); static const StringName s_clip_y2("clip_y2"); static const StringName s_text("text"); static const StringName s_quad("quad"); static const StringName s_uv("uv"); static const StringName s_blend("blend"); static const StringName s_mask("mask"); static const StringName s_mask_uv("mask_uv"); static const StringName s_behind_3d("behind_3d"); static const StringName s_val_bar("bar"); static const StringName s_val_gradient_bar("gradient_bar"); static const StringName s_val_line("line"); static const StringName s_val_image("image"); static const StringName s_val_text("text"); Dictionary item; item[s_kind] = command.kind == UIRenderCommand::GradientBar ? s_val_gradient_bar : (command.kind == UIRenderCommand::Bar ? s_val_bar : (command.kind == UIRenderCommand::Line ? s_val_line : (command.kind == UIRenderCommand::Image ? s_val_image : s_val_text))); item[s_x1] = command.x1; item[s_y1] = command.y1; item[s_x2] = command.x2; item[s_y2] = command.y2; item[s_argb] = static_cast(command.argb); if (command.kind == UIRenderCommand::GradientBar) item[s_end_argb] = static_cast(command.end_argb); item[s_clip_x1] = command.clip_x1; item[s_clip_y1] = command.clip_y1; item[s_clip_x2] = command.clip_x2; item[s_clip_y2] = command.clip_y2; if (command.kind == UIRenderCommand::Text || command.kind == UIRenderCommand::Image) item[s_text] = String::utf8(command.text.c_str()); if (command.quad) { PackedVector2Array quad; quad.resize(4); Vector2 *qptr = quad.ptrw(); for (int i = 0; i < 4; ++i) qptr[i] = Vector2(command.qx[i], command.qy[i]); item[s_quad] = quad; PackedVector2Array uv; uv.resize(4); Vector2 *uvptr = uv.ptrw(); uvptr[0] = Vector2(command.su, command.sv); uvptr[1] = Vector2(command.eu, command.sv); uvptr[2] = Vector2(command.su, command.ev); uvptr[3] = Vector2(command.eu, command.ev); item[s_uv] = uv; item[s_blend] = command.blend; if (!command.mask.empty()) { item[s_mask] = String::utf8(command.mask.c_str()); PackedVector2Array mask_uv; mask_uv.resize(4); Vector2 *mptr = mask_uv.ptrw(); for (int i = 0; i < 4; ++i) mptr[i] = Vector2(command.mu[i], command.mv[i]); item[s_mask_uv] = mask_uv; } } item[s_behind_3d] = command.behind_3d; return item; } } // namespace Array Metin2PythonHost::ui_render_commands() { PythonBoot::UIRender(); static std::uint64_t cached_frame_id = UINT64_MAX; static Array cached_commands; const std::uint64_t frame_id = UIRenderFrameId(); if (cached_frame_id == frame_id) return cached_commands; const auto &commands = UIRenderCommands(); Array out; out.resize(static_cast(commands.size())); for (size_t idx = 0; idx < commands.size(); ++idx) out[static_cast(idx)] = ui_command_item(commands[idx]); cached_frame_id = frame_id; cached_commands = out; return out; } Array Metin2PythonHost::ui_render_commands_batched() { PythonBoot::UIRender(); static std::uint64_t cached_frame_id = UINT64_MAX; static Array cached_commands; const std::uint64_t frame_id = UIRenderFrameId(); if (cached_frame_id == frame_id) return cached_commands; const auto &commands = UIRenderCommands(); const auto batchable = [](const UIRenderCommand &c) { return c.kind == UIRenderCommand::Image && c.quad && c.text.rfind("mem:", 0) == 0 && c.mask.empty() && c.blend == 0 && c.x1 >= c.clip_x1 && c.y1 >= c.clip_y1 && c.x2 <= c.clip_x2 && c.y2 <= c.clip_y2; }; Array out; for (size_t i = 0; i < commands.size();) { const UIRenderCommand &first = commands[i]; if (!batchable(first)) { out.push_back(ui_command_item(first)); ++i; continue; } size_t end = i + 1; while (end < commands.size() && batchable(commands[end]) && commands[end].text == first.text && commands[end].behind_3d == first.behind_3d) ++end; if (end == i + 1) { out.push_back(ui_command_item(first)); i = end; continue; } const int64_t count = static_cast(end - i); PackedVector2Array points, uvs; PackedColorArray colors; PackedInt32Array indices; points.resize(count * 4); uvs.resize(count * 4); colors.resize(count * 4); indices.resize(count * 6); Vector2 *point_data = points.ptrw(), *uv_data = uvs.ptrw(); Color *color_data = colors.ptrw(); int32_t *index_data = indices.ptrw(); for (int64_t j = 0; j < count; ++j) { const UIRenderCommand &c = commands[i + static_cast(j)]; const int64_t v = j * 4, t = j * 6; point_data[v] = Vector2(c.qx[0], c.qy[0]); point_data[v + 1] = Vector2(c.qx[1], c.qy[1]); point_data[v + 2] = Vector2(c.qx[3], c.qy[3]); point_data[v + 3] = Vector2(c.qx[2], c.qy[2]); uv_data[v] = Vector2(c.su, c.sv); uv_data[v + 1] = Vector2(c.eu, c.sv); uv_data[v + 2] = Vector2(c.eu, c.ev); uv_data[v + 3] = Vector2(c.su, c.ev); const Color color(((c.argb >> 16) & 255) / 255.0f, ((c.argb >> 8) & 255) / 255.0f, (c.argb & 255) / 255.0f, ((c.argb >> 24) & 255) / 255.0f); for (int k = 0; k < 4; ++k) color_data[v + k] = color; const int32_t base = static_cast(v); index_data[t] = base; index_data[t + 1] = base + 1; index_data[t + 2] = base + 2; index_data[t + 3] = base; index_data[t + 4] = base + 2; index_data[t + 5] = base + 3; } Dictionary item; item["kind"] = StringName("glyph_batch"); item["text"] = String::utf8(first.text.c_str()); item["behind_3d"] = first.behind_3d; item["points"] = points; item["uvs"] = uvs; item["colors"] = colors; item["indices"] = indices; item["glyph_count"] = count; out.push_back(item); i = end; } cached_frame_id = frame_id; cached_commands = out; return out; } bool Metin2PythonHost::has_3d_draws() { return !Render3DDraws().empty(); } Ref Metin2PythonHost::memory_texture(const String &name, int64_t known_revision) { UIMemoryTexture texture; if (!UIRenderMemoryTexture(name.utf8().get_data(), &texture) || texture.width <= 0 || texture.height <= 0) return Ref(); if (known_revision >= 0 && texture.revision == static_cast(known_revision)) return Ref(); PackedByteArray rgba; rgba.resize(static_cast(texture.argb.size()) * 4); uint8_t *out = rgba.ptrw(); for (std::uint32_t argb : texture.argb) { *out++ = static_cast(argb >> 16); *out++ = static_cast(argb >> 8); *out++ = static_cast(argb); *out++ = static_cast(argb >> 24); } return Image::create_from_data(texture.width, texture.height, false, Image::FORMAT_RGBA8, rgba); } namespace { PackedFloat32Array floats(const float *values, int count) { PackedFloat32Array out; out.resize(count); for (int i = 0; i < count; ++i) out[i] = values[i]; return out; } Color argb_color(std::uint32_t argb) { return Color(((argb >> 16) & 255) / 255.0f, ((argb >> 8) & 255) / 255.0f, (argb & 255) / 255.0f, ((argb >> 24) & 255) / 255.0f); } bool read_pack_texture(const std::string &vpath, std::vector &bytes) { if (vpath.empty() || !mtpack40250::ready()) return false; std::string norm = vpath; for (char &ch : norm) if (ch == '\\') ch = '/'; std::string stripped = norm; if (stripped.size() >= 2 && stripped[1] == ':') stripped = stripped.substr(2); while (!stripped.empty() && stripped.front() == '/') stripped.erase(stripped.begin()); auto lower = [](std::string s) { for (char &ch : s) if (ch >= 'A' && ch <= 'Z') ch = static_cast(ch - 'A' + 'a'); return s; }; for (const std::string &candidate : { norm, stripped, "d:/" + stripped, lower(norm), lower(stripped), lower("d:/" + stripped)}) { if (mtpack40250::read(candidate, bytes) && !bytes.empty()) return true; } return false; } } // namespace Array Metin2PythonHost::render3d_draws() { struct CachedGeometry { std::uint64_t revision; std::uint64_t last_used; Dictionary arrays; }; static std::unordered_map geometry_cache; static std::uint64_t extraction = 0; ++extraction; const auto &draws = Render3DDraws(); static bool capture_written = false; if (!capture_written) { const char *capture_path = std::getenv("MT_NATIVE_CAPTURE_PATH"); const char *minimum_text = std::getenv("MT_NATIVE_CAPTURE_MIN_DRAWS"); const unsigned long minimum = minimum_text ? std::strtoul(minimum_text, nullptr, 10) : 100UL; if (capture_path && *capture_path && draws.size() >= minimum) { capture_written = true; try { std::unordered_map> textures; auto add_texture = [&](const std::string &name) { if (name.empty() || textures.count(name)) return; if (name.rfind("mem:", 0) == 0) { UIMemoryTexture mem_tex; if (UIRenderMemoryTexture(name, &mem_tex) && mem_tex.width > 0 && mem_tex.height > 0) { auto mtra = native_draw_capture::encode_raw_argb_as_mtra( static_cast(mem_tex.width), static_cast(mem_tex.height), mem_tex.argb.data()); if (!mtra.empty()) textures.emplace(name, std::move(mtra)); } return; } std::vector bytes; if (read_pack_texture(name, bytes)) textures.emplace(name, std::move(bytes)); }; for (const Render3DDraw &draw : draws) { add_texture(draw.texture0); add_texture(draw.texture1); } const auto &ui_commands = UIRenderCommands(); for (const UIRenderCommand &cmd : ui_commands) { if (cmd.kind == UIRenderCommand::Image) { add_texture(cmd.text); add_texture(cmd.mask); } } unsigned ui_w = 0, ui_h = 0; UIRenderGetSize(&ui_w, &ui_h); native_draw_capture::write( capture_path, draws, textures, ui_w ? ui_w : 960u, ui_h ? ui_h : 640u, ui_commands); UtilityFunctions::print( "native draw capture: ", capture_path, " draws=", static_cast(draws.size()), " ui_commands=", static_cast(ui_commands.size()), " textures=", static_cast(textures.size())); } catch (const std::exception &error) { UtilityFunctions::printerr("native draw capture failed: ", error.what()); } } } Array out; out.resize(static_cast(draws.size())); int64_t draw_index = 0; for (const Render3DDraw &draw : draws) { if (draw.clear_flags) continue; // back-buffer clears are consumed by the native renderer only Dictionary item; item["geometry_key"] = static_cast(draw.geometry_key); item["geometry_revision"] = static_cast(draw.geometry_revision); item["world"] = floats(draw.world, 16); item["view"] = floats(draw.view, 16); item["proj"] = floats(draw.proj, 16); item["texture0"] = String::utf8(draw.texture0.c_str()); item["texture1"] = String::utf8(draw.texture1.c_str()); item["pretransformed"] = draw.pretransformed; item["lines"] = draw.lines; // Stage-0 coordinates the D3D8 pipeline generates or transforms depend on per-draw matrices, // so such draws carry them outside the geometry cache. const bool stage0_texgen = (draw.texcoord_index[0] & 0xFFFF0000u) != 0 || (draw.texcoord_index[0] & 0xFFFFu) != 0 || draw.texture_transform_flags[0] != 0; if (stage0_texgen) item["geometry_key"] = static_cast(0); Dictionary geometry; if (draw.geometry_key != 0 && !stage0_texgen) { auto cached = geometry_cache.find(draw.geometry_key); if (cached != geometry_cache.end() && cached->second.revision == draw.geometry_revision) { cached->second.last_used = extraction; geometry = cached->second.arrays; } } if (geometry.is_empty()) { PackedVector3Array positions; positions.resize(static_cast(draw.positions.size() / 3)); if constexpr (sizeof(Vector3) == sizeof(float) * 3 && std::is_trivially_copyable_v) { if (!draw.positions.empty()) std::memcpy(positions.ptrw(), draw.positions.data(), draw.positions.size() * sizeof(float)); } else { Vector3 *dst = positions.ptrw(); for (int64_t i = 0; i < positions.size(); ++i) dst[i] = Vector3(draw.positions[i * 3], draw.positions[i * 3 + 1], draw.positions[i * 3 + 2]); } geometry["positions"] = positions; if (!draw.rhw.empty()) geometry["rhw"] = floats(draw.rhw.data(), static_cast(draw.rhw.size())); if (!draw.normals.empty()) { PackedVector3Array normals; normals.resize(static_cast(draw.normals.size() / 3)); if constexpr (sizeof(Vector3) == sizeof(float) * 3 && std::is_trivially_copyable_v) { std::memcpy(normals.ptrw(), draw.normals.data(), draw.normals.size() * sizeof(float)); } else { Vector3 *dst = normals.ptrw(); for (int64_t i = 0; i < normals.size(); ++i) dst[i] = Vector3(draw.normals[i * 3], draw.normals[i * 3 + 1], draw.normals[i * 3 + 2]); } geometry["normals"] = normals; } auto uvs = [](const std::vector &values) { PackedVector2Array out; out.resize(static_cast(values.size() / 2)); if constexpr (sizeof(Vector2) == sizeof(float) * 2 && std::is_trivially_copyable_v) { if (!values.empty()) std::memcpy(out.ptrw(), values.data(), values.size() * sizeof(float)); } else { Vector2 *dst = out.ptrw(); for (int64_t i = 0; i < out.size(); ++i) dst[i] = Vector2(values[i * 2], values[i * 2 + 1]); } return out; }; if (stage0_texgen) { std::vector stage_uv; Render3DStageTexcoords(draw, 0, stage_uv); geometry["uv0"] = uvs(stage_uv); } else if (!draw.uv0.empty()) geometry["uv0"] = uvs(draw.uv0); if (!draw.uv1.empty()) geometry["uv1"] = uvs(draw.uv1); if (!draw.diffuse.empty()) { PackedColorArray colors; colors.resize(static_cast(draw.diffuse.size())); for (int64_t i = 0; i < colors.size(); ++i) colors[i] = argb_color(draw.diffuse[i]); geometry["diffuse"] = colors; } PackedInt32Array indices; indices.resize(static_cast(draw.indices.size())); for (int64_t i = 0; i < indices.size(); ++i) indices[i] = static_cast(draw.indices[i]); geometry["indices"] = indices; if (draw.geometry_key != 0 && !stage0_texgen) geometry_cache[draw.geometry_key] = {draw.geometry_revision, extraction, geometry}; } item.merge(geometry); item["alpha_blend"] = static_cast(draw.alpha_blend); item["src_blend"] = static_cast(draw.src_blend); item["dest_blend"] = static_cast(draw.dest_blend); item["alpha_test"] = static_cast(draw.alpha_test); item["alpha_ref"] = static_cast(draw.alpha_ref); item["alpha_func"] = static_cast(draw.alpha_func); item["cull_mode"] = static_cast(draw.cull_mode); item["z_enable"] = static_cast(draw.z_enable); item["z_write"] = static_cast(draw.z_write); item["z_func"] = static_cast(draw.z_func); item["lighting"] = static_cast(draw.lighting); item["texture_factor"] = static_cast(draw.texture_factor); bool uses_tf = false; for (int s = 0; s < 2; ++s) { if (draw.color_op[s] > 1 && (((draw.color_arg1[s] & 0xF) == 3) || ((draw.color_arg2[s] & 0xF) == 3))) uses_tf = true; if (draw.alpha_op[s] > 1 && (((draw.alpha_arg1[s] & 0xF) == 3) || ((draw.alpha_arg2[s] & 0xF) == 3))) uses_tf = true; } item["uses_tf"] = uses_tf; item["fog_enable"] = static_cast(draw.fog_enable); item["color_op"] = static_cast(draw.color_op[0]); item["alpha_op"] = static_cast(draw.alpha_op[0]); item["material_diffuse"] = Color(draw.material_diffuse[0], draw.material_diffuse[1], draw.material_diffuse[2], draw.material_diffuse[3]); item["material_ambient"] = Color(draw.material_ambient[0], draw.material_ambient[1], draw.material_ambient[2], draw.material_ambient[3]); item["material_emissive"] = Color(draw.material_emissive[0], draw.material_emissive[1], draw.material_emissive[2], draw.material_emissive[3]); item["light0"] = draw.light0; item["light0_direction"] = Vector3(draw.light0_direction[0], draw.light0_direction[1], draw.light0_direction[2]); item["light0_diffuse"] = Color(draw.light0_diffuse[0], draw.light0_diffuse[1], draw.light0_diffuse[2], draw.light0_diffuse[3]); item["light0_ambient"] = Color(draw.light0_ambient[0], draw.light0_ambient[1], draw.light0_ambient[2], draw.light0_ambient[3]); item["ambient"] = argb_color(draw.ambient); PackedFloat32Array vp; vp.resize(4); vp[0] = draw.viewport[0]; vp[1] = draw.viewport[1]; vp[2] = draw.viewport[2]; vp[3] = draw.viewport[3]; item["viewport"] = vp; out[draw_index++] = item; } out.resize(draw_index); if (extraction % 120 == 0) { for (auto it = geometry_cache.begin(); it != geometry_cache.end();) { if (extraction - it->second.last_used > 120) it = geometry_cache.erase(it); else ++it; } } return out; } Array Metin2PythonHost::audio_commands() { Array out; for (const auto &cmd : DrainAudioCommands()) { Dictionary item; switch (cmd.type) { case AudioCommand::PlaySound2D: item["type"] = "play_sound_2d"; break; case AudioCommand::PlaySound3D: item["type"] = "play_sound_3d"; break; case AudioCommand::StopSound3D: item["type"] = "stop_sound_3d"; break; case AudioCommand::StopAllSound3D: item["type"] = "stop_all_sound_3d"; break; case AudioCommand::PlayMusic: item["type"] = "play_music"; break; case AudioCommand::FadeOutMusic: item["type"] = "fade_out_music"; break; case AudioCommand::FadeOutAllMusic: item["type"] = "fade_out_all_music"; break; case AudioCommand::SetSoundVolume: item["type"] = "set_sound_volume"; break; case AudioCommand::SetMusicVolume: item["type"] = "set_music_volume"; break; case AudioCommand::FadeInMusic: item["type"] = "fade_in_music"; break; case AudioCommand::FadeLimitOutMusic: item["type"] = "fade_limit_out_music"; break; case AudioCommand::SetSoundVolume3D: item["type"] = "set_sound_volume_3d"; break; default: continue; } item["filename"] = String::utf8(cmd.filename.c_str()); item["x"] = cmd.x; item["y"] = cmd.y; item["z"] = cmd.z; item["volume"] = cmd.volume; item["speed"] = cmd.speed; item["play_count"] = cmd.play_count; item["id"] = cmd.id; out.append(item); } return out; } String Metin2PythonHost::current_map_name() { return String::utf8(PythonBoot::CurrentMapName().c_str()); } void Metin2PythonHost::stop() { PythonBoot::Stop(); } #else // Built without -DMTGODOT_EMBED_PYTHON=ON: the class is still registered so GDScript can tell "no // interpreter in this build" apart from "the interpreter failed to start". String Metin2PythonHost::start(const String &) { return String("this build has no embedded Python (-DMTGODOT_EMBED_PYTHON=ON)"); } bool Metin2PythonHost::is_running() { return false; } String Metin2PythonHost::run_main_script(const String &) { return String("this build has no embedded Python (-DMTGODOT_EMBED_PYTHON=ON)"); } String Metin2PythonHost::run_line(const String &) { return String("this build has no embedded Python (-DMTGODOT_EMBED_PYTHON=ON)"); } String Metin2PythonHost::evaluate(const String &) { return String(); } bool Metin2PythonHost::is_app_looping() { return false; } void Metin2PythonHost::set_ui_size(int, int) {} void Metin2PythonHost::ui_mouse_move(int, int) {} void Metin2PythonHost::ui_mouse_button(int, bool, int, int) {} void Metin2PythonHost::ui_mouse_wheel(int) {} void Metin2PythonHost::ui_key(int, bool) {} void Metin2PythonHost::ui_char(int) {} void Metin2PythonHost::ui_ime_key(int) {} void Metin2PythonHost::ui_update() {} Array Metin2PythonHost::ui_render_commands() { return Array(); } Array Metin2PythonHost::ui_render_commands_batched() { return Array(); } bool Metin2PythonHost::has_3d_draws() { return false; } Ref Metin2PythonHost::memory_texture(const String &, int64_t) { return Ref(); } Array Metin2PythonHost::render3d_draws() { return Array(); } Array Metin2PythonHost::audio_commands() { return Array(); } String Metin2PythonHost::current_map_name() { return String(); } void Metin2PythonHost::stop() { } #endif void Metin2PythonHost::_bind_methods() { ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("start", "stdlib_path"), &Metin2PythonHost::start, DEFVAL(String())); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("is_running"), &Metin2PythonHost::is_running); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("evaluate", "expression"), &Metin2PythonHost::evaluate); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("run_main_script", "command_line"), &Metin2PythonHost::run_main_script, DEFVAL(String())); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("run_line", "source"), &Metin2PythonHost::run_line); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("is_app_looping"), &Metin2PythonHost::is_app_looping); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("set_ui_size", "width", "height"), &Metin2PythonHost::set_ui_size); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_move", "x", "y"), &Metin2PythonHost::ui_mouse_move); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_button", "button", "pressed", "x", "y"), &Metin2PythonHost::ui_mouse_button); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_wheel", "delta"), &Metin2PythonHost::ui_mouse_wheel); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_key", "key", "pressed"), &Metin2PythonHost::ui_key); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_char", "codepoint"), &Metin2PythonHost::ui_char); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_ime_key", "vkey"), &Metin2PythonHost::ui_ime_key); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_update"), &Metin2PythonHost::ui_update); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_render_commands"), &Metin2PythonHost::ui_render_commands); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_render_commands_batched"), &Metin2PythonHost::ui_render_commands_batched); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("has_3d_draws"), &Metin2PythonHost::has_3d_draws); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("memory_texture", "name", "known_revision"), &Metin2PythonHost::memory_texture, DEFVAL(-1)); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("render3d_draws"), &Metin2PythonHost::render3d_draws); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("audio_commands"), &Metin2PythonHost::audio_commands); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("current_map_name"), &Metin2PythonHost::current_map_name); ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("stop"), &Metin2PythonHost::stop); } } // namespace mtgodot