EvilCocoGame1/addons/reactivex/scheduler/virtualtimescheduler.gd
2025-01-11 00:25:52 +01:00

161 lines
4.0 KiB
GDScript

extends PeriodicScheduler
class_name VirtualTimeScheduler
const MAX_SPINNING = 100
var _clock : float
var _is_enabled : bool
var _lock : Lock
var _queue : PriorityQueue
func _init(initial_clock = 0):
super._init()
self._clock = initial_clock
self._is_enabled = false
self._lock = Lock.new()
self._queue = PriorityQueue.new()
func _get_clock() -> float:
var __ = LockGuard.new(self._lock)
return self._clock
var clock : float:
get:
return _get_clock()
func now() -> float:
return self._clock
## Schedule a new action for future execution
func schedule(action : Callable, state = null) -> DisposableBase:
return self.schedule_absolute(self._clock, action, state)
## Schedule a new action for future execution in [code]duetime[/code] seconds.
func schedule_relative(duetime, action : Callable, state = null) -> DisposableBase:
var time : float = self.add(self._clock, duetime)
return self.schedule_absolute(time, action, state)
## Schedule a new action for future execution at [code]duetime[/code].
func schedule_absolute(duetime, action : Callable, state = null) -> DisposableBase:
var dt : float = duetime
var si : ScheduledItem = ScheduledItem.new(self, dt, state, action)
if true:
var __ = LockGuard.new(self._lock)
self._queue.enqueue(si)
return si.disposable
## Starts the virtual time scheduler.
func start():
if true:
var __ = LockGuard.new(self._lock)
if self._is_enabled:
return
self._is_enabled = true
var spinning : int = 0
while true:
var item : ScheduledItem
if true:
var __ = LockGuard.new(self._lock)
if not self._is_enabled or self._queue.is_empty():
break
item = self._queue.dequeue()
if item.duetime > self.now():
self._clock = item.duetime
spinning = 0
elif spinning > MAX_SPINNING:
self._clock += 1.0
spinning = 0
if not item.is_cancelled():
item.invoke()
spinning += 1
self.stop()
## Stops the virtual time scheduler.
func stop():
var __ = LockGuard.new(self._lock)
self._is_enabled = false
## Advances the schedulers clock to the specified absolute time,
## running all work til that point.
## [br][br]
## [b]Args:[/b] [br]
## [code]time[/code]: Absolute time to advance the schedulers clock to.
func advance_to(time : float):
var dt : float = time
if true:
var __ = LockGuard.new(self._lock)
if self.now() > dt:
ArgumentOutOfRangeError.raise()
return
if self.now() == dt or self._is_enabled:
return
self._is_enabled = true
while true:
var item : ScheduledItem
if true:
var __ = LockGuard.new(self._lock)
if not self._is_enabled or self._queue.is_empty():
break
item = self._queue.peek()
if item.duetime > dt:
break
if item.duetime > self.now():
self._clock = item.duetime
self._queue.dequeue()
if not item.is_cancelled():
item.invoke()
if true:
var __ = LockGuard.new(self._lock)
self._is_enabled = false
self._clock = dt
## Advances the schedulers clock by the specified relative time,
## running all work scheduled for that timespan.
## [br][br]
## [b]Args:[/b] [br]
## [code]time[/code]: Relative time to advance the schedulers clock by.
func advance_by(time : float):
self.advance_to(self.add(self.now(), time))
## Advances the schedulers clock by the specified relative time.
## [br][br]
## [b]Args:[/b] [br]
## [code]time[/code]: Relative time to advance the schedulers clock by.
func sleep(time : float):
var absolute = self.add(self.now(), time)
var dt : float = absolute
if self.now() > dt:
ArgumentOutOfRangeError.raise()
return
var __ = LockGuard.new(self._lock)
self._clock = dt
## Adds a relative time value to an absolute time value.
## [br][br]
## [b]Args:[/b] [br]
## [code]absolute[/code]: Absolute virtual time value. [br]
## [code]relative[/code]: Relative virtual time value to add. [br]
## [br]
## [b]Returns:[/b] [br]
## The resulting absolute virtual time sum value.
static func add(absolute : float, relative : float) -> float:
return absolute + relative