diff --git a/spine-godot/BUGFIX_SUMMARY_V2.md b/spine-godot/BUGFIX_SUMMARY_V2.md deleted file mode 100644 index acc841cf4..000000000 --- a/spine-godot/BUGFIX_SUMMARY_V2.md +++ /dev/null @@ -1,172 +0,0 @@ -# Crash Fix: SpineSkeletonDataResource Destructor (v2) - -## TL;DR - -Fixed segmentation fault during Godot editor shutdown by using `ObjectDB::get_instance()` to safely validate the `EditorFileSystem` pointer before attempting to disconnect the signal in the destructor. The original code used a raw pointer that could become dangling during shutdown; the fix stores the `ObjectID` at construction time and validates it in the destructor. - -**Changed files:** -- `spine_godot/SpineSkeletonDataResource.h` - Added `ObjectID editor_file_system_id` member variable -- `spine_godot/SpineSkeletonDataResource.cpp` - Modified constructor and destructor - ---- - -## Context - -Mario requested a reference for the claim that removing the disconnect code entirely was safe. Upon investigation, that claim was not fully accurate. Godot's automatic signal cleanup in `Object::~Object()` also calls methods on the signal owner, which could crash if the owner is already destroyed. - -The proper fix is to retain the disconnect logic but use Godot's `ObjectDB` system to safely validate that `EditorFileSystem` still exists before calling any methods on it. - ---- - -## The Problem - -The Godot editor crashed with signal 11 (SIGSEGV) during shutdown. The crash occurred in the `SpineSkeletonDataResource` destructor when attempting to disconnect from the `EditorFileSystem` singleton's "resources_reimported" signal. - -**Original crash backtrace:** -``` -[14] SpineSkeletonDataResource::~SpineSkeletonDataResource() - (spine_godot/SpineSkeletonDataResource.cpp:255) -``` - -**Root cause:** During editor shutdown, singletons are destroyed in an undefined order. The destructor used `get_editor_file_system()` which returns a raw pointer. If `EditorFileSystem` was destroyed before `SpineSkeletonDataResource` objects, the pointer became dangling. Calling `efs->is_connected()` on a dangling pointer caused the segfault. - -The check `if (efs)` only verifies the pointer is non-null, not that the memory it points to is valid. - ---- - -## The Solution - -Use Godot's `ObjectDB` system to safely validate object existence: - -1. Store the `ObjectID` of `EditorFileSystem` when connecting the signal (in constructor) -2. Use `ObjectDB::get_instance(id)` in the destructor to check if the object still exists -3. Only proceed with disconnect if the object is valid - -`ObjectDB::get_instance()` is safe to call even if the object has been destroyed - it returns `nullptr` in that case, rather than accessing freed memory. - ---- - -## Code Changes - -### Header file (SpineSkeletonDataResource.h) - -**Added member variable (inside private section, under TOOLS_ENABLED):** -```cpp -#ifdef TOOLS_ENABLED - // Store the ObjectID of EditorFileSystem to safely validate it in destructor. - // Raw pointers to singletons can become dangling during editor shutdown, - // but ObjectID can be safely validated via ObjectDB::get_instance(). - ObjectID editor_file_system_id; -#endif -``` - -### Source file (SpineSkeletonDataResource.cpp) - -**Constructor - store the ObjectID when connecting:** -```cpp -SpineSkeletonDataResource::SpineSkeletonDataResource() - : default_mix(0), skeleton_data(nullptr), animation_state_data(nullptr) { - -#ifdef TOOLS_ENABLED -#if VERSION_MAJOR > 3 - if (Engine::get_singleton()->is_editor_hint()) { - EditorFileSystem *efs = get_editor_file_system(); - if (efs) { - // Store the ObjectID for safe validation in destructor - editor_file_system_id = efs->get_instance_id(); - efs->connect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported)); - } - } -#else - if (Engine::get_singleton()->is_editor_hint()) { - EditorFileSystem *efs = EditorFileSystem::get_singleton(); - if (efs) { - // Store the ObjectID for safe validation in destructor - editor_file_system_id = efs->get_instance_id(); - efs->connect("resources_reimported", this, "_on_resources_reimported"); - } - } -#endif -#endif -} -``` - -**Destructor - validate via ObjectDB before disconnecting:** -```cpp -SpineSkeletonDataResource::~SpineSkeletonDataResource() { -#ifdef TOOLS_ENABLED -#if VERSION_MAJOR > 3 - if (Engine::get_singleton()->is_editor_hint()) { - // Use ObjectDB::get_instance() to safely check if EditorFileSystem still exists. - // This avoids the dangling pointer problem during editor shutdown where - // EditorFileSystem may be destroyed before SpineSkeletonDataResource objects. - EditorFileSystem *efs = Object::cast_to(ObjectDB::get_instance(editor_file_system_id)); - if (efs && efs->is_connected("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported))) { - efs->disconnect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported)); - } - } -#else - if (Engine::get_singleton()->is_editor_hint()) { - // Use ObjectDB::get_instance() to safely check if EditorFileSystem still exists. - EditorFileSystem *efs = Object::cast_to(ObjectDB::get_instance(editor_file_system_id)); - if (efs && efs->is_connected("resources_reimported", this, "_on_resources_reimported")) { - efs->disconnect("resources_reimported", this, "_on_resources_reimported"); - } - } -#endif -#endif - - delete skeleton_data; - delete animation_state_data; -} -``` - ---- - -## Why This Works - -1. **ObjectID is just a uint64_t** - It's safe to store and doesn't hold a reference to the object -2. **ObjectDB::get_instance() is safe** - It performs a lookup in Godot's object registry and returns `nullptr` if the object no longer exists, without accessing any potentially-freed memory -3. **The pattern is used throughout Godot** - See `godot/core/object/undo_redo.cpp`, `godot/core/object/message_queue.cpp`, and `godot/core/object/callable_method_pointer.h` for examples - -**From godot/core/object/undo_redo.cpp:355:** -```cpp -Object *obj = ObjectDB::get_instance(op.object); -if (!obj) { //may have been deleted and this is fine - continue; -} -``` - ---- - -## Why Not Just Remove the Disconnect? - -Mario correctly questioned whether removing the disconnect entirely was safe. Investigation revealed: - -1. Godot's `Object::~Object()` does perform automatic signal cleanup (lines 2186-2198 in object.cpp) -2. However, that cleanup also calls `c.signal.get_object()->_disconnect(...)` which accesses the signal owner -3. If the signal owner is destroyed first, the automatic cleanup would also crash - -The automatic cleanup uses `c.callable.get_object()` which does perform ObjectDB validation, but the crash was happening in our custom destructor code before the automatic cleanup ran. - -Retaining explicit disconnect with proper validation is the safer approach and follows patterns used elsewhere in the Godot codebase. - ---- - -## Testing Checklist - -- [ ] Editor starts without errors -- [ ] Editor closes cleanly without crashes -- [ ] Hot-reload works (modify and reimport a Spine asset while editor is running) -- [ ] Normal gameplay with Spine assets works -- [ ] Test with both module build and GDExtension build -- [ ] Test with C# build if applicable - ---- - -## References - -- Godot ObjectDB implementation: `godot/core/object/object.h:1044-1064` -- Godot Object destructor signal cleanup: `godot/core/object/object.cpp:2135-2198` -- Example usage in undo_redo: `godot/core/object/undo_redo.cpp:355` -- GitHub Issue on signal disconnection: https://github.com/godotengine/godot/issues/70414 diff --git a/spine-godot/SpineSkeletonDataResource_old.cpp b/spine-godot/SpineSkeletonDataResource_old.cpp deleted file mode 100644 index 62a3b2246..000000000 --- a/spine-godot/SpineSkeletonDataResource_old.cpp +++ /dev/null @@ -1,853 +0,0 @@ -/****************************************************************************** - * Spine Runtimes License Agreement - * Last updated April 5, 2025. Replaces all prior versions. - * - * Copyright (c) 2013-2025, Esoteric Software LLC - * - * Integration of the Spine Runtimes into software or otherwise creating - * derivative works of the Spine Runtimes is permitted under the terms and - * conditions of Section 2 of the Spine Editor License Agreement: - * http://esotericsoftware.com/spine-editor-license - * - * Otherwise, it is permitted to integrate the Spine Runtimes into software - * or otherwise create derivative works of the Spine Runtimes (collectively, - * "Products"), provided that each user of the Products must obtain their own - * Spine Editor license and redistribution of the Products in any form must - * include this license and copyright notice. - * - * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, - * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#include "SpineSkeletonDataResource.h" -#include "SpineCommon.h" - -#ifdef SPINE_GODOT_EXTENSION -#include -#include -#ifdef TOOLS_ENABLED -#include -#endif -#else -#if VERSION_MAJOR > 3 -#include "core/config/engine.h" -#ifdef TOOLS_ENABLED -#include "editor/editor_interface.h" -#endif -#else -#include "core/engine.h" -#endif -#include -#endif - -#ifdef TOOLS_ENABLED -#ifdef SPINE_GODOT_EXTENSION -#include -#else -#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5) -#include "editor/file_system/editor_file_system.h" -#else -#include "editor/editor_file_system.h" -#endif -#endif -#endif - -void SpineAnimationMix::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_from", "from"), - &SpineAnimationMix::set_from); - ClassDB::bind_method(D_METHOD("get_from"), &SpineAnimationMix::get_from); - ClassDB::bind_method(D_METHOD("set_to", "to"), &SpineAnimationMix::set_to); - ClassDB::bind_method(D_METHOD("get_to"), &SpineAnimationMix::get_to); - ClassDB::bind_method(D_METHOD("set_mix", "mix"), &SpineAnimationMix::set_mix); - ClassDB::bind_method(D_METHOD("get_mix"), &SpineAnimationMix::get_mix); - - ADD_PROPERTY(PropertyInfo(Variant::STRING, "from"), "set_from", "get_from"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "to"), "set_to", "get_to"); -#if VERSION_MAJOR > 3 - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mix"), "set_mix", "get_mix"); -#else - ADD_PROPERTY(PropertyInfo(Variant::REAL, "mix"), "set_mix", "get_mix"); -#endif -} - -SpineAnimationMix::SpineAnimationMix() : from(""), to(""), mix(0) {} - -void SpineAnimationMix::set_from(const String &_from) { this->from = _from; } - -String SpineAnimationMix::get_from() { return from; } - -void SpineAnimationMix::set_to(const String &_to) { this->to = _to; } - -String SpineAnimationMix::get_to() { return to; } - -void SpineAnimationMix::set_mix(float _mix) { this->mix = _mix; } - -float SpineAnimationMix::get_mix() { return mix; } - -void SpineSkeletonDataResource::_bind_methods() { - ClassDB::bind_method(D_METHOD("is_skeleton_data_loaded"), - &SpineSkeletonDataResource::is_skeleton_data_loaded); - ClassDB::bind_method(D_METHOD("set_atlas_res", "atlas_res"), - &SpineSkeletonDataResource::set_atlas_res); - ClassDB::bind_method(D_METHOD("get_atlas_res"), - &SpineSkeletonDataResource::get_atlas_res); - ClassDB::bind_method(D_METHOD("set_skeleton_file_res", "skeleton_file_res"), - &SpineSkeletonDataResource::set_skeleton_file_res); - ClassDB::bind_method(D_METHOD("get_skeleton_file_res"), - &SpineSkeletonDataResource::get_skeleton_file_res); - ClassDB::bind_method(D_METHOD("set_default_mix", "default_mix"), - &SpineSkeletonDataResource::set_default_mix); - ClassDB::bind_method(D_METHOD("get_default_mix"), - &SpineSkeletonDataResource::get_default_mix); - ClassDB::bind_method(D_METHOD("set_animation_mixes", "mixes"), - &SpineSkeletonDataResource::set_animation_mixes); - ClassDB::bind_method(D_METHOD("get_animation_mixes"), - &SpineSkeletonDataResource::get_animation_mixes); - - // Spine API - ClassDB::bind_method(D_METHOD("find_bone", "bone_name"), - &SpineSkeletonDataResource::find_bone); - ClassDB::bind_method(D_METHOD("find_slot", "slot_name"), - &SpineSkeletonDataResource::find_slot); - ClassDB::bind_method(D_METHOD("find_skin", "skin_name"), - &SpineSkeletonDataResource::find_skin); - ClassDB::bind_method(D_METHOD("find_event", "event_data_name"), - &SpineSkeletonDataResource::find_event); - ClassDB::bind_method(D_METHOD("find_animation", "animation_name"), - &SpineSkeletonDataResource::find_animation); - ClassDB::bind_method(D_METHOD("find_ik_constraint_data", "constraint_name"), - &SpineSkeletonDataResource::find_ik_constraint); - ClassDB::bind_method( - D_METHOD("find_transform_constraint_data", "constraint_name"), - &SpineSkeletonDataResource::find_transform_constraint); - ClassDB::bind_method(D_METHOD("find_path_constraint_data", "constraint_name"), - &SpineSkeletonDataResource::find_path_constraint); - ClassDB::bind_method(D_METHOD("find_physics_constraint_data", "constraint_name"), - &SpineSkeletonDataResource::find_physics_constraint); - ClassDB::bind_method(D_METHOD("get_skeleton_name"), - &SpineSkeletonDataResource::get_skeleton_name); - ClassDB::bind_method(D_METHOD("get_bones"), - &SpineSkeletonDataResource::get_bones); - ClassDB::bind_method(D_METHOD("get_slots"), - &SpineSkeletonDataResource::get_slots); - ClassDB::bind_method(D_METHOD("get_skins"), - &SpineSkeletonDataResource::get_skins); - ClassDB::bind_method(D_METHOD("get_default_skin"), - &SpineSkeletonDataResource::get_default_skin); - ClassDB::bind_method(D_METHOD("set_default_skin", "skin"), - &SpineSkeletonDataResource::set_default_skin); - ClassDB::bind_method(D_METHOD("get_events"), - &SpineSkeletonDataResource::get_events); - ClassDB::bind_method(D_METHOD("get_animations"), - &SpineSkeletonDataResource::get_animations); - ClassDB::bind_method(D_METHOD("get_ik_constraints"), - &SpineSkeletonDataResource::get_ik_constraints); - ClassDB::bind_method(D_METHOD("get_transform_constraints"), - &SpineSkeletonDataResource::get_transform_constraints); - ClassDB::bind_method(D_METHOD("get_path_constraints"), - &SpineSkeletonDataResource::get_path_constraints); - ClassDB::bind_method(D_METHOD("get_physics_constraints"), - &SpineSkeletonDataResource::get_physics_constraints); - ClassDB::bind_method(D_METHOD("get_x"), &SpineSkeletonDataResource::get_x); - ClassDB::bind_method(D_METHOD("get_y"), &SpineSkeletonDataResource::get_y); - ClassDB::bind_method(D_METHOD("get_width"), - &SpineSkeletonDataResource::get_width); - ClassDB::bind_method(D_METHOD("get_height"), - &SpineSkeletonDataResource::get_height); - ClassDB::bind_method(D_METHOD("get_version"), - &SpineSkeletonDataResource::get_version); - ClassDB::bind_method(D_METHOD("get_hash"), - &SpineSkeletonDataResource::get_hash); - ClassDB::bind_method(D_METHOD("get_images_path"), - &SpineSkeletonDataResource::get_images_path); - ClassDB::bind_method(D_METHOD("get_audio_path"), - &SpineSkeletonDataResource::get_audio_path); - ClassDB::bind_method(D_METHOD("get_fps"), - &SpineSkeletonDataResource::get_fps); - ClassDB::bind_method(D_METHOD("get_reference_scale"), - &SpineSkeletonDataResource::get_reference_scale); - ClassDB::bind_method(D_METHOD("set_reference_scale", "reference_scale"), - &SpineSkeletonDataResource::set_reference_scale); - ClassDB::bind_method(D_METHOD("update_skeleton_data"), - &SpineSkeletonDataResource::update_skeleton_data); - - ADD_SIGNAL(MethodInfo("skeleton_data_changed")); - ADD_SIGNAL(MethodInfo("_internal_spine_objects_invalidated")); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "atlas_res", - PropertyHint::PROPERTY_HINT_RESOURCE_TYPE, - "SpineAtlasResource"), - "set_atlas_res", "get_atlas_res"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skeleton_file_res", - PropertyHint::PROPERTY_HINT_RESOURCE_TYPE, - "SpineSkeletonFileResource"), - "set_skeleton_file_res", "get_skeleton_file_res"); -#if VERSION_MAJOR > 3 - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_mix"), "set_default_mix", - "get_default_mix"); -#else - ADD_PROPERTY(PropertyInfo(Variant::REAL, "default_mix"), "set_default_mix", - "get_default_mix"); -#endif - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animation_mixes"), - "set_animation_mixes", "get_animation_mixes"); - -#ifdef TOOLS_ENABLED -#if VERSION_MAJOR > 3 - ClassDB::bind_method(D_METHOD("_on_resources_reimported", "resources"), - &SpineSkeletonDataResource::_on_resources_reimported); -#else - ClassDB::bind_method(D_METHOD("_on_resources_reimported", "resources"), - &SpineSkeletonDataResource::_on_resources_reimported); -#endif -#endif -} - -#ifdef TOOLS_ENABLED -EditorFileSystem *get_editor_file_system() { -#ifdef SPINE_GODOT_EXTENSION - EditorInterface *editor_interface = EditorInterface::get_singleton(); - if (editor_interface) { - return editor_interface->get_resource_filesystem(); - } - return nullptr; -#else - return EditorFileSystem::get_singleton(); -#endif -} -#endif - -SpineSkeletonDataResource::SpineSkeletonDataResource() - : default_mix(0), skeleton_data(nullptr), animation_state_data(nullptr) { - -#ifdef TOOLS_ENABLED -#if VERSION_MAJOR > 3 - if (Engine::get_singleton()->is_editor_hint()) { - EditorFileSystem *efs = get_editor_file_system(); - if (efs) { - efs->connect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported)); - } - } -#else - if (Engine::get_singleton()->is_editor_hint()) { - EditorFileSystem *efs = EditorFileSystem::get_singleton(); - if (efs) { - efs->connect("resources_reimported", this, "_on_resources_reimported"); - } - } -#endif -#endif -} - -SpineSkeletonDataResource::~SpineSkeletonDataResource() { -/*#ifdef TOOLS_ENABLED -#if VERSION_MAJOR > 3 - if (Engine::get_singleton()->is_editor_hint()) { - EditorFileSystem *efs = get_editor_file_system(); - if (efs && efs->is_connected("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported))) { - efs->disconnect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported)); - } - } -#else - if (Engine::get_singleton()->is_editor_hint()) { - EditorFileSystem *efs = EditorFileSystem::get_singleton(); - if (efs && efs->is_connected("resources_reimported", this, "_on_resources_reimported")) { - efs->disconnect("resources_reimported", this, "_on_resources_reimported"); - } - } -#endif -#endif -*/ - delete skeleton_data; - delete animation_state_data; -} - -#ifdef TOOLS_ENABLED -#if VERSION_MAJOR > 3 -void SpineSkeletonDataResource::_on_resources_reimported(const PackedStringArray &resources) { - for (int i = 0; i < resources.size(); i++) { - if (atlas_res.is_valid() && atlas_res->get_path() == resources[i]) { -#ifdef SPINE_GODOT_EXTENSION - atlas_res = ResourceLoader::get_singleton()->load(resources[i], "SpineAtlasResource", ResourceLoader::CACHE_MODE_IGNORE); -#else - atlas_res = ResourceLoader::load(resources[i], "SpineAtlasResource", ResourceFormatLoader::CACHE_MODE_IGNORE); -#endif - update_skeleton_data(); - } else if (skeleton_file_res.is_valid() && skeleton_file_res->get_path() == resources[i]) { -#ifdef SPINE_GODOT_EXTENSION - skeleton_file_res = ResourceLoader::get_singleton()->load(resources[i], "SpineSkeletonFileResource", ResourceLoader::CACHE_MODE_IGNORE); -#else - skeleton_file_res = ResourceLoader::load(resources[i], "SpineSkeletonFileResource", ResourceFormatLoader::CACHE_MODE_IGNORE); -#endif - update_skeleton_data(); - } - } -} -#else -void SpineSkeletonDataResource::_on_resources_reimported(const PoolStringArray &resources) { - for (int i = 0; i < resources.size(); i++) { - if (atlas_res.is_valid() && atlas_res->get_path() == resources[i]) { - atlas_res = ResourceLoader::load(resources[i]); - update_skeleton_data(); - } else if (skeleton_file_res.is_valid() && skeleton_file_res->get_path() == resources[i]) { - skeleton_file_res = ResourceLoader::load(resources[i]); - update_skeleton_data(); - } - } -} -#endif -#endif - -void SpineSkeletonDataResource::update_skeleton_data() { - if (skeleton_data) { - delete skeleton_data; - skeleton_data = nullptr; - } - if (animation_state_data) { - delete animation_state_data; - animation_state_data = nullptr; - } - - emit_signal(SNAME("_internal_spine_objects_invalidated")); - - if (atlas_res.is_valid() && skeleton_file_res.is_valid()) { - load_resources(atlas_res->get_spine_atlas(), skeleton_file_res->get_json(), - skeleton_file_res->get_binary()); - } - emit_signal(SNAME("skeleton_data_changed")); -#ifdef TOOLS_ENABLED - NOTIFY_PROPERTY_LIST_CHANGED(); -#endif -} - -#ifdef SPINE_GODOT_EXTENSION -void SpineSkeletonDataResource::load_resources(spine::Atlas *atlas, - const String &json, - const PackedByteArray &binary) { -#else -void SpineSkeletonDataResource::load_resources(spine::Atlas *atlas, - const String &json, - const Vector &binary) { -#endif - if ((EMPTY(json) && EMPTY(binary)) || atlas == nullptr) - return; - - spine::SkeletonData *data; - if (!EMPTY(json)) { - spine::SkeletonJson skeletonJson(atlas); - data = skeletonJson.readSkeletonData(json.utf8().ptr()); - if (!data) { - ERR_PRINT(String("Error while loading skeleton data: ") + get_path()); - ERR_PRINT(String("Error message: ") + skeletonJson.getError().buffer()); - return; - } - } else { - spine::SkeletonBinary skeletonBinary(atlas); - data = skeletonBinary.readSkeletonData(binary.ptr(), binary.size()); - if (!data) { - ERR_PRINT(String("Error while loading skeleton data: ") + get_path()); - ERR_PRINT(String("Error message: ") + skeletonBinary.getError().buffer()); - return; - } - } - skeleton_data = data; - animation_state_data = new spine::AnimationStateData(data); - update_mixes(); -} - -bool SpineSkeletonDataResource::is_skeleton_data_loaded() const { - return skeleton_data != nullptr; -} - -void SpineSkeletonDataResource::set_atlas_res( - const Ref &atlas) { - atlas_res = atlas; - update_skeleton_data(); -} - -Ref SpineSkeletonDataResource::get_atlas_res() { - return atlas_res; -} - -void SpineSkeletonDataResource::set_skeleton_file_res( - const Ref &skeleton_file) { - skeleton_file_res = skeleton_file; - update_skeleton_data(); -} - -Ref -SpineSkeletonDataResource::get_skeleton_file_res() { - return skeleton_file_res; -} - -#ifdef SPINE_GODOT_EXTENSION -void SpineSkeletonDataResource::get_animation_names(PackedStringArray &animation_names) const { -#else -void SpineSkeletonDataResource::get_animation_names(Vector &animation_names) const { -#endif - animation_names.clear(); - if (!is_skeleton_data_loaded()) - return; - auto animations = skeleton_data->getAnimations(); - for (size_t i = 0; i < animations.size(); ++i) { - auto animation = animations[i]; - String name; -#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5) - name = String::utf8(animation->getName().buffer()); -#else - name.parse_utf8(animation->getName().buffer()); -#endif - animation_names.push_back(name); - } -} - -#ifdef SPINE_GODOT_EXTENSION -void SpineSkeletonDataResource::get_skin_names(PackedStringArray &skin_names) const { -#else -void SpineSkeletonDataResource::get_skin_names(Vector &skin_names) const { -#endif - skin_names.clear(); - if (!is_skeleton_data_loaded()) - return; - auto skins = skeleton_data->getSkins(); - for (size_t i = 0; i < skins.size(); ++i) { - auto skin = skins[i]; - String name; -#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5) - name = String::utf8(skin->getName().buffer()); -#else - name.parse_utf8(skin->getName().buffer()); -#endif - skin_names.push_back(name); - } -} - -#ifdef SPINE_GODOT_EXTENSION -void SpineSkeletonDataResource::get_slot_names(PackedStringArray &slot_names) { -#else -void SpineSkeletonDataResource::get_slot_names(Vector &slot_names) { -#endif - slot_names.clear(); - if (!is_skeleton_data_loaded()) - return; - auto slots = skeleton_data->getSlots(); - for (size_t i = 0; i < slots.size(); ++i) { - auto slot = slots[i]; - String name; -#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5) - name = String::utf8(slot->getName().buffer()); -#else - name.parse_utf8(slot->getName().buffer()); -#endif - slot_names.push_back(name); - } -} - -#ifdef SPINE_GODOT_EXTENSION -void SpineSkeletonDataResource::get_bone_names(PackedStringArray &bone_names) { -#else -void SpineSkeletonDataResource::get_bone_names(Vector &bone_names) { -#endif - bone_names.clear(); - if (!is_skeleton_data_loaded()) - return; - auto bones = skeleton_data->getBones(); - for (size_t i = 0; i < bones.size(); ++i) { - auto bone = bones[i]; - String name; -#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5) - name = String::utf8(bone->getName().buffer()); -#else - name.parse_utf8(bone->getName().buffer()); -#endif - bone_names.push_back(name); - } -} - -void SpineSkeletonDataResource::set_default_mix(float _default_mix) { - this->default_mix = _default_mix; - update_mixes(); -} - -float SpineSkeletonDataResource::get_default_mix() { return default_mix; } - -void SpineSkeletonDataResource::set_animation_mixes(Array _animation_mixes) { - for (int i = 0; i < _animation_mixes.size(); i++) { - auto objectId = Object::cast_to(_animation_mixes[0]); - if (objectId) { - ERR_PRINT("Live-editing of animation mixes is not supported."); - return; - } - } - - this->animation_mixes = _animation_mixes; - update_mixes(); -} - -Array SpineSkeletonDataResource::get_animation_mixes() { - return animation_mixes; -} - -void SpineSkeletonDataResource::update_mixes() { - if (!is_skeleton_data_loaded()) - return; - animation_state_data->clear(); - animation_state_data->setDefaultMix(default_mix); - for (int i = 0; i < animation_mixes.size(); i++) { - Ref mix = animation_mixes[i]; - spine::Animation *from = - skeleton_data->findAnimation(mix->get_from().utf8().ptr()); - spine::Animation *to = - skeleton_data->findAnimation(mix->get_to().utf8().ptr()); - if (!from) { - ERR_PRINT(vformat("Failed to set animation mix %s->%s. Animation %s does " - "not exist in skeleton.", - from, to, from)); - continue; - } - if (!to) { - ERR_PRINT(vformat("Failed to set animation mix %s->%s. Animation %s does " - "not exist in skeleton.", - from, to, to)); - continue; - } - animation_state_data->setMix(from, to, mix->get_mix()); - } -} - -Ref -SpineSkeletonDataResource::find_animation(const String &animation_name) const { - SPINE_CHECK(skeleton_data, nullptr) - if (EMPTY(animation_name)) - return nullptr; - auto animation = - skeleton_data->findAnimation(SPINE_STRING_TMP(animation_name)); - if (!animation) - return nullptr; - Ref animation_ref(memnew(SpineAnimation)); - animation_ref->set_spine_object(this, animation); - return animation_ref; -} - -Ref -SpineSkeletonDataResource::find_bone(const String &bone_name) const { - SPINE_CHECK(skeleton_data, nullptr) - if (EMPTY(bone_name)) - return nullptr; - auto bone = skeleton_data->findBone(SPINE_STRING_TMP(bone_name)); - if (!bone) - return nullptr; - Ref bone_ref(memnew(SpineBoneData)); - bone_ref->set_spine_object(this, bone); - return bone_ref; -} - -Ref -SpineSkeletonDataResource::find_slot(const String &slot_name) const { - SPINE_CHECK(skeleton_data, nullptr) - if (EMPTY(slot_name)) - return nullptr; - auto slot = skeleton_data->findSlot(SPINE_STRING_TMP(slot_name)); - if (!slot) - return nullptr; - Ref slot_ref(memnew(SpineSlotData)); - slot_ref->set_spine_object(this, slot); - return slot_ref; -} - -Ref -SpineSkeletonDataResource::find_skin(const String &skin_name) const { - SPINE_CHECK(skeleton_data, nullptr) - if (EMPTY(skin_name)) - return nullptr; - auto skin = skeleton_data->findSkin(SPINE_STRING_TMP(skin_name)); - if (!skin) - return nullptr; - Ref skin_ref(memnew(SpineSkin)); - skin_ref->set_spine_object(this, skin); - return skin_ref; -} - -Ref -SpineSkeletonDataResource::find_event(const String &event_data_name) const { - SPINE_CHECK(skeleton_data, nullptr) - if (EMPTY(event_data_name)) - return nullptr; - auto event = skeleton_data->findEvent(SPINE_STRING_TMP(event_data_name)); - if (!event) - return nullptr; - Ref event_ref(memnew(SpineEventData)); - event_ref->set_spine_object(this, event); - return event_ref; -} - -Ref SpineSkeletonDataResource::find_ik_constraint( - const String &constraint_name) const { - SPINE_CHECK(skeleton_data, nullptr) - if (EMPTY(constraint_name)) - return nullptr; - auto constraint = - skeleton_data->findIkConstraint(SPINE_STRING_TMP(constraint_name)); - if (!constraint) - return nullptr; - Ref constraint_ref(memnew(SpineIkConstraintData)); - constraint_ref->set_spine_object(this, constraint); - return constraint_ref; -} - -Ref -SpineSkeletonDataResource::find_transform_constraint( - const String &constraint_name) const { - SPINE_CHECK(skeleton_data, nullptr) - if (EMPTY(constraint_name)) - return nullptr; - auto constraint = - skeleton_data->findTransformConstraint(SPINE_STRING_TMP(constraint_name)); - if (!constraint) - return nullptr; - Ref constraint_ref( - memnew(SpineTransformConstraintData)); - constraint_ref->set_spine_object(this, constraint); - return constraint_ref; -} - -Ref SpineSkeletonDataResource::find_path_constraint( - const String &constraint_name) const { - SPINE_CHECK(skeleton_data, nullptr) - if (EMPTY(constraint_name)) - return nullptr; - auto constraint = - skeleton_data->findPathConstraint(SPINE_STRING_TMP(constraint_name)); - if (constraint == nullptr) - return nullptr; - Ref constraint_ref(memnew(SpinePathConstraintData)); - constraint_ref->set_spine_object(this, constraint); - return constraint_ref; -} - -Ref -SpineSkeletonDataResource::find_physics_constraint( - const String &constraint_name) const { - SPINE_CHECK(skeleton_data, nullptr) - if (EMPTY(constraint_name)) - return nullptr; - auto constraint = - skeleton_data->findPhysicsConstraint(SPINE_STRING_TMP(constraint_name)); - if (constraint == nullptr) - return nullptr; - Ref constraint_ref( - memnew(SpinePhysicsConstraintData)); - constraint_ref->set_spine_object(this, constraint); - return constraint_ref; -} - -String SpineSkeletonDataResource::get_skeleton_name() const { - SPINE_CHECK(skeleton_data, "") - String name; -#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5) - name = String::utf8(skeleton_data->getName().buffer()); -#else - name.parse_utf8(skeleton_data->getName().buffer()); -#endif - return name; -} - -Array SpineSkeletonDataResource::get_bones() const { - Array result; - SPINE_CHECK(skeleton_data, result) - auto bones = skeleton_data->getBones(); - result.resize((int) bones.size()); - for (int i = 0; i < bones.size(); ++i) { - Ref bone_ref(memnew(SpineBoneData)); - bone_ref->set_spine_object(this, bones[i]); - result[i] = bone_ref; - } - return result; -} - -Array SpineSkeletonDataResource::get_slots() const { - Array result; - SPINE_CHECK(skeleton_data, result) - auto slots = skeleton_data->getSlots(); - result.resize((int) slots.size()); - for (int i = 0; i < slots.size(); ++i) { - Ref slot_ref(memnew(SpineSlotData)); - slot_ref->set_spine_object(this, slots[i]); - result[i] = slot_ref; - } - return result; -} - -Array SpineSkeletonDataResource::get_skins() const { - Array result; - SPINE_CHECK(skeleton_data, result) - auto skins = skeleton_data->getSkins(); - result.resize((int) skins.size()); - for (int i = 0; i < skins.size(); ++i) { - Ref skin_ref(memnew(SpineSkin)); - skin_ref->set_spine_object(this, skins[i]); - result[i] = skin_ref; - } - return result; -} - -Ref SpineSkeletonDataResource::get_default_skin() const { - SPINE_CHECK(skeleton_data, nullptr) - auto skin = skeleton_data->getDefaultSkin(); - if (skin) - return nullptr; - Ref skin_ref(memnew(SpineSkin)); - skin_ref->set_spine_object(this, skin); - return skin_ref; -} - -void SpineSkeletonDataResource::set_default_skin(Ref skin) { - SPINE_CHECK(skeleton_data, ) - skeleton_data->setDefaultSkin(skin.is_valid() && skin->get_spine_object() - ? skin->get_spine_object() - : nullptr); -} - -Array SpineSkeletonDataResource::get_events() const { - Array result; - SPINE_CHECK(skeleton_data, result) - auto events = skeleton_data->getEvents(); - result.resize((int) events.size()); - for (int i = 0; i < events.size(); ++i) { - Ref event_ref(memnew(SpineEventData)); - event_ref->set_spine_object(this, events[i]); - result[i] = event_ref; - } - return result; -} - -Array SpineSkeletonDataResource::get_animations() const { - Array result; - SPINE_CHECK(skeleton_data, result) - auto animations = skeleton_data->getAnimations(); - result.resize((int) animations.size()); - for (int i = 0; i < animations.size(); ++i) { - Ref animation_ref(memnew(SpineAnimation)); - animation_ref->set_spine_object(this, animations[i]); - result[i] = animation_ref; - } - return result; -} - -Array SpineSkeletonDataResource::get_ik_constraints() const { - Array result; - SPINE_CHECK(skeleton_data, result) - auto constraints = skeleton_data->getIkConstraints(); - result.resize((int) constraints.size()); - for (int i = 0; i < constraints.size(); ++i) { - Ref constraint_ref(memnew(SpineIkConstraintData)); - constraint_ref->set_spine_object(this, constraints[i]); - result[i] = constraint_ref; - } - return result; -} - -Array SpineSkeletonDataResource::get_transform_constraints() const { - Array result; - SPINE_CHECK(skeleton_data, result) - auto constraints = skeleton_data->getTransformConstraints(); - result.resize((int) constraints.size()); - for (int i = 0; i < constraints.size(); ++i) { - Ref constraint_ref( - memnew(SpineTransformConstraintData)); - constraint_ref->set_spine_object(this, constraints[i]); - result[i] = constraint_ref; - } - return result; -} - -Array SpineSkeletonDataResource::get_path_constraints() const { - Array result; - SPINE_CHECK(skeleton_data, result) - auto constraints = skeleton_data->getPathConstraints(); - result.resize((int) constraints.size()); - for (int i = 0; i < constraints.size(); ++i) { - Ref constraint_ref( - memnew(SpinePathConstraintData)); - constraint_ref->set_spine_object(this, constraints[i]); - result[i] = constraint_ref; - } - return result; -} - -Array SpineSkeletonDataResource::get_physics_constraints() const { - Array result; - SPINE_CHECK(skeleton_data, result) - auto constraints = skeleton_data->getPhysicsConstraints(); - result.resize((int) constraints.size()); - for (int i = 0; i < constraints.size(); ++i) { - Ref constraint_ref( - memnew(SpinePhysicsConstraintData)); - constraint_ref->set_spine_object(this, constraints[i]); - result[i] = constraint_ref; - } - return result; -} - -float SpineSkeletonDataResource::get_x() const { - SPINE_CHECK(skeleton_data, 0) - return skeleton_data->getX(); -} - -float SpineSkeletonDataResource::get_y() const { - SPINE_CHECK(skeleton_data, 0) - return skeleton_data->getY(); -} - -float SpineSkeletonDataResource::get_width() const { - SPINE_CHECK(skeleton_data, 0) - return skeleton_data->getWidth(); -} - -float SpineSkeletonDataResource::get_height() const { - SPINE_CHECK(skeleton_data, 0) - return skeleton_data->getHeight(); -} - -String SpineSkeletonDataResource::get_version() const { - SPINE_CHECK(skeleton_data, "") - return skeleton_data->getVersion().buffer(); -} - -String SpineSkeletonDataResource::get_hash() const { - SPINE_CHECK(skeleton_data, "") - return skeleton_data->getHash().buffer(); -} - -String SpineSkeletonDataResource::get_images_path() const { - SPINE_CHECK(skeleton_data, "") - return skeleton_data->getImagesPath().buffer(); -} - -String SpineSkeletonDataResource::get_audio_path() const { - SPINE_CHECK(skeleton_data, "") - return skeleton_data->getAudioPath().buffer(); -} - -float SpineSkeletonDataResource::get_fps() const { - SPINE_CHECK(skeleton_data, 0) - return skeleton_data->getFps(); -} - -float SpineSkeletonDataResource::get_reference_scale() const { - SPINE_CHECK(skeleton_data, 100); - return skeleton_data->getReferenceScale(); -} - -void SpineSkeletonDataResource::set_reference_scale(float reference_scale) { - SPINE_CHECK(skeleton_data, ) - skeleton_data->setReferenceScale(reference_scale); -} diff --git a/spine-godot/SpineSkeletonDataResource_old.h b/spine-godot/SpineSkeletonDataResource_old.h deleted file mode 100644 index 6292e9c8c..000000000 --- a/spine-godot/SpineSkeletonDataResource_old.h +++ /dev/null @@ -1,220 +0,0 @@ -/****************************************************************************** - * Spine Runtimes License Agreement - * Last updated April 5, 2025. Replaces all prior versions. - * - * Copyright (c) 2013-2025, Esoteric Software LLC - * - * Integration of the Spine Runtimes into software or otherwise creating - * derivative works of the Spine Runtimes is permitted under the terms and - * conditions of Section 2 of the Spine Editor License Agreement: - * http://esotericsoftware.com/spine-editor-license - * - * Otherwise, it is permitted to integrate the Spine Runtimes into software - * or otherwise create derivative works of the Spine Runtimes (collectively, - * "Products"), provided that each user of the Products must obtain their own - * Spine Editor license and redistribution of the Products in any form must - * include this license and copyright notice. - * - * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, - * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#pragma once - -#include "SpineAnimation.h" -#include "SpineAtlasResource.h" -#include "SpineBoneData.h" -#include "SpineEventData.h" -#include "SpineIkConstraintData.h" -#include "SpinePathConstraintData.h" -#include "SpinePhysicsConstraintData.h" -#include "SpineSkeletonFileResource.h" -#include "SpineSkin.h" -#include "SpineSlotData.h" -#include "SpineTransformConstraintData.h" - -class SpineAnimationMix : public Resource { - GDCLASS(SpineAnimationMix, Resource) - -protected: - static void _bind_methods(); - - String from; - String to; - float mix; - -public: - SpineAnimationMix(); - - void set_from(const String &from); - - String get_from(); - - void set_to(const String &to); - - String get_to(); - - void set_mix(float mix); - - float get_mix(); -}; - -class SpineSkeletonDataResource : public Resource { - GDCLASS(SpineSkeletonDataResource, Resource) - -protected: - static void _bind_methods(); - -private: - Ref atlas_res; - Ref skeleton_file_res; - float default_mix; - Array animation_mixes; - - spine::SkeletonData *skeleton_data; - spine::AnimationStateData *animation_state_data; - - void update_skeleton_data(); - -#ifdef SPINE_GODOT_EXTENSION - void load_resources(spine::Atlas *atlas, const String &json, - const PackedByteArray &binary); -#else - void load_resources(spine::Atlas *atlas, const String &json, - const Vector &binary); -#endif - -public: - SpineSkeletonDataResource(); - virtual ~SpineSkeletonDataResource(); - - bool is_skeleton_data_loaded() const; - - void set_atlas_res(const Ref &atlas); - Ref get_atlas_res(); - - void - set_skeleton_file_res(const Ref &skeleton_file); - Ref get_skeleton_file_res(); - - spine::SkeletonData *get_skeleton_data() const { return skeleton_data; } - - spine::AnimationStateData *get_animation_state_data() const { - return animation_state_data; - } - -#ifdef SPINE_GODOT_EXTENSION - void get_animation_names(PackedStringArray &animation_names) const; - - void get_skin_names(PackedStringArray &l) const; - - void get_slot_names(PackedStringArray &slot_names); - - void get_bone_names(PackedStringArray &bone_names); -#else - void get_animation_names(Vector &animation_names) const; - - void get_skin_names(Vector &l) const; - - void get_slot_names(Vector &slot_names); - - void get_bone_names(Vector &bone_names); -#endif - - void set_default_mix(float default_mix); - - float get_default_mix(); - - void set_animation_mixes(Array animation_mixes); - - Array get_animation_mixes(); - - // Used by SpineEditorPropertyAnimationMix(es) to update the underlying - // AnimationState - void update_mixes(); - - // Spine API - Ref find_bone(const String &bone_name) const; - - Ref find_slot(const String &slot_name) const; - - Ref find_skin(const String &skin_name) const; - - Ref find_event(const String &event_data_name) const; - - Ref find_animation(const String &animation_name) const; - - Ref - find_ik_constraint(const String &constraint_name) const; - - Ref - find_transform_constraint(const String &constraint_name) const; - - Ref - find_path_constraint(const String &constraint_name) const; - - Ref - find_physics_constraint(const String &constraint_name) const; - - String get_skeleton_name() const; - - Array get_bones() const; - - Array get_slots() const; - - Array get_skins() const; - - Ref get_default_skin() const; - - void set_default_skin(Ref skin); - - Array get_events() const; - - Array get_animations() const; - - Array get_ik_constraints() const; - - Array get_transform_constraints() const; - - Array get_path_constraints() const; - - Array get_physics_constraints() const; - - float get_x() const; - - float get_y() const; - - float get_width() const; - - float get_height() const; - - String get_version() const; - - String get_hash() const; - - String get_images_path() const; - - String get_audio_path() const; - - float get_fps() const; - - float get_reference_scale() const; - - void set_reference_scale(float reference_scale); - -#ifdef TOOLS_ENABLED -#if VERSION_MAJOR > 3 - void _on_resources_reimported(const PackedStringArray &resources); -#else - void _on_resources_reimported(const PoolStringArray &resources); -#endif -#endif -}; diff --git a/spine-godot/spine_godot/SpineSkeletonDataResource.cpp b/spine-godot/spine_godot/SpineSkeletonDataResource.cpp index 8fc35ca10..81a5e51cd 100644 --- a/spine-godot/spine_godot/SpineSkeletonDataResource.cpp +++ b/spine-godot/spine_godot/SpineSkeletonDataResource.cpp @@ -1,4 +1,3 @@ -// this is my version (v1), generated with Claude Code /****************************************************************************** * Spine Runtimes License Agreement * Last updated April 5, 2025. Replaces all prior versions. @@ -256,9 +255,6 @@ SpineSkeletonDataResource::~SpineSkeletonDataResource() { #ifdef TOOLS_ENABLED #if VERSION_MAJOR > 3 if (Engine::get_singleton()->is_editor_hint()) { - // Use ObjectDB::get_instance() to safely check if EditorFileSystem still exists. - // This avoids the dangling pointer problem during editor shutdown where - // EditorFileSystem may be destroyed before SpineSkeletonDataResource objects. EditorFileSystem *efs = Object::cast_to(ObjectDB::get_instance(editor_file_system_id)); if (efs && efs->is_connected("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported))) { efs->disconnect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported)); @@ -266,7 +262,6 @@ SpineSkeletonDataResource::~SpineSkeletonDataResource() { } #else if (Engine::get_singleton()->is_editor_hint()) { - // Use ObjectDB::get_instance() to safely check if EditorFileSystem still exists. EditorFileSystem *efs = Object::cast_to(ObjectDB::get_instance(editor_file_system_id)); if (efs && efs->is_connected("resources_reimported", this, "_on_resources_reimported")) { efs->disconnect("resources_reimported", this, "_on_resources_reimported"); diff --git a/spine-godot/spine_godot/SpineSkeletonDataResource.h b/spine-godot/spine_godot/SpineSkeletonDataResource.h index 95b1441fa..f07eff8a1 100644 --- a/spine-godot/spine_godot/SpineSkeletonDataResource.h +++ b/spine-godot/spine_godot/SpineSkeletonDataResource.h @@ -1,4 +1,3 @@ -// this is my version (v1), generated with Claude Code /****************************************************************************** * Spine Runtimes License Agreement * Last updated April 5, 2025. Replaces all prior versions. @@ -84,9 +83,6 @@ private: spine::AnimationStateData *animation_state_data; #ifdef TOOLS_ENABLED - // Store the ObjectID of EditorFileSystem to safely validate it in destructor. - // Raw pointers to singletons can become dangling during editor shutdown, - // but ObjectID can be safely validated via ObjectDB::get_instance(). ObjectID editor_file_system_id; #endif diff --git a/spine-godot/spine_godot/SpineSkin.cpp b/spine-godot/spine_godot/SpineSkin.cpp index 4271f2fe4..32ef1070b 100644 --- a/spine-godot/spine_godot/SpineSkin.cpp +++ b/spine-godot/spine_godot/SpineSkin.cpp @@ -45,7 +45,9 @@ void SpineSkin::_bind_methods() { ClassDB::bind_method(D_METHOD("get_attachments"), &SpineSkin::get_attachments); ClassDB::bind_method(D_METHOD("get_bones"), &SpineSkin::get_bones); ClassDB::bind_method(D_METHOD("get_constraints"), &SpineSkin::get_constraints); +#if VERSION_MAJOR >= 4 ClassDB::bind_method(D_METHOD("init", "name", "sprite"), &SpineSkin::init); +#endif } SpineSkin::SpineSkin() : owns_skin(false) { diff --git a/spine-godot/spine_godot/register_types.cpp b/spine-godot/spine_godot/register_types.cpp index f4b2cf781..c3eebd6c5 100644 --- a/spine-godot/spine_godot/register_types.cpp +++ b/spine-godot/spine_godot/register_types.cpp @@ -116,12 +116,12 @@ void register_spine_godot_types() { #endif spine::Bone::setYDown(true); - #ifndef SPINE_GODOT_EXTENSION +#ifndef SPINE_GODOT_EXTENSION GDREGISTER_CLASS(SpineAtlasResourceFormatLoader); GDREGISTER_CLASS(SpineAtlasResourceFormatSaver); GDREGISTER_CLASS(SpineSkeletonFileResourceFormatLoader); GDREGISTER_CLASS(SpineSkeletonFileResourceFormatSaver); - #endif +#endif GDREGISTER_CLASS(SpineObjectWrapper); GDREGISTER_CLASS(SpineAtlasResource);