1 /** 2 Utility for medium-precision interval timing. 3 */ 4 5 module dcord.util.ticker; 6 7 import core.time, 8 std.algorithm.comparison; 9 10 import vibe.core.core : vibeSleep = sleep; 11 12 import dcord.util.time; 13 14 /** 15 Ticker which can be used for interval-based timing. Operates at millisecond 16 precision. 17 */ 18 class Ticker { 19 private { 20 long interval; 21 long next = 0; 22 } 23 24 /** 25 Create a new Ticker 26 27 Params: 28 interval = interval to tick at (any unit up to millisecond precision) 29 autoStart = whether the instantiation of the object marks the first tick 30 */ 31 this(Duration interval, bool autoStart=false) { 32 this.interval = interval.total!"msecs"; 33 34 if (autoStart) this.start(); 35 } 36 37 /// Sets when the next tick occurs 38 void setNext() { 39 this.next = getEpochTimeMilli() + this.interval; 40 } 41 42 /// Starts the ticker 43 void start() { 44 assert(this.next == 0, "Cannot start already running ticker"); 45 this.setNext(); 46 } 47 48 /// Sleeps until the next tick 49 void sleep() { 50 long now = getEpochTimeMilli(); 51 52 if (this.next < now) { 53 this.setNext(); 54 return; 55 } 56 57 long delay = min(this.interval, this.next - now); 58 vibeSleep(delay.msecs); 59 this.setNext(); 60 } 61 } 62 63 /** 64 Ticker which ticks at accurate offsets from a starting point. 65 */ 66 class StaticTicker: Ticker { 67 private { 68 long iter; 69 long startTime; 70 } 71 72 this(Duration interval, bool autoStart=false) { 73 super(interval, autoStart); 74 } 75 76 void reset() { 77 this.iter = 0; 78 this.next = 0; 79 this.start(); 80 } 81 82 override void start() { 83 this.startTime = getEpochTimeMilli(); 84 super.start(); 85 } 86 87 override void setNext() { 88 this.iter += 1; 89 this.next = this.startTime + (this.interval * this.iter); 90 } 91 }