Skip to content

Commit

Permalink
Merge pull request #159 from defold/callback-fix
Browse files Browse the repository at this point in the history
Fix issues when animation started from different scripts
  • Loading branch information
AGulev authored Jan 30, 2024
2 parents 14f4d07 + 2642282 commit f689925
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 24 deletions.
53 changes: 37 additions & 16 deletions defold-spine/src/comp_spine_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,10 +299,10 @@ namespace dmSpine

static void ClearCompletionCallback(SpineAnimationTrack* track)
{
if (track->m_AnimationInstance && track->m_AnimationCallbackRef)
if (track->m_CallbackInfo)
{
dmScript::UnrefInInstance(track->m_Context, track->m_AnimationCallbackRef+LUA_NOREF);
track->m_AnimationCallbackRef = 0;
dmScript::DestroyCallback(track->m_CallbackInfo);
track->m_CallbackInfo = 0x0;
}
}

Expand Down Expand Up @@ -358,7 +358,7 @@ namespace dmSpine
track.m_AnimationInstance->mixDuration = blend_duration;
track.m_AnimationInstance->trackTime = dmMath::Clamp(offset, track.m_AnimationInstance->animationStart, track.m_AnimationInstance->animationEnd);

track.m_AnimationCallbackRef = 0;
track.m_CallbackInfo = 0x0;
dmMessage::ResetURL(&track.m_Listener);

return true;
Expand Down Expand Up @@ -418,10 +418,27 @@ namespace dmSpine
message.m_Playback = track.m_Playback;
message.m_Track = entry->trackIndex + 1;

dmGameObject::Result result = dmGameObject::PostDDF(&message, &sender, &receiver, track.m_AnimationCallbackRef, true);
if (result != dmGameObject::RESULT_OK)
if (track.m_CallbackInfo)
{
dmLogError("Could not send animation_done to listener: %d", result);
uint32_t id = track.m_CallbackId;
RunTrackCallback(track.m_CallbackInfo, dmGameSystemDDF::SpineAnimationDone::m_DDFDescriptor, (const char*)&message, &sender);
// If, in a Lua callback, the user calls spine.play_anim(),
// it will destroy the current callback and create a new one (if specified).
// Therefore, we need to check whether we are going to remove the same callback
// that we are running. If not, it has already been removed.
if (id == track.m_CallbackId)
{
ClearCompletionCallback(&track);
}

}
else
{
dmGameObject::Result result = dmGameObject::PostDDF(&message, &sender, &receiver, 0, true);
if (result != dmGameObject::RESULT_OK)
{
dmLogError("Could not send animation_done to listener: %d", result);
}
}
}

Expand Down Expand Up @@ -456,10 +473,17 @@ namespace dmSpine
message.m_Node.m_ContextTableRef = 0;
message.m_Track = entry->trackIndex + 1;

dmGameObject::Result result = dmGameObject::PostDDF(&message, &sender, &receiver, track.m_AnimationCallbackRef, false);
if (result != dmGameObject::RESULT_OK)
if (track.m_CallbackInfo)
{
RunTrackCallback(track.m_CallbackInfo, dmGameSystemDDF::SpineEvent::m_DDFDescriptor, (const char*)&message, &sender);
}
else
{
dmLogError("Could not send animation event '%s' from animation '%s' to listener: %d", entry->animation->name, event->data->name, result);
dmGameObject::Result result = dmGameObject::PostDDF(&message, &sender, &receiver, 0, false);
if (result != dmGameObject::RESULT_OK)
{
dmLogError("Could not send animation event '%s' from animation '%s' to listener: %d", entry->animation->name, event->data->name, result);
}
}
}

Expand Down Expand Up @@ -502,10 +526,6 @@ namespace dmSpine
{
// We only send the event if it's not looping (same behavior as before)
SendAnimationDone(component, state, entry, event);

// The animation has ended, so we won't send any more on this
track.m_AnimationCallbackRef = 0;
track.m_AnimationInstance = nullptr;
}

if (IsPingPong(track.m_Playback))
Expand Down Expand Up @@ -967,7 +987,7 @@ namespace dmSpine
component->m_ReHash = 1;
}

bool CompSpineModelPlayAnimation(SpineModelComponent* component, dmGameSystemDDF::SpinePlayAnimation* message, dmMessage::URL* sender, int callback_ref, lua_State* L)
bool CompSpineModelPlayAnimation(SpineModelComponent* component, dmGameSystemDDF::SpinePlayAnimation* message, dmMessage::URL* sender, dmScript::LuaCallbackInfo* callback_info, lua_State* L)
{
bool result = PlayAnimation(component, message->m_AnimationId, (dmGameObject::Playback)message->m_Playback, message->m_BlendDuration,
message->m_Offset, message->m_PlaybackRate, message->m_Track - 1);
Expand All @@ -976,7 +996,8 @@ namespace dmSpine
SpineAnimationTrack& track = component->m_AnimationTracks[message->m_Track - 1];
track.m_Listener = *sender;
track.m_Context = L;
track.m_AnimationCallbackRef = callback_ref;
track.m_CallbackId++;
track.m_CallbackInfo = callback_info;
}
return result;
}
Expand Down
9 changes: 7 additions & 2 deletions defold-spine/src/comp_spine_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <dmsdk/gameobject/gameobject.h>
#include <dmsdk/gamesys/render_constants.h>
#include <dmsdk/render/render.h>
#include <dmsdk/gamesys/script.h>
// The engine ddf formats aren't stored in the "dmsdk" folder (yet)
#include <gamesys/gamesys_ddf.h>

