Countdown timer using BukkitRunnable not accurate

Discussion in 'Spigot Plugin Development' started by SiezureSalad, Jul 6, 2018.

  1. I have made a scoreboard timer on the side of the screen using a repeating task repeating every 20 ticks (1 second) and decreasing the second by one each iteration. It works perfectly except from the fact that over long periods of time, the timer will fall behind and take longer than it should due to TPS being too low (due to lag). I was wondering if there was a better way I can do this.
     
  2. Tps shouldn't drop, anyway use this:
    Code (Text):
    new BukkitRunnable() {
    int time = 30;
    @Override
    public void run() {
      time--;
      if(time == 0) {
           cancel() ;
        }
      }
    }
     
  3. Is that even a repeating task? It would have the same problem mine has. It slowly falls behind an accurate timer when the server TPS isn't 20.

    This is going to be ran during the laggiest times of the server, an event which a lot of people will be at, causing TPS to drop.
     
  4. Fix your TPS issue. You could run the timer portion ASync then update the scoreboard with a Sync call, but really if it's falling behind you've got a memory leak probably or your computer is shit.
     
  5. It's not falling behind by a lot, but a 20 minute timer will fall behind by maybe 3 seconds when TPS is like 19.8. This is going to be run during a laggy time on my server where I expect TPS to drop to maybe 17 TPS.
     
  6. Run your own "timer" ASync, and not a runTaskTimer or whatever- those are still based on ticks and will fall behind regardless,
    just run an ASync task that sends sync calls to reduce the timer by 1 second every time the System currentmillis is 1000 more than it was.
     
  7. My problem with that is I create a new Scoreboard and set it to players each iteration and the docs say "Asynchronous tasks should never access any API in Bukkit." https://hub.spigotmc.org/javadocs/spigot/org/bukkit/scheduler/BukkitRunnable.html
     
  8. You won't be. I said call a sync task for the actual scoreboard updating. The only thing your ASync task will be doing is running a while loop over System currentmillis, and calling that sync task if it's 1000 more than it was before.
     
  9. I actually don't know what you mean by call a sync task for the actual scoreboard updating. Could you give an example?
     
  10. Easy schedule a new BukkitRunnable that runs the task timer aSync when you need to update the scoreboard just schedule a new sync task
     
  11. Ah okay got it working with ASynchornously , thanks for the help guys.
     
  12. FrostedSnowman

    Resource Staff

    there should be no need for you to run it async.

    "but my tps are dropping.."
    that's a problem you should be fixing, not finding a workaround for.
     
  13. Async tasks are still susceptible to TPS loss. Idk where the mentality of async is not bound by TPS comes from...

    If you dont want to be bound by TPS then you need to use Java's own Timer api that's bound by the CPU clock. Just be warned this is just as unsafe (if not more) to use Bukkit API with.
     
    • Agree Agree x 1
  14. My suggestion was just to use an ASync task, not timer, just as an easy way to get stuff off of the main thread. Then inside of it do something like this awful pseudocode
    Code (Text):
    long start = System.currentTimeMillis();
    long stop = start+(timerlength*1000);
    long tick = start;
    while(System.currentTimeMillis() < stop) {
        if(System.currentTimeMillis() > 1000+tick){
            Bukkit.dosyncscoreboardshit;
            tick = System.currentTimeMillis();
        }
    }
     
  15. Right, but threads isn't at question here. He wants his timer to not be effected by TPS loss, which an async task still is bound to the TPS counter. If a server is running at 5 TPS then your async task that's delayed 20 ticks will take 4 seconds between runs, not 1. ;)
     
  16. Instead of relying on ticks for everything, only use ticks to update the scoreboard, and rely on the server time to deduce the time you want to display.

    Doesn't fix the update frequency issue when TPS is low, but at least it keeps the counter in sync with real time.

    And no, async is not a solid solution here.

    @Velariyel busy waiting is a terrible practice for waits which are bigger than the accuracy of the timer (which is micro/nanoseconds, depending on your architecture)
     
  17. It wouldn't be delayed 20 ticks, it'd just be a task with a while loop inside of it. That isn't bound by ticks. I don't mean a repeating task, run it once and have it count out currentmillis and call synchronous tasks when necessary.
     
  18. Oh, I see what you did there. That's a good way to 100% a CPU core ^_^ Best to just stick with a Timer ;)
     
  19. I did say it was an awful pseudocode when I posted it. Definitely wouldn't want to use spinning for it, but the point stands that that general approach is a decent way of unhinging the time from tickrate.

    Edit: actually yeah it kinda was retarded, an async task was unnecessary, could just as easily hold onto the system millis and update the scoreboard without an async task, I'm tired and somehow combined trains of thought.

    The approach of using currentmillis works though :^)
     
  20. What do you mean by unsafe? What can go wrong by using it? Also is there any benefit to using ASync?