From d64f0a938b67e0497eb476fbb8a6257273ea75d8 Mon Sep 17 00:00:00 2001 From: Mario Liebisch Date: Mon, 31 Jan 2022 11:36:26 +0100 Subject: [PATCH] Initial commit --- .gitignore | 1 + .import/.gdignore | 1 + LICENSE | 19 ++++ README.md | 7 ++ TestScene.gd | 23 ++++ TestScene.tscn | 50 +++++++++ addons/BackgroundWorker/BackgroundWorker.gd | 110 ++++++++++++++++++++ addons/BackgroundWorker/README.md | 73 +++++++++++++ addons/BackgroundWorker/icon.png | Bin 0 -> 286 bytes addons/BackgroundWorker/icon.png.import | 35 +++++++ addons/BackgroundWorker/plugin.cfg | 7 ++ addons/BackgroundWorker/plugin.gd | 8 ++ default_env.tres | 7 ++ icon.png | Bin 0 -> 3305 bytes icon.png.import | 35 +++++++ project.godot | 27 +++++ 16 files changed, 403 insertions(+) create mode 100644 .gitignore create mode 100644 .import/.gdignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 TestScene.gd create mode 100644 TestScene.tscn create mode 100644 addons/BackgroundWorker/BackgroundWorker.gd create mode 100644 addons/BackgroundWorker/README.md create mode 100644 addons/BackgroundWorker/icon.png create mode 100644 addons/BackgroundWorker/icon.png.import create mode 100644 addons/BackgroundWorker/plugin.cfg create mode 100644 addons/BackgroundWorker/plugin.gd create mode 100644 default_env.tres create mode 100644 icon.png create mode 100644 icon.png.import create mode 100644 project.godot diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..37f05f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.import diff --git a/.import/.gdignore b/.import/.gdignore new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.import/.gdignore @@ -0,0 +1 @@ + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e02a34e --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2022 Mario Liebisch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..93bebb3 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# BackgroundWorker Node for Godot 3.x + +This is an example project for my `BackgroundWorker` node, which can be found in the `addons` sub directory. + +The latest version of the addon can be found in [my repository](https://github.com/MarioLiebisch/GD-BackgroundWorker). + +For more details sett the [actual readme file of the addon itself](./addons/BackgroundWorker/README.md). diff --git a/TestScene.gd b/TestScene.gd new file mode 100644 index 0000000..b435a20 --- /dev/null +++ b/TestScene.gd @@ -0,0 +1,23 @@ +extends Node2D + +func _process(delta: float) -> void: + $Icon.rotation_degrees += 180 * delta + + +func _on_BackgroundWorker_busy() -> void: + $Loading.visible = true + + +func _on_BackgroundWorker_free() -> void: + $Loading.visible = false + + +func _on_BackgroundWorker_progress(completed, total) -> void: + $Loading/ProgressBar.max_value = total + $Loading/ProgressBar.value = completed + +func _on_Button_pressed() -> void: + $BackgroundWorker.queue_job(self, "random_job", []) + +func random_job() -> void: + OS.delay_msec(rand_range(1000, 5000)) diff --git a/TestScene.tscn b/TestScene.tscn new file mode 100644 index 0000000..156350c --- /dev/null +++ b/TestScene.tscn @@ -0,0 +1,50 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://icon.png" type="Texture" id=1] +[ext_resource path="res://TestScene.gd" type="Script" id=2] +[ext_resource path="res://addons/BackgroundWorker/BackgroundWorker.gd" type="Script" id=3] + +[node name="TestScene" type="Node2D"] +script = ExtResource( 2 ) + +[node name="Icon" type="Sprite" parent="."] +position = Vector2( 65, 66 ) +texture = ExtResource( 1 ) + +[node name="Loading" type="VBoxContainer" parent="."] +visible = false +margin_left = 156.0 +margin_top = 22.0 +margin_right = 379.0 +margin_bottom = 62.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label" type="Label" parent="Loading"] +margin_right = 223.0 +margin_bottom = 14.0 +text = "Loading..." + +[node name="ProgressBar" type="ProgressBar" parent="Loading"] +margin_top = 18.0 +margin_right = 223.0 +margin_bottom = 32.0 + +[node name="BackgroundWorker" type="Node" parent="."] +script = ExtResource( 3 ) + +[node name="Button" type="Button" parent="."] +margin_left = 34.0 +margin_top = 136.0 +margin_right = 214.0 +margin_bottom = 218.0 +text = "Do some WORK!" +__meta__ = { +"_edit_use_anchors_": false +} + +[connection signal="busy" from="BackgroundWorker" to="." method="_on_BackgroundWorker_busy"] +[connection signal="free" from="BackgroundWorker" to="." method="_on_BackgroundWorker_free"] +[connection signal="progress" from="BackgroundWorker" to="." method="_on_BackgroundWorker_progress"] +[connection signal="pressed" from="Button" to="." method="_on_Button_pressed"] diff --git a/addons/BackgroundWorker/BackgroundWorker.gd b/addons/BackgroundWorker/BackgroundWorker.gd new file mode 100644 index 0000000..1ecd028 --- /dev/null +++ b/addons/BackgroundWorker/BackgroundWorker.gd @@ -0,0 +1,110 @@ +extends Node + +## The minimum time in seconds the workers have to be busy for visibility to kick in +export var show_timeout = 0.250 + +## The mimium time in seconds the workers have to be idle for visibility to cease +export var hide_timeout = 0.500 + +## The number of worker threads to be used +export var worker_count = 2 + + +var visible = false +var jobs: Array +var start_time: float +var last_time: float +var running = 0 +var threads: Array +var active = false +var completed = 0 +var total = 0 + +## Triggered once work starts +signal started() +## Triggered once work is finished +signal finished(total) +## Triggered once UI hints should be shown +signal busy() +## Triggered once UI hints should be hidden +signal free() +## Triggered whenever progress updates +signal progress(completed, total) + +class BackgroundWorkerJob: + var target: Object + var method: String + var binds: Array + + ## Creates a new Background Worker Job + func _init(target: Object, method: String, binds: Array = []) -> void: + self.target = target + self.method = method + self.binds = binds + + ## Executes the job and returns true, if processing of the job is complete + func execute(_dummy: BackgroundWorkerJob = null) -> void: + target.callv(method, binds) + +## Queue up a member function call as a new job +func queue_job(target: Object, method: String, binds: Array = []) -> void: + jobs.append(BackgroundWorkerJob.new(target, method, binds)) + total += 1 + +## Queue up a resource load as a job +func queue_load(file: String, type_hint: String = '') -> void: + jobs.append(BackgroundWorkerJob.new(ResourceLoader, 'load', [file, type_hint])) + total += 1 + +## Clear all still unprocessed jobs +func clear_queue() -> void: + total -= len(jobs) + jobs.clear() + +## Return the number of queued jobs +func queue_length() -> int: + return len(jobs) + +func _process(_delta: float) -> void: + if active and not visible: + if OS.get_ticks_msec() / 1000.0 > start_time + show_timeout: + emit_signal('progress', completed, total) + emit_signal('busy') + visible = true + if len(jobs) > 0: + while len(threads) < worker_count: + threads.append(Thread.new()) + + if not active: + start_time = OS.get_ticks_msec() / 1000.0 + active = true + emit_signal('started') + var c = 0 + for i in worker_count: + c += 1 + if not threads[i].is_active(): + var job = jobs.front() + threads[i].start(job, 'execute', job) + jobs.pop_front() + running += 1 + break + last_time = OS.get_ticks_msec() / 1000.0 + emit_signal('progress', completed, total) + if running > 0: + var c = 0 + for thread in threads: + c += 1 + if thread.is_active() and not thread.is_alive(): + thread.wait_to_finish() + running -= 1 + completed += 1 + emit_signal('progress', completed, total) + if running == 0: + active = false + emit_signal('finished', total) + completed = 0 + total = 0 + last_time = OS.get_ticks_msec() / 1000.0 + elif not active and visible and OS.get_ticks_msec() / 1000.0 > last_time + hide_timeout: + emit_signal('free') + visible = false diff --git a/addons/BackgroundWorker/README.md b/addons/BackgroundWorker/README.md new file mode 100644 index 0000000..6199ec5 --- /dev/null +++ b/addons/BackgroundWorker/README.md @@ -0,0 +1,73 @@ +# BackgroundWorker Node for Godot 3.x + +This plugin provides a new node `BackgroundWorker` to make it easy to queue up and track background tasks. +This can be used to preload scenes, save games, synchronize or upload files, etc. + +All queued up jobs are processed by an adjustable amount of worker threads. +For strict linear processing "one after the other" you can reduce the number of active worker threads to one. + +Progress is reported back through signals, although hooked up functions can still trigger their own signals from their own thread in case you need it. + +## Installation + +The latest version of the addon can be found in [my repository](https://github.com/MarioLiebisch/GD-BackgroundWorker). + +As with any other Godot addon, put the contents of `addons/BackgroundWorker` in your own project's `addons` directory and make sure to enable it in Project Setting's "Plugins" tab. + +## Usage + +To queue up new jobs, call the member methods `queue_job(target, method, [binds])` or `queue_load(file, [type_hint]`). Processing starts automatically on the next frame. + +### Properties + +#### show_timeout [= 0.250] + +The minimum time in seconds the workers have to be busy for visibility to kick in. + +#### hide_timeout [= 0.500] + +The mimium time in seconds the workers have to be idle for visibility to cease + +#### worker_count [= 2] + +The number of worker threads to be used. + +### Methods + +#### queue_job(target: Object, method: String, binds: Array = []) -> void + +Queues a new job to call `method()` on `target` passing `binds` (if set). + +#### queue_load(file: String, type_hint: String = '') -> void + +Queues loading a file using Godot's `load()`, passing the optional `type_hint` (if set). + +#### clear_queue() -> void + +Remove/cancel any queued job not yet started. + +#### queue_length() -> int + +Return the number of queued jobs not yet started. + +### Signals + +#### started() + +Triggered as soon as the first job starts processing. + +#### finished(total: int) + +Triggered once all queued up jobs have finished processing. + +#### busy() + +Triggered once busy longer than defined in `show_timeout` to show a visible indicator. + +#### free() + +Triggered once all queued up jobs have finished and the timeout defined in `hide_timeout` has passed to hide indicators. + +#### progress(completed: int, total: int) + +Triggered whenever progress is made. This can be used to show relative progress to the user. diff --git a/addons/BackgroundWorker/icon.png b/addons/BackgroundWorker/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..efd12cebd179ffd2ae88feefda21083b36f81a52 GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBufiR<}hF1enaI>e2V~B void: + add_custom_type("BackgroundWorker", "Node", preload("BackgroundWorker.gd"), preload("icon.png")) + +func _exit_tree() -> void: + remove_custom_type("BackgroundWorker") diff --git a/default_env.tres b/default_env.tres new file mode 100644 index 0000000..20207a4 --- /dev/null +++ b/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" load_steps=2 format=2] + +[sub_resource type="ProceduralSky" id=1] + +[resource] +background_mode = 2 +background_sky = SubResource( 1 ) diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c98fbb601c83c81ec8c22b1dba7d1d57c62b323c GIT binary patch literal 3305 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 literal 0 HcmV?d00001 diff --git a/icon.png.import b/icon.png.import new file mode 100644 index 0000000..a4c02e6 --- /dev/null +++ b/icon.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.png" +dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..bb80657 --- /dev/null +++ b/project.godot @@ -0,0 +1,27 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +[application] + +config/name="GD-BackgroundWorker" +run/main_scene="res://TestScene.tscn" +config/icon="res://icon.png" + +[editor_plugins] + +enabled=PoolStringArray( "res://addons/BackgroundWorker/plugin.cfg" ) + +[physics] + +common/enable_pause_aware_picking=true + +[rendering] + +environment/default_environment="res://default_env.tres"