From 55d46f2019a374e79cad0e02a98ed63984d16cc3 Mon Sep 17 00:00:00 2001 From: Luke Ingram Date: Mon, 15 Dec 2025 18:12:36 -0400 Subject: [PATCH] [godot] Removes unnecessary files and comments. Additionally, CI builds were failing due to the commit where SpineSkin had an init() method added, but this caused Godot 3.x builds to fail. Our formatter was also run, so it should no longer fail the format check. --- spine-godot/BUGFIX_SUMMARY_V2.md | 172 ---- spine-godot/SpineSkeletonDataResource_old.cpp | 853 ------------------ spine-godot/SpineSkeletonDataResource_old.h | 220 ----- .../spine_godot/SpineSkeletonDataResource.cpp | 5 - .../spine_godot/SpineSkeletonDataResource.h | 4 - spine-godot/spine_godot/SpineSkin.cpp | 2 + spine-godot/spine_godot/register_types.cpp | 4 +- 7 files changed, 4 insertions(+), 1256 deletions(-) delete mode 100644 spine-godot/BUGFIX_SUMMARY_V2.md delete mode 100644 spine-godot/SpineSkeletonDataResource_old.cpp delete mode 100644 spine-godot/SpineSkeletonDataResource_old.h 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);