Expand All @@ -44,7 +45,9 @@ namespace dmSpine
dmGameObject::Playback m_Playback;
dmMessage::URL m_Listener;
lua_State* m_Context;
int m_AnimationCallbackRef;

dmScript::LuaCallbackInfo* m_CallbackInfo;
uint32_t m_CallbackId;
};

struct IKTarget
Expand Down Expand Up @@ -82,7 +85,7 @@ namespace dmSpine
};

// For scripting
bool CompSpineModelPlayAnimation(SpineModelComponent* component, dmGameSystemDDF::SpinePlayAnimation* message, dmMessage::URL* sender, int callback_ref, lua_State* L);
bool CompSpineModelPlayAnimation(SpineModelComponent* component, dmGameSystemDDF::SpinePlayAnimation* message, dmMessage::URL* sender, dmScript::LuaCallbackInfo* callback_info, lua_State* L);
bool CompSpineModelCancelAnimation(SpineModelComponent* component, dmGameSystemDDF::SpineCancelAnimation* message);

bool CompSpineModelSetConstant(SpineModelComponent* component, dmGameSystemDDF::SetConstant* message);
Expand All @@ -97,6 +100,8 @@ namespace dmSpine

bool CompSpineModelGetBone(SpineModelComponent* component, dmhash_t bone_name, dmhash_t* instance_id);

void RunTrackCallback(dmScript::LuaCallbackInfo* callback_data, const dmDDF::Descriptor* desc, const char* data, const dmMessage::URL* sender);

}

#endif // DM_GAMESYS_COMP_SPINE_MODEL_H
38 changes: 32 additions & 6 deletions defold-spine/src/script_spine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <dmsdk/dlib/message.h>
#include <dmsdk/dlib/vmath.h>
#include <dmsdk/gameobject/script.h>
#include <dmsdk/gamesys/script.h>

// The engine ddf formats aren't stored in the "dmsdk" folder (yet)
#include <gamesys/gamesys_ddf.h>
Expand All @@ -22,6 +21,7 @@
namespace dmScript
{
bool GetURL(lua_State* L, dmMessage::URL* out_url);
void PushURL(lua_State* L, const dmMessage::URL& m);
}

namespace dmSpine
Expand Down Expand Up @@ -306,14 +306,12 @@ namespace dmSpine
lua_pop(L, 1);
}

int functionref = 0;
dmScript::LuaCallbackInfo* cbk = 0x0;
if (top > 4) // completed cb
{
if (lua_isfunction(L, 5))
{
lua_pushvalue(L, 5);
// NOTE: By convention m_FunctionRef is offset by LUA_NOREF, see message.h in dlib
functionref = dmScript::RefInInstance(L) - LUA_NOREF;
cbk = dmScript::CreateCallback(L, 5);
}
}

Expand All @@ -327,15 +325,43 @@ namespace dmSpine

dmMessage::URL sender;
dmScript::GetURL(L, &sender);
if (!CompSpineModelPlayAnimation(component, &msg, &sender, functionref, L))
if (!CompSpineModelPlayAnimation(component, &msg, &sender, cbk, L))
{
// destroy callback immediately if error happened
if (cbk)
{
dmScript::DestroyCallback(cbk);
}
char buffer[128];
dmLogError("Failed to run animation '%s' on component '%s'", lua_tostring(L, 2), dmScript::UrlToString(&receiver, buffer, sizeof(buffer)));
}

return 0;
}

void RunTrackCallback(dmScript::LuaCallbackInfo* cbk, const dmDDF::Descriptor* desc, const char* data, const dmMessage::URL* sender)
{
if (!dmScript::IsCallbackValid(cbk))
{
dmLogError("Spine models callback is invalid.");
return;
}
lua_State* L = dmScript::GetCallbackLuaContext(cbk);
DM_LUA_STACK_CHECK(L, 0);

if (!dmScript::SetupCallback(cbk))
{
dmLogError("Failed to setup spine animation callback");
return;
}
lua_pushstring(L, desc->m_Name);
dmScript::PushDDF(L, desc, data, false);
dmScript::PushURL(L, *sender);
int ret = dmScript::PCall(L, 4, 0);
(void)ret;
dmScript::TeardownCallback(cbk);
}

/*# cancel all animation on a spine model
* Cancels all running animations on a specified spine model component.
*
Expand Down

0 comments on commit f689925

Please sign in to comment.