Files
Void-Bastion/update_manager.gd

95 lines
3.2 KiB
GDScript

class_name UpdateManager
extends Node
signal update_available(info: Dictionary)
const RELEASES_API := "https://git.dominicspringer.de/api/v1/repos/Emporex/Void-Bastion-Releases/releases"
const PUBLIC_RELEASE_BASE := "https://git.dominicspringer.de/Emporex/Void-Bastion-Releases/releases/tag/"
var _request: HTTPRequest
func _ready() -> void:
if DisplayServer.get_name() == "headless" or Engine.is_editor_hint():
return
_request = HTTPRequest.new()
add_child(_request)
_request.request_completed.connect(_on_request_completed)
call_deferred("check_for_update")
func check_for_update() -> void:
if _request == null or _request.get_http_client_status() != HTTPClient.STATUS_DISCONNECTED:
return
_request.request(RELEASES_API, ["Accept: application/json", "User-Agent: Void-Bastion/%s" % _current_version()])
func _on_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
if result != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300:
return
var releases = JSON.parse_string(body.get_string_from_utf8())
if not releases is Array:
return
for release in releases:
if not release is Dictionary or bool(release.get("draft", false)):
continue
var version := str(release.get("tag_name", "")).trim_prefix("v")
if version.is_empty() or not _is_newer_version(version, _current_version()):
return
var download_url := PUBLIC_RELEASE_BASE + "v" + version
var wanted_extension := ".apk" if OS.get_name() == "Android" else ".exe"
for asset in release.get("assets", []):
if asset is Dictionary and str(asset.get("name", "")).to_lower().ends_with(wanted_extension):
download_url = str(asset.get("browser_download_url", download_url))
break
update_available.emit({
"version": version,
"name": str(release.get("name", version)),
"url": download_url
})
return
func _current_version() -> String:
return str(ProjectSettings.get_setting("application/config/version", "0.0.0"))
func _is_newer_version(remote: String, local: String) -> bool:
var remote_parts := _version_parts(remote)
var local_parts := _version_parts(local)
for index in 3:
if remote_parts[index] != local_parts[index]:
return remote_parts[index] > local_parts[index]
var remote_suffix: String = remote_parts[3]
var local_suffix: String = local_parts[3]
if remote_suffix.is_empty():
return not local_suffix.is_empty()
if local_suffix.is_empty():
return false
return _prerelease_weight(remote_suffix) > _prerelease_weight(local_suffix)
func _version_parts(value: String) -> Array:
var normalized := value.trim_prefix("v").strip_edges().to_lower()
var sections := normalized.split("-", true, 1)
var numbers := sections[0].split(".")
return [
int(numbers[0]) if numbers.size() > 0 else 0,
int(numbers[1]) if numbers.size() > 1 else 0,
int(numbers[2]) if numbers.size() > 2 else 0,
sections[1] if sections.size() > 1 else ""
]
func _prerelease_weight(value: String) -> int:
var stage := 0
if value.begins_with("beta"):
stage = 100000
elif value.begins_with("rc"):
stage = 200000
var sequence := 0
for part in value.split("."):
if part.is_valid_int():
sequence = int(part)
return stage + sequence