Playdate.timer value increases between calling :pause() and :start()

It looks like a timer's value keeps updating even after it's been paused? I expected it to not change after :pause() was called, and to only begin incrementing again after calling :start()

I put together a minimal repro - if you press A and check the console output you will see the value incrementing normally, but if you press A again, wait for a couple of seconds, and press A once more, you will notice the value has jumped as though the timer was never paused.

import 'CoreLibs/timer'

local timer = playdate.timer.new(12000, 1, 1000)
timer:pause()
timer.updateCallback = function ()
  print('timer update', timer.value)
end
timer.timerEndedCallback = function ()
  print('timer end', timer.value)
end

local isPlaying = false

function playdate:AButtonDown()
  if isPlaying then
    timer:pause()
    print('paused timer')
    isPlaying = false
  else
    timer:start()
    print('started timer')
    isPlaying = true
  end
end

function playdate.update()
  playdate.timer.updateTimers()
end

For now, editing CoreLibs/timer.lua so that the :start() function sets the timer's _lastTime value back to nil seems to work:

function playdate.timer:start()
	self._lastTime = nil
	self.paused = false
end
8 Likes

Thanks for the workaround!

I've also reported this, a long time ago, in the days when the was an issue tracker.

Is this likely to get fixed soon?

1 Like

I've tagged Dave. Until then you can edit the lua files to add the fix yourself

I ran into this bug again on SDK 1.13.0, and it's been so long I had forgotten about it and spent a few minutes very confused in the debugger :smiling_face_with_tear:. Is a fix in the roadmap?

2 Likes

Filed, and passed over to Dan :slight_smile:

1 Like

I'm working on a game and I have this issue but I am not 100% sure how to edit the core libs timer file. Can you expand on how to update the core libs timer file so the timers work correctly? I looked for other threads but I cannot find anything. Appreciate any help, I have come a long way on my project just from looking at threads like this. Timers are the bane of my existence right now so I would love to get this right, I have the pause code written perfectly but my timer always resets!

There is a CoreLibs Folder in the SDK Folder. You can just open, change and save the file

2 Likes

Thanks Lukas, very helpful. I feel sort of stupid for not realizing that earlier, I see exactly what you mean now.

1 Like

Still have the same issue as of 2.0.3

Didn't test this fix fully but in timer.lua i added this fix to the code

	for i = 1, #timers do

		local timer = timers[i]

		if timer.active == true and timer.paused == false then			
                -- line 269 to 344 (unchanged)
        else
			timer._lastTime = currentTime;
        end      
    end 
1 Like

Issue is still present in the new SDK 2.1.0.

Would inclusion of @jaames fix in the SDK work? Or does setting self._lastTime = nil in playdate.timer:start() have some any other side-effects?

Does it make any difference if you check playdate.timer.paused instead?

timer.paused is a very welcome addition in the new SDK, but checking it has no effect on this issue.

I've created a new example with a video of the result for everyone coming to this thread at a later time. It highlighting the differences between timer and frameTimer (which behaves as expected). In this, I'm initializing timers that should count from 0 to 100 over the span of 10 seconds. Pausing the timer after calling updateTimers() once, and then resuming it after 5 seconds of runtime shows it jumps ahead to a value of 50, where as frameTimer behaves as expected and starts counting from 0:

import 'CoreLibs/timer'
import 'CoreLibs/frameTimer'

-- timer increasing value from 0 to 100 across 10 seconds
local timer = playdate.timer.new(10000, 0, 100)
-- trigger timer once, then pause
playdate.timer.updateTimers()
timer:pause()

-- add frametimer for comparison (we're running at the default 30FPS)
local frameTimer = playdate.frameTimer.new(300, 0, 100)
frameTimer:pause()


function playdate.update()
  playdate.timer.updateTimers()
  playdate.frameTimer.updateTimers()
  
  local elapsedGameTime = playdate.getElapsedTime()
  -- continue timer after 5 seconds runtime
  if timer.paused and elapsedGameTime > 5 then
    timer:start() -- timer value will jump to half way instead of continuing from ~0.
    frameTimer:start()
  end
  
  playdate.graphics.clear()
  playdate.graphics.drawText("elapsed game time:  " .. elapsedGameTime, 10, 10)
  
  playdate.graphics.drawText("timer.currentTime:  " .. timer.currentTime, 10, 50)
  playdate.graphics.drawText("      timer.timeLeft:  " .. timer.timeLeft, 10, 70)
  playdate.graphics.drawText("            *timer.value:  " .. math.floor(timer.value) .. "*", 10, 95)
  
  playdate.graphics.drawText("frameTimer.frame:  " .. frameTimer.frame, 10, 130)
  playdate.graphics.drawText("  *frameTimer.value:  " .. math.floor(frameTimer.value) .. "*", 10, 155)
end

In the video you can see how the timer jumps ahead when being started again after 5 seconds:

Setting self._lastTime = nil in playdate.timer:start() would fix this.

1 Like

Rather infuriating that this is still an issue, with a completely trivial fix provided, nearly 2 years after it was opened.

Would be nice if the Lua SDK helpers were open source so the community could submit PRs.

tbh the fact that there's a fix here means we don't feel as much pressure to fix it in the SDK, so it slips through the cracks when we're prioritizing fixes. It's also something that could potentially change existing behavior in unexpected ways, and we have to be very careful there.

That said, there's a prospective fix in the queue currently scheduled for 2.1.2. That adds a timer._lastTime = playdate.getCurrentTimeMilliseconds() call to timer:start() instead of setting it to nil, not sure what difference that makes and I don't have the time right now to look into it. :person_shrugging:

2 Likes

The difference of doing timer._lastTime = playdate.getCurrentTimeMilliseconds() vs timer._lastTime = nil will be very subtle (and I suspect in most cases not noticeable at all).

During timer.updateTimers(), if timer._lastTime is nil, the delta since the last update will default to 0.
So the prospective fix with playdate.getCurrentTimeMilliseconds() is technically more correct. :playdate_happy:

3 Likes

A fix is scheduled for 2.3, ftr

8 Likes