EvilCocoGame1/addons/reactivex/__gdrxsingleton__.gd

529 lines
22 KiB
GDScript3
Raw Normal View History

2025-01-11 00:25:52 +01:00
extends Node
class_name __GDRx_Singleton__
## GDRx Singleton Script
##
## Provides access to [Observable] constructors and operators.
## [color=yellow] Please add to autoload with name [code]GDRx[/code]![/color]
# =========================================================================== #
# Init script database
# =========================================================================== #
## Access to internals
var __init__ : __GDRx_Init__ = __GDRx_Init__.new()
## [Observable] constructor functions
var obs : __GDRx_Obs__ = __GDRx_Obs__.new()
## [Observable] operator functions
var op : __GDRx_Op__ = __GDRx_Op__.new()
## Engine Backend
var gd : __GDRx_Engine__ = __GDRx_Engine__.new()
## See [OnNextNotification]
var OnNext = __init__.NotificationOnNext_
## See [OnErrorNotification]
var OnError = __init__.NotificationOnError_
## See [OnCompletedNotification]
var OnCompleted = __init__.NotificationOnCompleted_
## Internal heap implementation
var heap = __init__.Heap_.new()
## Basic functions & types
var basic = __init__.Basic_.new()
## Concurrency functions & types
var concur = __init__.Concurrency_.new()
## Utility functions & types
var util = __init__.Util_.new()
## Access to pipe operators
var pipe = __init__.Pipe_.new()
# =========================================================================== #
# Constructor/Destructor
# =========================================================================== #
func _init():
## Insert Main Thread
if true:
var reglock : ReadWriteLock = self.THREAD_MANAGER.THREAD_REGISTRY.at(0)
reglock.w_lock()
self.THREAD_MANAGER.THREAD_REGISTRY.at(1)[MAIN_THREAD_ID] = MAIN_THREAD
reglock.w_unlock()
## Init [SceneTreeScheduler] singleton
for i in range(8):
var process_always : bool = bool(i & 0b1)
var process_in_physics : bool = bool(i & 0b10)
var ignore_time_scale : bool = bool(i & 0b100)
self.SceneTreeTimeoutScheduler_.push_back(SceneTreeTimeoutScheduler.new(
"GDRx", process_always, process_in_physics, ignore_time_scale))
func _notification(what):
if what == NOTIFICATION_PREDELETE:
self.THREAD_MANAGER.stop_and_join()
self.THREAD_MANAGER.free()
# =========================================================================== #
# Multi-Threading
# =========================================================================== #
## Dummy instance for the Main Thread
var MAIN_THREAD = concur.MainThreadDummy_.new()
## ID of the main thread
var MAIN_THREAD_ID = OS.get_thread_caller_id()
## Thread Manager
var THREAD_MANAGER = concur.ThreadManager.new()
## Returns the caller's current [Thread]
## If the caller thread is the main thread, it returns [b]MAIN_THREAD[/b].
func get_current_thread() -> Thread:
var id = OS.get_thread_caller_id()
var l : ReadWriteLock = self.THREAD_MANAGER.THREAD_REGISTRY.at(0)
l.r_lock()
id = self.THREAD_MANAGER.THREAD_REGISTRY.at(1)[id]
l.r_unlock()
return id
# =========================================================================== #
# Scheduler Singletons
# =========================================================================== #
## [ImmediateScheduler] Singleton; [color=red]Do [b]NOT[/b] access directly![/color]
var ImmediateScheduler_ : ImmediateScheduler = ImmediateScheduler.new("GDRx")
## [SceneTreeTimeoutScheduler] Singleton; [color=red]Do [b]NOT[/b] access directly![/color]
var SceneTreeTimeoutScheduler_ : Array[SceneTreeTimeoutScheduler]
## [ThreadedTimeoutScheduler] Singleton; [color=red]Do [b]NOT[/b] access directly![/color]
var ThreadedTimeoutScheduler_ : ThreadedTimeoutScheduler = ThreadedTimeoutScheduler.new("GDRx")
## [NewThreadScheduler] Singleton; [color=red]Do [b]NOT[/b] access directly![/color]
var NewThreadScheduler_ : NewThreadScheduler = NewThreadScheduler.new(self.concur.default_thread_factory)
## [GodotSignalScheduler] Singleton; [color=red]Do [b]NOT[/b] access directly![/color]
var GodotSignalScheduler_ : GodotSignalScheduler = GodotSignalScheduler.new("GDRx")
## Global singleton of [CurrentThreadScheduler]
var CurrentThreadScheduler_global_ : WeakKeyDictionary = WeakKeyDictionary.new()
## Thread local singleton of [CurrentThreadScheduler]
var CurrentThreadScheduler_local_ = CurrentThreadScheduler._Local.new()
# =========================================================================== #
# Error Handler Singleton
# =========================================================================== #
## [ErrorHandler] singleton; [color=red]Leave it alone![/color]
var ErrorHandler_ : WeakKeyDictionary = WeakKeyDictionary.new()
# =========================================================================== #
# Helper functions
# =========================================================================== #
## Equality
func eq(x, y) -> bool:
return GDRx.basic.default_comparer(x, y)
## Negated equality
func neq(x, y) -> bool:
return !eq(x, y)
## Less than operator
func lt(x, y) -> bool:
return x.lt(y) if (x is Object and x.has_method("lt")) else x < y
## Greater than operator
func gt(x, y) -> bool:
return x.gt(y) if (x is Object and x.has_method("gt")) else x > y
## Greater than equals operator
func gte(x, y) -> bool:
return x.gte(y) if (x is Object and x.has_method("gte")) else x >= y
## Less than equals operator
func lte(x, y) -> bool:
return x.lte(y) if (x is Object and x.has_method("lte")) else x <= y
## Raises a [AssertionFailedError] and returns [b]true[/b]
## should the assertion fail.
func assert_(assertion : bool, message : String = "Assertion failed!") -> bool:
if not assertion:
AssertionFailedError.new(message).throw()
return not assertion
## Creates a new [TryCatch] Statement
func try(fun : Callable) -> TryCatch:
return TryCatch.new(fun)
## Raises a [ThrowableBase]
func raise(exc_ : ThrowableBase, default = null) -> Variant:
return ErrorHandler.singleton().raise(exc_, default)
## Raises a [RxBaseError] containing the given message
func raise_message(msg : String, default = null):
return RxBaseError.raise(default, msg)
## Construct an [IterableBase] onto x.
func to_iterable(x) -> IterableBase:
return Iterator.to_iterable(x)
### Construct an [Iterator] onto x.
func iter(x) -> Iterator:
return Iterator.iter(x)
### Creates a [WhileIterable] from a given condition and another [IterableBase]
func take_while(cond : Callable, it : IterableBase) -> IterableBase:
return WhileIterable.new(it, cond)
### Generates an [InfiniteIterable] sequence of a given value.
func infinite(infval = NOT_SET) -> IterableBase:
return InfiniteIterable.new(infval)
### NOT Set value
var NOT_SET:
get: return util.NOT_SET
## Is NOT Set value
func not_set(value) -> bool:
return NOT_SET.eq(value)
## Unit item
var UNIT:
get: return StreamItem.Unit()
## Alias for [code]GDRx.util.add_ref()[/code]
func add_ref(xs : Observable, r : RefCountDisposable) -> Observable:
return util.add_ref(xs, r)
## Create an observable sequence from an array
func of(data : Array, scheduler : SchedulerBase = null) -> Observable:
return self.from_array(data, scheduler)
## Create an observable on any given iterable sequence
func from(seq, scheduler : SchedulerBase = null) -> Observable:
return self.from_iterable(to_iterable(seq), scheduler)
## Alias for [code]GDRx.obs.return_value[/code]
func just(value, scheduler : SchedulerBase = null) -> Observable:
return self.return_value(value, scheduler)
## Empty operation as defined in [code]GDRx.basic.noop[/code]
func noop(__ = null, ___ = null):
GDRx.basic.noop(__, ___)
## Identity mapping as defined in [code]GDRx.basic.identity[/code]
func identity(x, __ = null):
return GDRx.basic.identity(x, __)
# =========================================================================== #
# Observable Constructors
# =========================================================================== #
## See: [b]res://addons/reactivex/observable/amb.gd[/b]
func amb(sources) -> Observable:
return obs.amb(sources)
## See: [b]res://addons/reactivex/observable/case.gd[/b]
func case(mapper : Callable, sources : Dictionary, default_source : Observable = null) -> Observable:
return obs.case(mapper, sources, default_source)
## Create a new observable
func create(subscribe : Callable = func(_observer : ObserverBase, _scheduler : SchedulerBase = null) -> DisposableBase: return Disposable.new()) -> Observable:
return Observable.new(subscribe)
## See: [b]res://addons/reactivex/observable/catch.gd[/b]
func catch(sources : Array[Observable]) -> Observable:
return obs.catch_with_iterable(to_iterable(sources))
## See: [b]res://addons/reactivex/observable/catch.gd[/b]
func catch_with_iterable(sources : IterableBase) -> Observable:
return obs.catch_with_iterable(sources)
## See: [b]res://addons/reactivex/observable/combinelatest.gd[/b]
func combine_latest(sources) -> Observable:
return obs.combine_latest(sources)
## See: [b]res://addons/reactivex/observable/concat.gd[/b]
func concat_streams(sources : Array[Observable]) -> Observable:
return obs.concat_with_iterable(to_iterable(sources))
## See: [b]res://addons/reactivex/observable/concat.gd[/b]
func concat_with_iterable(sources : IterableBase) -> Observable:
return obs.concat_with_iterable(sources)
## See: [b]res://addons/reactivex/observable/defer.gd[/b]
func defer(factory : Callable = GDRx.basic.default_factory) -> Observable:
return obs.defer(factory)
## See: [b]res://addons/reactivex/observable/empty.gd[/b]
func empty(scheduler : SchedulerBase = null) -> Observable:
return obs.empty(scheduler)
## See: [b]res://addons/reactivex/observable/forkjoin.gd[/b]
func fork_join(sources) -> Observable:
return obs.fork_join(sources)
## See: [b]res://addons/reactivex/observable/fromcallback.gd[/b]
func from_callback(fun : Callable = func(_args : Array, _cb : Callable): return, mapper = null) -> Callable:
return obs.from_callback(fun, mapper)
## Transforms an array into an observable sequence.
func from_array(array : Array, scheduler : SchedulerBase = null) -> Observable:
return obs.from_iterable(to_iterable(array), scheduler)
## See: [b]res://addons/reactivex/observable/fromiterable.gd[/b]
func from_iterable(iterable : IterableBase, scheduler : SchedulerBase = null) -> Observable:
return obs.from_iterable(iterable, scheduler)
## See: [b]res://addons/reactivex/observable/generate.gd[/b]
func generate(initial_state, condition : Callable = GDRx.basic.default_condition, iterate : Callable = GDRx.basic.identity) -> Observable:
return obs.generate(initial_state, condition, iterate)
## See: [b]res://addons/reactivex/observable/generatewithrealtivetime.gd[/b]
func generate_with_relative_time(initial_state, condition : Callable = GDRx.basic.default_condition, iterate : Callable = GDRx.basic.identity, time_mapper : Callable = func(_state) -> float: return 1.0) -> Observable:
return obs.generate_with_relative_time(initial_state, condition, iterate, time_mapper)
## See: [b]res://addons/reactivex/observable/ifthen.gd[/b]
func if_then(condition : Callable = GDRx.basic.default_condition, then_source : Observable = null, else_source : Observable = null) -> Observable:
return obs.if_then(condition, then_source, else_source)
## See: [b]res://addons/reactivex/observable/interval.gd[/b]
func interval(period : float, scheduler : SchedulerBase = null) -> ObservableBase:
return obs.interval(period, scheduler)
## See: [b]res://addons/reactivex/observable/merge.gd[/b]
func merge(sources) -> Observable:
return obs.merge(sources)
## See: [b]res://addons/reactivex/observable/never.gd[/b]
func never() -> Observable:
return obs.never()
## See: [b]res://addons/reactivex/observable/onerrorresumenext.gd[/b]
func on_error_resume_next(sources : Array) -> Observable:
return obs.on_error_resume_next(sources)
## See: [b]res://addons/reactivex/observable/range.gd[/b]
@warning_ignore("shadowed_global_identifier")
func range(start : int, stop = null, step = null, scheduler : SchedulerBase = null) -> Observable:
return obs.range(start, stop, step, scheduler)
## See: [b]res://addons/reactivex/observable/repeat.gd[/b]
func repeat_value(value, repeat_count = null) -> Observable:
return obs.repeat_value(value, repeat_count)
## See: [b]res://addons/reactivex/observable/returnvalue.gd[/b]
func return_value(value, scheduler : SchedulerBase = null) -> Observable:
return obs.return_value(value, scheduler)
## See: [b]res://addons/reactivex/observable/returnvalue.gd[/b]
func from_callable(supplier : Callable, scheduler : SchedulerBase = null) -> Observable:
return obs.from_callable(supplier, scheduler)
## See: [b]res://addons/reactivex/observable/throw.gd[/b]
func throw(err, scheduler : SchedulerBase = null) -> Observable:
return obs.throw(err, scheduler)
## See: [b]res://addons/reactivex/observable/timer.gd[/b]
func timer(duetime : float, time_absolute : bool, period = null, scheduler : SchedulerBase = null) -> Observable:
return obs.timer(duetime, time_absolute, period, scheduler)
## See: [b]res://addons/reactivex/observable/toasync.gd[/b]
func to_async(fun : Callable, scheduler : SchedulerBase = null) -> Callable:
return obs.to_async(fun, scheduler)
## See: [b]res://addons/reactivex/observable/using.gd[/b]
func using(resource_factory : Callable, observable_factory : Callable) -> Observable:
return obs.using(resource_factory, observable_factory)
## See: [b]res://addons/reactivex/observable/withlatestfrom.gd[/b]
func with_latest_from(sources) -> Observable:
var _sources : Array[Observable] = util.unpack_arg(sources)
assert_(_sources.size() > 0)
var parent = _sources.pop_front()
return obs.with_latest_from(parent, _sources)
## See: [b]res://addons/reactivex/observable/zip.gd[/b]
func zip(sources) -> Observable:
return obs.zip(sources)
# =========================================================================== #
# Timers
# =========================================================================== #
## Creates an observable timer
func start_timer(timespan_sec : float, scheduler : SchedulerBase = null) -> Observable:
return obs.timer(timespan_sec, false, null, scheduler)
## Creates an observable periodic timer
func start_periodic_timer(period_sec : float, scheduler : SchedulerBase = null) -> Observable:
return obs.timer(period_sec, false, period_sec, scheduler)
## Creates an observable periodic timer which starts after a timespan has passed
func start_periodic_timer_after_timespan(timespan_sec : float, period_sec : float, scheduler : SchedulerBase = null) -> Observable:
return obs.timer(timespan_sec, false, period_sec, scheduler)
## Creates an observable timer
func schedule_datetime(datetime_sec : float, scheduler : SchedulerBase = null) -> Observable:
return obs.timer(datetime_sec, true, null, scheduler)
## Creates an observable periodic timer which starts at a given timestamp.
func start_periodic_timer_at_datetime(datetime_sec : float, period_sec : float, scheduler : SchedulerBase = null) -> Observable:
return obs.timer(datetime_sec, true, period_sec, scheduler)
# =========================================================================== #
# Godot-specific Observable Constructors
# =========================================================================== #
## Creates an observable from a Godot Signal
func from_signal(sig : Signal) -> Observable:
return gd.from_godot_signal(sig)
## Creates an observable from a Coroutine
func from_coroutine(fun : Callable, bindings : Array = [], scheduler : SchedulerBase = null) -> Observable:
return gd.from_godot_coroutine(fun, bindings, scheduler)
## Emits items from [method Node._process].
func on_process_as_observable(conn : Node) -> Observable:
return gd.from_godot_node_lifecycle_event(conn, 0)
## Emits items from [method Node._physics_process].
func on_physics_process_as_observable(conn : Node) -> Observable:
return gd.from_godot_node_lifecycle_event(conn, 1)
## Emits items from [method Node._input].
func on_input_as_observable(conn : Node) -> Observable:
return gd.from_godot_node_lifecycle_event(conn, 2)
## Emits items from [method Node._shortcut_input].
func on_shortcut_input_as_observable(conn : Node) -> Observable:
return gd.from_godot_node_lifecycle_event(conn, 3)
## Emits items from [method Node._unhandled_input].
func on_unhandled_input_as_observable(conn : Node) -> Observable:
return gd.from_godot_node_lifecycle_event(conn, 4)
## Emits items from [method Node._unhandled_key_input].
func on_unhandled_key_input_as_observable(conn : Node) -> Observable:
return gd.from_godot_node_lifecycle_event(conn, 5)
## Tranforms an input action into an observable sequence emiting items on check.
func input_action(input_action_ : String, checks : Observable) -> Observable:
return gd.from_godot_input_action(input_action_, checks)
## Creates a new Compute Shader as [Observable].
func from_compute_shader(shader_path : String, rd : RenderingDevice, work_groups : Vector3i, uniform_sets : Array = [], scheduler : SchedulerBase = null) -> Observable:
return gd.from_compute_shader(shader_path, rd, work_groups, uniform_sets, scheduler)
## Emits items when the node enters the scene tree
func on_tree_enter_as_observable(node : Node) -> Observable:
return from_signal(node.tree_entered)
## Emits items when the node just exited the scene tree
func on_tree_exit_as_observable(node : Node) -> Observable:
return from_signal(node.tree_exited)
## Emits items when the node is about to exit the scene tree
func on_tree_exiting_as_observable(node : Node) -> Observable:
return from_signal(node.tree_exiting)
## Creates an HTTP Request
func from_http_request(url : String, request_data = "", raw : bool = false, encoding : String = "", requester : HTTPRequest = null, custom_headers : PackedStringArray = PackedStringArray(), method : HTTPClient.Method = HTTPClient.METHOD_GET) -> Observable:
return gd.from_http_request(url, request_data, raw, encoding, requester, custom_headers, method)
# =========================================================================== #
# Some useful Input Observables
# =========================================================================== #
## Emits item when mouse button is just pressed
func on_mouse_down() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventMouseButton) \
.filter(func(ev : InputEventMouseButton): return ev.is_pressed())
## Emits item when mouse button is just released
func on_mouse_up() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventMouseButton) \
.filter(func(ev : InputEventMouseButton): return not ev.is_pressed())
## Emits item on mouse double-click
func on_mouse_double_click() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventMouseButton) \
.filter(func(ev : InputEventMouseButton): return ev.is_pressed() and ev.double_click)
## Emits items on mouse motion
func on_mouse_motion() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventMouseMotion)
## Emits the relative mouse motion as a [Vector2].
func relative_mouse_movement_as_observable() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventMouseMotion) \
.map(func(ev : InputEventMouseMotion): return ev.relative)
## Emits an item when the given keycode is just pressed.
func on_key_just_pressed(key : int) -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventKey) \
.filter(func(ev : InputEventKey): return ev.keycode == key and ev.pressed and not ev.echo)
## Emits an item when the given keycode is pressed.
func on_key_pressed(key : int) -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventKey) \
.filter(func(ev : InputEventKey): return ev.keycode == key and ev.pressed)
## Emits an item when the given keycode is just released.
func on_key_just_released(key : int) -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventKey) \
.filter(func(ev : InputEventKey): return ev.keycode == key and not ev.pressed)
## Emits an item, when the screen is touched (touch devices).
func on_screen_touch() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventScreenTouch)
## Emits an item, when the touch screen notices a drag gesture (touch devices)
func on_screen_drag() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventScreenDrag)
## Emits an item on Midi event.
func on_midi_event() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventMIDI)
## Emits an item, when a joypad button is just pressed.
func on_joypad_button_down() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventJoypadButton) \
.filter(func(ev : InputEventJoypadButton): return not ev.is_echo() and ev.is_pressed())
## Emits an item, when a joypad button is pressed.
func on_joypad_button_pressed() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventJoypadButton) \
.filter(func(ev : InputEventJoypadButton): return ev.is_pressed())
## Emits an item, when a joypad button is just released.
func on_joypad_button_released() -> Observable:
return on_input_as_observable(self) \
.filter(func(ev : InputEvent): return ev is InputEventJoypadButton) \
.filter(func(ev : InputEventJoypadButton): return not ev.is_pressed())
# =========================================================================== #
# Frame Events
# =========================================================================== #
## Emits items on idle frame events.
func on_idle_frame() -> Observable:
return from_signal(self.get_tree().process_frame) \
.map(func(__): return get_process_delta_time())
## Emits items on physics frame events.
func on_physics_step() -> Observable:
return from_signal(self.get_tree().physics_frame) \
.map(func(__): return get_physics_process_delta_time())
## Emits an item when the scene tree has changed.
func on_tree_changed() -> Observable:
return from_signal(self.get_tree().tree_changed)
## Emits an item at post-draw frame event.
func on_frame_post_draw() -> Observable:
return from_signal(RenderingServer.frame_post_draw)
## Emits an item at pre-draw frame event.
func on_frame_pre_draw() -> Observable:
return from_signal(RenderingServer.frame_pre_draw)