Update failed: main.lua:74: Sprite doesn't have a lua proxy!

I've started getting this error and I'm not sure what it means :grimacing:
Any know what might be causing this error?

not sure why but it's got something to do with this new class I added

local pd <const> = playdate
local gfx <const> = pd.graphics

class("Toast").extends(gfx.sprite)

local y = 0

function Toast:init(text)
  Toast.super.init(self)
  gfx.setFont(fonts.standard)

  local w, h = gfx.getTextSize(text)
  local margin = 6
  w+=4+margin
  h+=4

  local img = gfx.image.new(w, h)
  gfx.pushContext(img)
    gfx.setColor(gfx.kColorWhite)
    gfx.fillRoundRect(0,0,w,h,4)
    gfx.drawText(text,margin,0)
  gfx.popContext()

  self:setImage(img)
  self:setIgnoresDrawOffset(true)
  self:setCenter(1,0)
  self:setZIndex(ZIndex.ui)
  self:moveTo(0,y)
  self:add()

  y += h
  self._h = h

  self._x = sequence.new():from(0):to(self.width-4, 0.7, "inSine"):sleep(5):to(0, 0.7, "inSine"):start()
end

function Toast:update()
  self:moveTo(self._x:get(), self.y)
  if self._x:isDone() then
    self:remove()
  end
end

function Toast:remove()
  y -= self._h
  Toast.super.remove(self)
end

Based on not having imported graphics, sprite, and object in this file, I assume its not your main.lua?

If so, your problem isn't in this file. Your title says the issue was in main.lua on line 74 - what's on that line?

Hmm so some more digging around, here's what I found:

Line 74 is just gfx.sprite.update(), I don't think that's the problem.

The error only pop's up when I change rooms while one of the Toast sprites is still active.

To change rooms I'm using the roomy lib which in turn calls gfx.sprite.removeAll(). I've also updated it to then call collectgarbage() whenever a room changes.

Calling gfx.sprite.removeAll() and collectgarbage() when I change rooms has been in my game basically the whole time and has only started causing problems since I added the Toast class. However, if I remove the collectgarbage() call I stop getting the error.

I could just remove the collectgarbage() call but then I get cpu spikes and frame rate drops when the gc catches up, and it feels more like a work around than a solution.

does gfx.sprite.removeAll() call the remove method for each of the sprites?

Hmm its hard to say without seeing more. Would you mind sharing the whole project?

Try commenting out your Toast:remove() overload function.

I think what's happening is that when roomy removes all, your toast:remove is still active trying to do something to a non-existent sprite.

I tried that still getting the same error. Weirdly it only pops up the second time I leave the room.

Curious.

Can you create a minimal example that uses Toast and shows the problem?

The sprite library is implemented partly in Lua and partly in C. What I think is happening is that when you manually run the garbage collector, all references to your sprite have at that point gone out of scope in the Lua environment (having had remove() and/or sprite.removeAll() called). So your sprite's Lua object is deallocated right then and there.

However, the C side still has its own reference to the sprite, and it probably wasn't expecting the garbage collector to run mid-gameloop like that. So when sprite.update() tries to delegate to your sprite's update() method, it finds that the Lua table representing the sprite has already been deleted, and it logs this error message, pointing back to the line where you call sprite.update().

If you can't rely on automatic garbage collection, I would try moving the collectgarbage() call elsewhere, ideally to the very beginning or very end of playdate.update(). Meanwhile I'll ask around and see if this is something we could potentially solve in the SDK (other than by saying "hey, don't do that"). Sorry for the trouble.

2 Likes

It's definitely a bug. We have a mechanism for marking the sprite as "in use" outside of Lua so that Lua only frees the wrapper and not the sprite itself, but something's not working right here. Hopefully it'll be easy to reproduce with the above code.

2 Likes

I'm getting this error myself at the moment, after adding a sprite that was removed.

I call playdate.sprite.update() from lua, but all my sprite creation and adding and removal is done from C. Is that legal use of the API?

If I move the sprite update call to C, I get weird crashes with lua allocating strings, or lua complaining about my game update C function. Perhaps I am overwriting memory somewhere, but everything works fine until I remove the sprite and then add it again.

If I free the sprites and then recreate them as well as removing / adding them, it works fine.