TestGame/addons/reactivex/scheduler/catchscheduler.gd
2024-12-27 21:00:07 +01:00

111 lines
3.6 KiB
GDScript

extends PeriodicScheduler
class_name CatchScheduler
## A scheduler with error handling wrapping a [SchedulerBase]
var _scheduler : SchedulerBase
var _handler : Callable
var _recursive_original : SchedulerBase
var _recursive_wrapper : CatchScheduler
## Wraps a scheduler, passed as constructor argument, adding error
## handling for scheduled actions. The handler should return [b]true[/b] to
## indicate it handled the error successfully. Falsy return values will
## be taken to indicate that the error should be escalated (raised by
## this scheduler).
## [br][br]
## [b]Args:[/b] [br]
## [code]scheduler[/code]: The scheduler to be wrapped. [br]
## [code]handler[/code]: Callable to handle errors raised by wrapped scheduler.
func _init(scheduler : SchedulerBase, handler : Callable):
super._init()
self._scheduler = scheduler
self._handler = handler
self._recursive_original = null
self._recursive_wrapper = null
## Returns the current point in time (timestamp)
func now() -> float:
return self._scheduler.now()
## Schedule a new action for future execution
func schedule(action : Callable, state = null) -> DisposableBase:
action = self._wrap(action)
return self._scheduler.schedule(action, state)
## Schedule a new action for future execution in [code]duetime[/code] seconds.
func schedule_relative(duetime, action : Callable, state = null) -> DisposableBase:
action = self._wrap(action)
return self._scheduler.schedule_relative(duetime, action, state)
## Schedule a new action for future execution at [code]duetime[/code].
func schedule_absolute(duetime, action : Callable, state = null) -> DisposableBase:
action = self._wrap(action)
return self._scheduler.schedule_absolute(duetime, action, state)
## Schedule a periodic action for repeated execution every time
## [code]period[/code] seconds have expired.
func schedule_periodic(
period : float,
action : Callable,
state = null) -> DisposableBase:
var schedule_periodic = self._scheduler.get("schedule_periodic")
if schedule_periodic == null:
NotImplementedError.raise()
return Disposable.new()
var disp : SingleAssignmentDisposable = SingleAssignmentDisposable.new()
var failed : RefValue = RefValue.Set(false)
var periodic = func(state = null):
if failed.v:
return null
var res = RefValue.Null()
if GDRx.try(func():
res.v = action.call(state)
) \
.catch("Error", func(e):
failed.v = true
if not self._handler.call(e):
GDRx.raise(e)
disp.dispose()
) \
.end_try_catch():
return null
return res.v
var scheduler : PeriodicScheduler = self._scheduler
disp.disposable = scheduler.schedule_periodic(period, periodic, state)
return disp
func _clone(scheduler : SchedulerBase) -> CatchScheduler:
return CatchScheduler.new(scheduler, self._handler)
func _wrap(action : Callable) -> Callable:
var parent : CatchScheduler = self
var wrapped_action = func(self_ : SchedulerBase, state = null):
var res = RefValue.Null()
if GDRx.try(func():
res.v = action.call(parent._get_recursive_wrapper(self_), state)
) \
.catch("Error", func(ex):
if not parent._handler.call(ex):
GDRx.raise(ex)
) \
.end_try_catch():
return Disposable.new()
return res.v
return wrapped_action
func _get_recursive_wrapper(scheduler : SchedulerBase) -> CatchScheduler:
if self._recursive_wrapper == null or self._recursive_original != scheduler:
self._recursive_original = scheduler
var wrapper = self._clone(scheduler)
wrapper._recursive_original = scheduler
wrapper._recursive_wrapper = wrapper
self._recursive_wrapper = wrapper
return self._recursive_wrapper