-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: import functionality of plugin and installer code
- Loading branch information
Showing
12 changed files
with
1,198 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,23 @@ | ||
# Project specific ignores | ||
icon.jpg.import | ||
addons/plugin_updater/generated | ||
|
||
# Godot 4+ specific ignores | ||
.godot/ | ||
|
||
# Godot specific ignores | ||
.import/ | ||
export.cfg | ||
export_presets.cfg | ||
|
||
# gdUnit4 specific ignores | ||
reports/ | ||
addons/gdUnit4/tmp-update/ | ||
|
||
# Imported translations (automatically generated from CSV files) | ||
*.translation | ||
|
||
# Mono-specific ignores | ||
.mono/ | ||
data_*/ | ||
mono_crash.*.json |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
@tool | ||
extends Window | ||
|
||
## Updater heaviliy inspired by GDUnit4's updater, but decoupled completely from GDUnit. Also did not | ||
## include all the patching that included since it seemed to complicated to include for most projects. | ||
|
||
#TODO: read this from somewhere | ||
var config = UpdaterConfig.get_user_config() | ||
|
||
var spinner_icon = "res://addons/%s/updater2/spinner.tres" % config.plugin_name | ||
|
||
# Using this style of import avoids polluting user's namespaces | ||
const UpdaterConfig = preload("updater_config.gd") | ||
const HttpClient = preload("updater_http_client.gd") | ||
const MarkDownReader = preload("updater_markdown_reader.gd") | ||
|
||
const TEMP_FILE_NAME = "user://temp.zip" | ||
|
||
@onready var _md_reader: MarkDownReader = MarkDownReader.new() | ||
@onready var _http_client: HttpClient = $HttpClient | ||
@onready var _header: Label = $Panel/GridContainer/PanelContainer/header | ||
@onready var _content: RichTextLabel = $Panel/GridContainer/PanelContainer2/ScrollContainer/MarginContainer/content | ||
@onready var _update_button: Button = $Panel/GridContainer/Panel/HBoxContainer/update | ||
|
||
var _download_zip_url: String | ||
|
||
func _ready(): | ||
hide() | ||
_http_client.github_repo = config.github_repo | ||
|
||
var plugin :EditorPlugin = Engine.get_meta(config.editor_plugin_meta) | ||
|
||
# wait a bit to allow the editor to initialize itself | ||
await Engine.get_main_loop().create_timer(float(config.secs_before_check_for_update)).timeout | ||
|
||
_check_for_updater() | ||
|
||
func _check_for_updater(): | ||
var response = await _http_client.request_latest_version() | ||
if response.code() != 200: | ||
push_warning("Update information cannot be retrieved from GitHub! \n %s" % response.response()) | ||
return | ||
var latest_version := extract_latest_version(response) | ||
var current_version := extract_current_version() | ||
|
||
# if same version exit here no update need | ||
if latest_version.is_greater(current_version): | ||
_download_zip_url = extract_zip_url(response) | ||
_header.text = "Current version '%s'. A new version '%s' is available" % [current_version, latest_version] | ||
await show_update() | ||
|
||
func show_update() -> void: | ||
message_h4("\n\n\nRequest release infos ... [img=24x24]%s[/img]" % spinner_icon, Color.SNOW) | ||
popup_centered_ratio(.5) | ||
prints("Scanning for %s Update ..." % config.plugin_name) | ||
var content :String | ||
|
||
var response: HttpClient.HttpResponse = await _http_client.request_releases() | ||
if response.code() == 200: | ||
content = await extract_releases(response, extract_current_version()) | ||
else: | ||
message_h4("\n\n\nError checked request available releases!", Color.RED) | ||
return | ||
|
||
# finally force rescan to import images as textures | ||
if Engine.is_editor_hint(): | ||
await rescan() | ||
message(content, Color.DODGER_BLUE) | ||
_update_button.set_disabled(false) | ||
|
||
func rescan() -> void: | ||
if Engine.is_editor_hint(): | ||
if OS.is_stdout_verbose(): | ||
prints(".. reimport release resources") | ||
var fs := EditorInterface.get_resource_filesystem() | ||
fs.scan() | ||
while fs.is_scanning(): | ||
if OS.is_stdout_verbose(): | ||
progress_bar(fs.get_scanning_progress() * 100 as int) | ||
await Engine.get_main_loop().process_frame | ||
await Engine.get_main_loop().process_frame | ||
await Engine.get_main_loop().create_timer(1).timeout | ||
|
||
func extract_current_version() -> UpdaterSemVer: | ||
var config_file = ConfigFile.new() | ||
config_file.load('addons/%s/plugin.cfg' % config.plugin_name) | ||
return UpdaterSemVer.parse(config_file.get_value('plugin', 'version')) | ||
|
||
static func extract_latest_version(response: HttpClient.HttpResponse) -> UpdaterSemVer: | ||
var body :Array = response.response() | ||
return UpdaterSemVer.parse(body[0]["name"]) | ||
|
||
static func extract_zip_url(response: HttpClient.HttpResponse) -> String: | ||
var body :Array = response.response() | ||
return body[0]["zipball_url"] | ||
|
||
func extract_releases(response: HttpClient.HttpResponse, current_version) -> String: | ||
await get_tree().process_frame | ||
var result := "" | ||
for release in response.response(): | ||
if UpdaterSemVer.parse(release["tag_name"]).equals(current_version): | ||
break | ||
var release_description :String = release["body"] | ||
result += await _md_reader.to_bbcode(release_description) | ||
return result | ||
|
||
func message_h4(message :String, color :Color, clear := true) -> void: | ||
if clear: | ||
_content.clear() | ||
_content.append_text("[font_size=16]%s[/font_size]" % _colored(message, color)) | ||
|
||
func message(message :String, color :Color) -> void: | ||
_content.clear() | ||
_content.append_text(_colored(message, color)) | ||
|
||
func progress_bar(p_progress :int, p_color :Color = Color.POWDER_BLUE): | ||
if p_progress < 0: | ||
p_progress = 0 | ||
if p_progress > 100: | ||
p_progress = 100 | ||
printraw("scan [%-50s] %-3d%%\r" % ["".lpad(int(p_progress/2.0), "#").rpad(50, "-"), p_progress]) | ||
|
||
func _colored(message :String, color :Color) -> String: | ||
return "[color=#%s]%s[/color]" % [color.to_html(), message] | ||
|
||
func _on_disable_updates_toggled(toggled_on): | ||
# TODO: Store a setting somewhere | ||
pass | ||
|
||
func _on_update_pressed(): | ||
hide() | ||
_update_button.set_disabled(true) | ||
|
||
#TODO: How do I give the plugins a hook to perform actions before updating? | ||
|
||
#TODO: Perform the update, maybe use the simpler approach from the dialog plugin | ||
var updater_http_request = HTTPRequest.new() | ||
updater_http_request.accept_gzip = true | ||
add_child(updater_http_request) | ||
|
||
updater_http_request.request_completed.connect(_on_http_request_request_completed) | ||
updater_http_request.request(_download_zip_url) | ||
|
||
func _on_http_request_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void: | ||
if result != HTTPRequest.RESULT_SUCCESS: | ||
message_h4("\n\n\nError downloading update!", Color.RED) | ||
return | ||
|
||
# Save the downloaded zip | ||
var zip_file: FileAccess = FileAccess.open(TEMP_FILE_NAME, FileAccess.WRITE) | ||
zip_file.store_buffer(body) | ||
zip_file.close() | ||
|
||
OS.move_to_trash(ProjectSettings.globalize_path("res://addons/%s" % config.plugin_name)) | ||
|
||
var zip_reader: ZIPReader = ZIPReader.new() | ||
zip_reader.open(TEMP_FILE_NAME) | ||
var files: PackedStringArray = zip_reader.get_files() | ||
|
||
var base_path = files[1] | ||
# Remove archive folder | ||
files.remove_at(0) | ||
# Remove assets folder | ||
files.remove_at(0) | ||
|
||
for path in files: | ||
var new_file_path: String = path.replace(base_path, "") | ||
if path.ends_with("/"): | ||
DirAccess.make_dir_recursive_absolute("res://addons/%s" % new_file_path) | ||
else: | ||
var file: FileAccess = FileAccess.open("res://addons/%s" % new_file_path, FileAccess.WRITE) | ||
file.store_buffer(zip_reader.read_file(path)) | ||
|
||
zip_reader.close() | ||
DirAccess.remove_absolute(TEMP_FILE_NAME) | ||
|
||
#TODO: Show that we successfully updated | ||
|
||
func _on_close_pressed(): | ||
hide() | ||
|
||
func _on_content_meta_clicked(meta :String): | ||
var properties = str_to_var(meta) | ||
if properties.has("url"): | ||
OS.shell_open(properties.get("url")) | ||
|
||
func _on_content_meta_hover_started(meta :String): | ||
var properties = str_to_var(meta) | ||
if properties.has("tool_tip"): | ||
_content.set_tooltip_text(properties.get("tool_tip")) | ||
|
||
func _on_content_meta_hover_ended(meta): | ||
_content.set_tooltip_text("") |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
[gd_resource type="AnimatedTexture" load_steps=9 format=3 uid="uid://dljx0fmjlrbuh"] | ||
|
||
[ext_resource type="Texture2D" uid="uid://ddxpytkht0m5p" path="res://addons/gdUnit4/src/ui/assets/spinner/Progress1.svg" id="1_ctgew"] | ||
[ext_resource type="Texture2D" uid="uid://dowca7ike2thl" path="res://addons/gdUnit4/src/ui/assets/spinner/Progress2.svg" id="2_xwye8"] | ||
[ext_resource type="Texture2D" uid="uid://cwh8md6qipmdw" path="res://addons/gdUnit4/src/ui/assets/spinner/Progress3.svg" id="3_53vx7"] | ||
[ext_resource type="Texture2D" uid="uid://dm0jpqdjetv2c" path="res://addons/gdUnit4/src/ui/assets/spinner/Progress4.svg" id="4_eecnj"] | ||
[ext_resource type="Texture2D" uid="uid://bkj6kjyjyi7cd" path="res://addons/gdUnit4/src/ui/assets/spinner/Progress5.svg" id="5_auay0"] | ||
[ext_resource type="Texture2D" uid="uid://bsljbs1aiyels" path="res://addons/gdUnit4/src/ui/assets/spinner/Progress6.svg" id="6_fx28b"] | ||
[ext_resource type="Texture2D" uid="uid://cct6crbhix7u8" path="res://addons/gdUnit4/src/ui/assets/spinner/Progress7.svg" id="7_giugp"] | ||
[ext_resource type="Texture2D" uid="uid://dqc521iq12a7l" path="res://addons/gdUnit4/src/ui/assets/spinner/Progress8.svg" id="8_yppa0"] | ||
|
||
[resource] | ||
frames = 8 | ||
speed_scale = 2.5 | ||
frame_0/texture = ExtResource("1_ctgew") | ||
frame_0/duration = 0.2 | ||
frame_1/texture = ExtResource("2_xwye8") | ||
frame_1/duration = 0.2 | ||
frame_2/texture = ExtResource("3_53vx7") | ||
frame_2/duration = 0.2 | ||
frame_3/texture = ExtResource("4_eecnj") | ||
frame_3/duration = 0.2 | ||
frame_4/texture = ExtResource("5_auay0") | ||
frame_4/duration = 0.2 | ||
frame_5/texture = ExtResource("6_fx28b") | ||
frame_5/duration = 0.2 | ||
frame_6/texture = ExtResource("7_giugp") | ||
frame_6/duration = 0.2 | ||
frame_7/texture = ExtResource("8_yppa0") | ||
frame_7/duration = 0.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
extends RefCounted | ||
|
||
## TODO: Placeholder config | ||
|
||
static func get_user_config() -> Dictionary: | ||
var user_config: Dictionary = { | ||
plugin_name = "plugin_updater", | ||
secs_before_check_for_update = 5, | ||
github_repo = "myyk/godot-playlists", | ||
editor_plugin_meta = "PlaylistsEditorPlugin", #TODO: try to eliminate this one | ||
} | ||
|
||
#if FileAccess.file_exists(DialogueConstants.USER_CONFIG_PATH): | ||
#var file: FileAccess = FileAccess.open(DialogueConstants.USER_CONFIG_PATH, FileAccess.READ) | ||
#user_config.merge(JSON.parse_string(file.get_as_text()), true) | ||
|
||
return user_config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
@tool | ||
extends Node | ||
|
||
## Credits: Mostly copied from https://github.com/MikeSchulze/gdUnit4/blob/99b7c323f443e5fcc67f9a79b4df532727e8986f/addons/gdUnit4/src/update/GdUnitUpdateClient.gd | ||
|
||
signal request_completed(response) | ||
|
||
@export var github_repo = "" | ||
|
||
class HttpResponse: | ||
var _code :int | ||
var _body :PackedByteArray | ||
|
||
|
||
func _init(code_ :int, body_ :PackedByteArray): | ||
_code = code_ | ||
_body = body_ | ||
|
||
func code() -> int: | ||
return _code | ||
|
||
func response() -> Variant: | ||
var test_json_conv := JSON.new() | ||
test_json_conv.parse(_body.get_string_from_utf8()) | ||
return test_json_conv.get_data() | ||
|
||
func body() -> PackedByteArray: | ||
return _body | ||
|
||
var _http_request :HTTPRequest = HTTPRequest.new() | ||
|
||
func _ready(): | ||
add_child(_http_request) | ||
_http_request.connect("request_completed", Callable(self, "_on_request_completed")) | ||
|
||
|
||
func _notification(what): | ||
if what == NOTIFICATION_PREDELETE: | ||
if is_instance_valid(_http_request): | ||
_http_request.queue_free() | ||
|
||
|
||
func request_latest_version() -> HttpResponse: | ||
var error = _http_request.request("https://api.github.com/repos/%s/tags" % github_repo) | ||
if error != OK: | ||
var message = "request_latest_version failed: %d" % error | ||
return HttpResponse.new(error, message.to_utf8_buffer()) | ||
return await self.request_completed | ||
|
||
|
||
func request_releases() -> HttpResponse: | ||
var error = _http_request.request("https://api.github.com/repos/%s/releases" % github_repo) | ||
if error != OK: | ||
var message = "request_releases failed: %d" % error | ||
return HttpResponse.new(error, message.to_utf8_buffer()) | ||
return await self.request_completed | ||
|
||
|
||
func request_image(url :String) -> HttpResponse: | ||
var error = _http_request.request(url) | ||
if error != OK: | ||
var message = "request_image failed: %d" % error | ||
return HttpResponse.new(error, message.to_utf8_buffer()) | ||
return await self.request_completed | ||
|
||
|
||
func request_zip_package(url :String, file :String) -> HttpResponse: | ||
_http_request.set_download_file(file) | ||
var error = _http_request.request(url) | ||
if error != OK: | ||
var message = "request_zip_package failed: %d" % error | ||
return HttpResponse.new(error, message.to_utf8_buffer()) | ||
return await self.request_completed | ||
|
||
|
||
func _on_request_completed(_result :int, response_code :int, _headers :PackedStringArray, body :PackedByteArray): | ||
if _http_request.get_http_client_status() != HTTPClient.STATUS_DISCONNECTED: | ||
_http_request.set_download_file("") | ||
request_completed.emit(HttpResponse.new(response_code, body)) |
Oops, something went wrong.