diff --git a/Classes/ReleasesManager.gd b/Classes/ReleasesManager.gd index 0a24988..a8d6556 100644 --- a/Classes/ReleasesManager.gd +++ b/Classes/ReleasesManager.gd @@ -21,22 +21,55 @@ var threads: Array[Thread] signal releases_fetched(releases: Releases) signal version_downloaded(version: String) signal export_templates_downloaded(version: String) +signal status_update(status: String) +enum Status{ + IDLE, + RELEASE_DOWNLOAD_IN_PROGRESS, + TEMPLATE_DOWNLOAD_IN_PROGRESS, + DOWNLOAD_DONE, + FETCHING_RELEASES, + FETCHING_DONE, + VERSION_NOT_IN_INDEX, + RELEASE_ALREADY_EXISTS, + TEMPLATE_ALREADY_EXISTS, + UNPACKING_TEMPLATES, + UNPACKING_RELEASE, + RELEASE_INSTALLED, + TEMPLATE_INSTALLED, +} + +const STATUS_MESSAGES := { + Status.IDLE: "Idle", + Status.RELEASE_DOWNLOAD_IN_PROGRESS: "Downloading Godot v%s", + Status.TEMPLATE_DOWNLOAD_IN_PROGRESS: "Downloading Export Templates for v%s", + Status.DOWNLOAD_DONE: "Done downloading", + Status.FETCHING_RELEASES: "Fetching releases", + Status.FETCHING_DONE: "Done fetching releases", + Status.VERSION_NOT_IN_INDEX: "Version not in index. Fetch first", + Status.RELEASE_ALREADY_EXISTS: "This version already exists", + Status.TEMPLATE_ALREADY_EXISTS: "Templates for this version already exist", + Status.UNPACKING_TEMPLATES: "Unpacking Export Templates for v%s", + Status.UNPACKING_RELEASE: "Unpacking Godot v%s", + Status.RELEASE_INSTALLED: "Godot v%s installed", + Status.TEMPLATE_INSTALLED: "Export Templates for Godot v%s installed", +} func _ready() -> void: DirAccess.make_dir_absolute(TEMP_FOLDER) DirAccess.make_dir_absolute(RELEASES_BASE_FOLDER) add_child(http) releases = load_releases() + clean_tmp() func fetch_releases(force_local: bool = false) -> void: - if force_local: + if force_local && FileAccess.file_exists(RELEASES_FILE): releases_fetched.emit(releases) return if is_downloading: - print("downloading something, try again later") + print("Another download is in progress, try again later") return if releases.last_checked_at + FETCH_COOLDOWN > Time.get_unix_time_from_system(): @@ -82,7 +115,8 @@ func fetch_releases(force_local: bool = false) -> void: if rm.is_empty(): releases.releases.erase(i.tag_name) - releases_fetched.emit() + releases_fetched.emit(releases) + status_update.emit(STATUS_MESSAGES[Status.FETCHING_DONE]) is_downloading = false save_releases() @@ -92,6 +126,7 @@ func fetch_releases(force_local: bool = false) -> void: "X-GitHub-Api-Version: 2022-11-28", ] is_downloading = true + status_update.emit(STATUS_MESSAGES[Status.FETCHING_RELEASES]) http.request(API_SLUG + RELEASES_SLUG, PackedStringArray(headers)) http.request_completed.connect(response_func, CONNECT_ONE_SHOT) @@ -102,11 +137,13 @@ func download_release(version: String) -> void: return if !releases.releases.has(version): - print("this version is not in the index yet or does not exist. fetch first") +# print("this version is not in the index yet or does not exist. fetch first") + status_update.emit(STATUS_MESSAGES[Status.VERSION_NOT_IN_INDEX]) return if (releases.releases[version] as ReleaseMetadata).binary_path != "": - print("already have this version") +# print("already have this version") + status_update.emit(STATUS_MESSAGES[Status.RELEASE_ALREADY_EXISTS]) return var rm: ReleaseMetadata = releases.releases[version] @@ -114,6 +151,7 @@ func download_release(version: String) -> void: if response_code == 200: # download happens in place. # we know the file is zip, so save it to temp first before unpacking + status_update.emit(STATUS_MESSAGES[Status.DOWNLOAD_DONE]) print("got it in place") print("downloading url: ", rm.binary_github_filename) var zip_filename := TEMP_FOLDER.path_join("%s.zip" % version) @@ -132,7 +170,8 @@ func download_release(version: String) -> void: return var res := zip.read_file(filename.trim_suffix(".zip")) zip.close() -# clean_tmp() + clean_tmp() + status_update.emit(STATUS_MESSAGES[Status.UNPACKING_RELEASE] % version) var d := DirAccess.open(RELEASES_BASE_FOLDER) d.make_dir_recursive(version) var new_file := RELEASES_BASE_FOLDER.path_join(version) @@ -142,6 +181,7 @@ func download_release(version: String) -> void: f.close() rm.binary_path = new_file version_downloaded.emit(version) + status_update.emit(STATUS_MESSAGES[Status.RELEASE_INSTALLED] % version) is_downloading = false save_releases() @@ -155,6 +195,7 @@ func download_release(version: String) -> void: "X-GitHub-Api-Version: 2022-11-28", ] var asset_url = API_SLUG.path_join(RELEASES_SLUG).path_join(ASSET_SLUG) % rm.binary_github_asset_id + status_update.emit(STATUS_MESSAGES[Status.RELEASE_DOWNLOAD_IN_PROGRESS] % version) is_downloading = true http.request(asset_url, PackedStringArray(headers)) http.request_completed.connect(response_func.bind(rm.binary_github_filename), CONNECT_ONE_SHOT) @@ -166,16 +207,19 @@ func download_export_templates(version: String) -> void: return if !releases.releases.has(version): - print("this version is not in the index yet or does not exist. fetch first") +# print("this version is not in the index yet or does not exist. fetch first") + status_update.emit(STATUS_MESSAGES[Status.VERSION_NOT_IN_INDEX]) return if (releases.releases[version] as ReleaseMetadata).export_templates_path != "": - print("already have templates for this version") +# print("already have templates for this version") + status_update.emit(STATUS_MESSAGES[Status.TEMPLATE_ALREADY_EXISTS]) return var rm: ReleaseMetadata = releases.releases[version] var response_func = func(_result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray): if response_code == 200: + status_update.emit(STATUS_MESSAGES[Status.DOWNLOAD_DONE]) print("got it in place") print("downloading url: ", rm.export_github_filename) var zip_filename := TEMP_FOLDER.path_join("%s_export.tpz" % version) @@ -196,10 +240,11 @@ func download_export_templates(version: String) -> void: var d := DirAccess.open(RELEASES_BASE_FOLDER) d.make_dir_recursive(version.path_join("templates")) var templates_folder := RELEASES_BASE_FOLDER.path_join(version) + status_update.emit(STATUS_MESSAGES[Status.UNPACKING_TEMPLATES] % version) for file in files: var t := Thread.new() threads.append(t) - t.start(unpack_zip_file.bind(zip_filename, file, templates_folder.path_join(file), t)) + t.start(unpack_zip_file.bind(zip_filename, file, templates_folder.path_join(file), t, version)) zip.close() # don't clean tmp just yet, since there might be other threads @@ -220,12 +265,13 @@ func download_export_templates(version: String) -> void: "X-GitHub-Api-Version: 2022-11-28", ] var asset_url = API_SLUG.path_join(RELEASES_SLUG).path_join(ASSET_SLUG) % rm.export_github_asset_id + status_update.emit(STATUS_MESSAGES[Status.TEMPLATE_DOWNLOAD_IN_PROGRESS] % version) is_downloading = true http.request(asset_url, PackedStringArray(headers)) http.request_completed.connect(response_func, CONNECT_ONE_SHOT) -func unpack_zip_file(zip_file: String, from_file: String, dest_file: String, thread: Thread) -> void: +func unpack_zip_file(zip_file: String, from_file: String, dest_file: String, thread: Thread, version: String) -> void: print("extracting file ", from_file, " to: ", dest_file) var z := ZIPReader.new() var f := z.open(zip_file) @@ -239,14 +285,15 @@ func unpack_zip_file(zip_file: String, from_file: String, dest_file: String, thr file.close() z.close() - _clean_threads.call_deferred(thread) + _clean_threads.call_deferred(thread, version) -func _clean_threads(thread: Thread) -> void: +func _clean_threads(thread: Thread, version: String) -> void: threads.erase(thread) thread.wait_to_finish() if threads.is_empty(): + status_update.emit(STATUS_MESSAGES[Status.TEMPLATE_INSTALLED] % version) clean_tmp() @@ -274,6 +321,14 @@ func get_version_metadata(version: String) -> ReleaseMetadata: return releases.releases.get(version) +func get_installed_versions() -> Array[ReleaseMetadata]: + var res: Array[ReleaseMetadata] = [] + for version in releases.releases: + if is_version_installed(version): + res.append(releases.releases[version]) + return res + + func clean_tmp() -> void: var d := DirAccess.open(TEMP_FOLDER) if !d: diff --git a/UI/Components/ReleaseItem.gd b/UI/Components/ReleaseItem.gd index 58946fb..e37439a 100644 --- a/UI/Components/ReleaseItem.gd +++ b/UI/Components/ReleaseItem.gd @@ -7,18 +7,18 @@ class_name ReleaseItem var version: String -signal install_button_pressed -signal install_templates_button_pressed +signal install_button_pressed(version) +signal install_templates_button_pressed(version) func _ready() -> void: - install_button.pressed.connect(func(): install_button_pressed.emit()) - install_templates_button.pressed.connect(func(): install_templates_button_pressed.emit()) + install_button.pressed.connect(func(): install_button_pressed.emit(version)) + install_templates_button.pressed.connect(func(): install_templates_button_pressed.emit(version)) func set_version(v: String) -> void: version = v - version_label.text = v + version_label.text = "Godot v%s" % v func set_install_button_disabled(d: bool) -> void: @@ -27,3 +27,21 @@ func set_install_button_disabled(d: bool) -> void: func set_templates_button_disabled(d: bool) -> void: install_templates_button.disabled = d + + +func _on_version_downloaded(p_version: String) -> void: + if p_version != version: + # this isn't us + return + + set_install_button_disabled(true) + ReleasesManager.version_downloaded.disconnect(_on_version_downloaded) + + +func _on_export_templates_downloaded(p_version: String) -> void: + if p_version != version: + # this isn't us + return + + set_templates_button_disabled(true) + ReleasesManager.export_templates_downloaded.disconnect(_on_export_templates_downloaded) diff --git a/UI/Main/ReleasesView.gd b/UI/Main/ReleasesView.gd index 8b7037c..ee04fed 100644 --- a/UI/Main/ReleasesView.gd +++ b/UI/Main/ReleasesView.gd @@ -1,3 +1,43 @@ extends VBoxContainer const ITEM_SCENE := preload("res://UI/Components/ReleaseItem.tscn") + +@onready var release_items_container: VBoxContainer = %ReleaseItemsContainer +@onready var fetch_button: Button = %FetchButton +@onready var status_label: Label = %StatusLabel + + +func _ready() -> void: + ReleasesManager.releases_fetched.connect(releases_updated) + ReleasesManager.releases_fetched.connect(func(_x): + await get_tree().process_frame + fetch_button.disabled = false + ) + fetch_button.pressed.connect(func(): + ReleasesManager.fetch_releases() + fetch_button.disabled = true + ) + ReleasesManager.fetch_releases(true) + + ReleasesManager.status_update.connect(status_label.set_text) + + +func releases_updated(releases: ReleasesManager.Releases) -> void: + clear_releases() + for version in releases.releases: + var item: ReleaseItem = ITEM_SCENE.instantiate() + release_items_container.add_child(item) + item.set_version(version) + item.set_install_button_disabled(ReleasesManager.is_version_installed(version)) + item.set_templates_button_disabled(ReleasesManager.is_version_templates_installed(version)) + + item.install_button_pressed.connect(ReleasesManager.download_release) + item.install_templates_button_pressed.connect(ReleasesManager.download_release) + + ReleasesManager.version_downloaded.connect(item._on_version_downloaded) + ReleasesManager.export_templates_downloaded.connect(item._on_export_templates_downloaded) + + +func clear_releases() -> void: + for i in release_items_container.get_children(): + i.queue_free() diff --git a/UI/Main/ReleasesView.tscn b/UI/Main/ReleasesView.tscn index 8b0762e..d721097 100644 --- a/UI/Main/ReleasesView.tscn +++ b/UI/Main/ReleasesView.tscn @@ -46,6 +46,5 @@ alignment = 2 [node name="StatusLabel" type="Label" parent="HBoxContainer/HBoxContainer"] unique_name_in_owner = true -visible = false layout_mode = 2 -text = "Downloading v..." +text = "Idle"