111 lines
3.6 KiB
GDScript
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
|