#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 #endif #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_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(); } Array Metin2PythonHost::ui_render_commands() { PythonBoot::UIRender(); Array out; for (const auto &command : UIRenderCommands()) { Dictionary item; item["kind"] = command.kind == UIRenderCommand::GradientBar ? "gradient_bar" : (command.kind == UIRenderCommand::Bar ? "bar" : (command.kind == UIRenderCommand::Line ? "line" : (command.kind == UIRenderCommand::Image ? "image" : "text"))); item["x1"] = command.x1; item["y1"] = command.y1; item["x2"] = command.x2; item["y2"] = command.y2; item["argb"] = static_cast(command.argb); if (command.kind == UIRenderCommand::GradientBar) item["end_argb"] = static_cast(command.end_argb); item["clip_x1"] = command.clip_x1; item["clip_y1"] = command.clip_y1; item["clip_x2"] = command.clip_x2; item["clip_y2"] = command.clip_y2; if (command.kind == UIRenderCommand::Text || command.kind == UIRenderCommand::Image) item["text"] = String::utf8(command.text.c_str()); if (command.quad) { PackedVector2Array quad; for (int i = 0; i < 4; ++i) quad.push_back(Vector2(command.qx[i], command.qy[i])); item["quad"] = quad; item["uv"] = PackedVector2Array({Vector2(command.su, command.sv), Vector2(command.eu, command.sv), Vector2(command.su, command.ev), Vector2(command.eu, command.ev)}); item["blend"] = command.blend; if (!command.mask.empty()) { item["mask"] = String::utf8(command.mask.c_str()); PackedVector2Array mask_uv; for (int i = 0; i < 4; ++i) mask_uv.push_back(Vector2(command.mu[i], command.mv[i])); item["mask_uv"] = mask_uv; } } out.push_back(item); } return out; } 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); } } // namespace Array Metin2PythonHost::render3d_draws() { Array out; for (const Render3DDraw &draw : Render3DDraws()) { Dictionary item; 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; PackedVector3Array positions; positions.resize(static_cast(draw.positions.size() / 3)); for (int64_t i = 0; i < positions.size(); ++i) positions[i] = Vector3(draw.positions[i * 3], draw.positions[i * 3 + 1], draw.positions[i * 3 + 2]); item["positions"] = positions; if (!draw.rhw.empty()) item["rhw"] = floats(draw.rhw.data(), static_cast(draw.rhw.size())); if (!draw.normals.empty()) { PackedVector3Array normals; normals.resize(static_cast(draw.normals.size() / 3)); for (int64_t i = 0; i < normals.size(); ++i) normals[i] = Vector3(draw.normals[i * 3], draw.normals[i * 3 + 1], draw.normals[i * 3 + 2]); item["normals"] = normals; } auto uvs = [](const std::vector &values) { PackedVector2Array out; out.resize(static_cast(values.size() / 2)); for (int64_t i = 0; i < out.size(); ++i) out[i] = Vector2(values[i * 2], values[i * 2 + 1]); return out; }; if (!draw.uv0.empty()) item["uv0"] = uvs(draw.uv0); if (!draw.uv1.empty()) item["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]); item["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]); item["indices"] = indices; 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); 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); out.push_back(item); } 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_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(); } 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_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("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