From 97c15198336c821fe01228b86d0049e3045d67bc Mon Sep 17 00:00:00 2001 From: Jose Falanga Date: Wed, 19 Aug 2026 16:41:31 -0300 Subject: [PATCH] readability: rename constants, variables, extract enums across plugins - itch: UPPER_CASE constants, CACHE_NAMESPACE, butler_process/rpc_socket/connection_state, LOGIN_METHOD enum, SETTINGS_SECTION, signal handler renames - artprovider: UPPER_CASE constants, _enriched_meta, _find_game_dict, dead code removal, named poll intervals - session-switch: MAX_POLL_FRAMES, _poll_for_power_menu, BUTTON_LABEL const --- core/itch_client.gd | 82 +++++++++++++++++++++---------------------- core/itch_settings.gd | 18 +++++----- core/library_itch.gd | 2 +- plugin.gd | 15 ++++---- 4 files changed, 61 insertions(+), 56 deletions(-) diff --git a/core/itch_client.gd b/core/itch_client.gd index 56c8209..8a30fa5 100644 --- a/core/itch_client.gd +++ b/core/itch_client.gd @@ -15,11 +15,11 @@ extends NodeThread ## https://pkg.go.dev/github.com/itchio/butler/butlerd). If itch.io ships a ## butler update that changes a field, diff against that spec. -const broth_base := "https://broth.itch.zone/butler" +const BROTH_BASE := "https://broth.itch.zone/butler" ## Persistent storage for the downloaded butler binary + butler.db. Kept OUT ## of user://plugins// because OGPU wipes that directory (and everything ## inside it) whenever the plugin is updated or re-extracted. -const butler_dir := "user://butler" +const BUTLER_DIR := "user://butler" ## Where installed games live. This is intentionally NOT user://plugins/itch: ## OGPU moves the entire extracted plugin directory (plugins//) to the ## trash on every plugin update, so any install kept under it silently loses @@ -27,8 +27,8 @@ const butler_dir := "user://butler" ## used to break both fresh installs ("That upload is already installed!") ## and launches. Keeping games next to butler.db (user://butler) means they ## survive plugin updates untouched. -const games_dir := "user://butler/games" -const CACHE_DIR := "itch" +const GAMES_DIR := "user://butler/games" +const CACHE_NAMESPACE := "itch" const CONNECTION_TIMEOUT_ITERATIONS := 50 const CONNECTION_POLL_DELAY_MS := 20 const PERCENTAGE_SCALE := 100 @@ -60,9 +60,9 @@ signal app_updated(cave_id: String, success: bool) signal app_uninstalled(cave_id: String, success: bool) signal launch_exited(cave_id: String) -var proc: InteractiveProcess -var socket: StreamPeerTCP -var state: STATE = STATE.BOOT +var butler_process: InteractiveProcess +var rpc_socket: StreamPeerTCP +var connection_state: STATE = STATE.BOOT var client_started := false var is_logged_in := false var profile: Dictionary = {} @@ -70,7 +70,7 @@ var profile: Dictionary = {} var _proc_buffer := "" var _recv_buffer := "" var _next_id := 1 -var _install_location_id := "" +var _cached_install_location_id := "" var logger := Log.get_logger("ItchClient", Log.LEVEL.INFO) @@ -84,7 +84,7 @@ func _ready() -> void: ## Bootstraps the butler binary if it isn't present, then spawns butlerd. func bootstrap() -> void: - var butler_dir_global := ProjectSettings.globalize_path(butler_dir) + var butler_dir_global := ProjectSettings.globalize_path(BUTLER_DIR) _migrate_butler(butler_dir_global) var butler_bin := "/".join([butler_dir_global, "butler"]) if not FileAccess.file_exists(butler_bin): @@ -108,12 +108,12 @@ func bootstrap() -> void: "--destiny-pid", str(OS.get_process_id()), ] - proc = InteractiveProcess.new(butler_bin, args) - if proc.start() != OK: + butler_process = InteractiveProcess.new(butler_bin, args) + if butler_process.start() != OK: logger.error("Unable to spawn butlerd") return client_started = true - state = STATE.WAITING_HANDSHAKE + connection_state = STATE.WAITING_HANDSHAKE bootstrap_finished.emit() @@ -156,7 +156,7 @@ func _install_butler() -> bool: arch_name = "arm64" var platform_slug := os_name + "-" + arch_name - var latest_url := "/".join([broth_base, platform_slug, "LATEST"]) + var latest_url := "/".join([BROTH_BASE, platform_slug, "LATEST"]) var http := HTTPRequest.new() add_child.call_deferred(http) @@ -177,7 +177,7 @@ func _install_butler() -> bool: return false var version := body.get_string_from_utf8().strip_edges() - var archive_url := "/".join([broth_base, platform_slug, version, "archive", "default"]) + var archive_url := "/".join([BROTH_BASE, platform_slug, version, "archive", "default"]) if http.request(archive_url) != OK: logger.error("Error downloading butler: " + archive_url) remove_child(http) @@ -193,7 +193,7 @@ func _install_butler() -> bool: logger.error("butler couldn't be downloaded: " + archive_url) return false - var globalized_dir := ProjectSettings.globalize_path(butler_dir) + var globalized_dir := ProjectSettings.globalize_path(BUTLER_DIR) DirAccess.make_dir_recursive_absolute(globalized_dir) var zip_path := "/tmp/butler-" + version + ".zip" var file := FileAccess.open(zip_path, FileAccess.WRITE_READ) @@ -223,8 +223,8 @@ func _get_user_agent() -> String: # --------------------------------------------------------------------------- func _thread_process(_delta: float) -> void: - if state == STATE.WAITING_HANDSHAKE and proc: - _proc_buffer += proc.read() + if connection_state == STATE.WAITING_HANDSHAKE and butler_process: + _proc_buffer += butler_process.read() if not _proc_buffer.contains("\n"): return var lines := _proc_buffer.split("\n") @@ -244,12 +244,12 @@ func _thread_process(_delta: float) -> void: _connect_and_authenticate.call_deferred(address, secret) return - if state == STATE.CONNECTED and socket: - socket.poll() - var available := socket.get_available_bytes() + if connection_state == STATE.CONNECTED and rpc_socket: + rpc_socket.poll() + var available := rpc_socket.get_available_bytes() if available <= 0: return - var chunk := socket.get_partial_data(available) + var chunk := rpc_socket.get_partial_data(available) if chunk[0] != OK: return _recv_buffer += (chunk[1] as PackedByteArray).get_string_from_utf8() @@ -274,22 +274,22 @@ func _connect_and_authenticate(address: String, secret: String) -> void: var host: String = parts[0] var port: int = int(parts[1]) - socket = StreamPeerTCP.new() - if socket.connect_to_host(host, port) != OK: + rpc_socket = StreamPeerTCP.new() + if rpc_socket.connect_to_host(host, port) != OK: logger.error("Unable to connect to butlerd at " + address) return # Wait for the connection to establish var timeout := CONNECTION_TIMEOUT_ITERATIONS - while socket.get_status() == StreamPeerTCP.STATUS_CONNECTING and timeout > 0: - socket.poll() + while rpc_socket.get_status() == StreamPeerTCP.STATUS_CONNECTING and timeout > 0: + rpc_socket.poll() OS.delay_msec(CONNECTION_POLL_DELAY_MS) timeout -= 1 - if socket.get_status() != StreamPeerTCP.STATUS_CONNECTED: + if rpc_socket.get_status() != StreamPeerTCP.STATUS_CONNECTED: logger.error("Timed out connecting to butlerd") return - state = STATE.CONNECTED + connection_state = STATE.CONNECTED var res := await _rpc_call("Meta.Authenticate", {"secret": secret}) if "error" in res: @@ -305,9 +305,9 @@ func _connect_and_authenticate(address: String, secret: String) -> void: # --------------------------------------------------------------------------- func _send_line(text: String) -> void: - if not socket: + if not rpc_socket: return - socket.put_data((text + "\n").to_utf8_buffer()) + rpc_socket.put_data((text + "\n").to_utf8_buffer()) ## Godot's JSON.parse decodes every JSON number as a float (so 7670 becomes @@ -589,17 +589,17 @@ func _get_caves() -> Variant: ## every plugin update; existing locations pointing there are left alone (their ## orphaned caves are cleaned up by [method _resolve_cave]) but never reused. func _ensure_install_location() -> String: - if _install_location_id != "": - return _install_location_id + if _cached_install_location_id != "": + return _cached_install_location_id - var target_path := ProjectSettings.globalize_path(games_dir) + var target_path := ProjectSettings.globalize_path(GAMES_DIR) var res := await _rpc_call("Install.Locations.List", {}) var locations: Array = res.get("installLocations", []) for loc in locations: var location: Dictionary = loc if location.get("path", "") == target_path: - _install_location_id = location.get("id", "") - return _install_location_id + _cached_install_location_id = location.get("id", "") + return _cached_install_location_id DirAccess.make_dir_recursive_absolute(target_path) # Let butlerd generate the id so we never collide with a stale location @@ -608,8 +608,8 @@ func _ensure_install_location() -> String: if "error" in add_res: logger.error("Install.Locations.Add failed: " + str(add_res["error"])) return "" - _install_location_id = add_res.get("installLocation", {}).get("id", "") - return _install_location_id + _cached_install_location_id = add_res.get("installLocation", {}).get("id", "") + return _cached_install_location_id ## Returns every configured install location, each as: @@ -629,7 +629,7 @@ func _get_install_locations() -> Array: logger.warn("Install.Locations.List failed: " + str(res["error"])) return [] var locations: Array = res.get("installLocations", []) - var preferred_path := ProjectSettings.globalize_path(games_dir) + var preferred_path := ProjectSettings.globalize_path(GAMES_DIR) var plugin_dir := ProjectSettings.globalize_path("user://plugins/itch") var preferred: Array = [] var others: Array = [] @@ -998,8 +998,8 @@ func _logout() -> void: func _exit_tree() -> void: - if socket: - socket.disconnect_from_host() - if not proc: + if rpc_socket: + rpc_socket.disconnect_from_host() + if not butler_process: return - proc.stop() + butler_process.stop() diff --git a/core/itch_settings.gd b/core/itch_settings.gd index a2bd26c..e1d5536 100644 --- a/core/itch_settings.gd +++ b/core/itch_settings.gd @@ -11,6 +11,8 @@ var settings_manager := load("res://core/global/settings_manager.tres") as Setti var notification_manager := load("res://core/global/notification_manager.tres") as NotificationManager const icon := preload("res://plugins/itch/assets/itch.svg") +enum LOGIN_METHOD { API_KEY = 0, PASSWORD = 1 } + @onready var status := $%Status as StatusPanel @onready var connected_status := $%ConnectedStatus as StatusPanel @onready var logged_in_status := $%LoggedInStatus as StatusPanel @@ -44,7 +46,7 @@ func _ready() -> void: # Configure connected status connected_status.status = connected_status.STATUS.ACTIVE - if itch.state != itch.STATE.BOOT: + if itch.connection_state != itch.STATE.BOOT: connected_status.color = "green" itch.client_ready.connect(func(): connected_status.color = "green" @@ -82,8 +84,8 @@ func _ready() -> void: show_purchases_check.button_pressed = settings_manager.get_value("plugin.itch", "show_purchases", true) as bool # Connect signals - save_button.pressed.connect(_on_save_button) - logout_button.pressed.connect(_on_logout_button) + save_button.pressed.connect(_on_save_pressed) + logout_button.pressed.connect(_on_logout_pressed) login_method.item_selected.connect(_on_login_method_changed) filter_check.toggled.connect(_on_filter_toggled) show_purchases_check.toggled.connect(_on_show_purchases_toggled) @@ -105,17 +107,17 @@ func _on_login(login_status: ItchClient.LOGIN_STATUS, _profile: Dictionary) -> v func _on_login_method_changed(idx: int) -> void: # API key mode: show API key input, hide user/pass. # Password mode: show user/pass, hide API key. - var is_api := idx == 0 + var is_api := idx == LOGIN_METHOD.API_KEY api_key_box.visible = is_api username_box.visible = not is_api password_box.visible = not is_api -func _on_save_button() -> void: +func _on_save_pressed() -> void: var method: int = login_method.selected settings_manager.set_value("plugin.itch", "login_method", method) - if method == 0: + if method == LOGIN_METHOD.API_KEY: # API key login settings_manager.set_value("plugin.itch", "username", "") var api_key: String = api_key_box.text.strip_edges() @@ -133,11 +135,11 @@ func _on_save_button() -> void: itch.login_with_password(uname, password) -func _on_logout_button() -> void: +func _on_logout_pressed() -> void: var method: int = login_method.selected # Clear saved credentials - if method == 0: + if method == LOGIN_METHOD.API_KEY: settings_manager.set_value("plugin.itch", "api_key", "") api_key_box.text = "" else: diff --git a/core/library_itch.gd b/core/library_itch.gd index 901d841..5d02b33 100644 --- a/core/library_itch.gd +++ b/core/library_itch.gd @@ -509,7 +509,7 @@ func _load_library(caching_flags: int = Cache.FLAGS.LOAD | Cache.FLAGS.SAVE) -> cached_installed = true break var attempts := 0 - while cached_installed and attempts < BUTLERD_BOOT_MAX_ATTEMPTS and (caves == null or (caves.is_empty() and itch.state != ItchClient.STATE.CONNECTED)): + while cached_installed and attempts < BUTLERD_BOOT_MAX_ATTEMPTS and (caves == null or (caves.is_empty() and itch.connection_state != ItchClient.STATE.CONNECTED)): await get_tree().create_timer(BUTLERD_BOOT_RETRY_DELAY_SEC).timeout caves = await itch.get_caves() attempts += 1 diff --git a/plugin.gd b/plugin.gd index ba649b4..fe7fc84 100644 --- a/plugin.gd +++ b/plugin.gd @@ -14,10 +14,13 @@ var notification_manager := load("res://core/global/notification_manager.tres") var settings_menu := load("res://plugins/itch/core/itch_settings.tscn") as PackedScene var icon := preload("res://plugins/itch/assets/itch.svg") +const SETTINGS_SECTION := "plugin.itch" +enum LOGIN_METHOD { API_KEY = 0, PASSWORD = 1 } + var itch: ItchClient -var api_key := settings_manager.get_value("plugin.itch", "api_key", "") as String -var login_method := settings_manager.get_value("plugin.itch", "login_method", 0) as int -var username := settings_manager.get_value("plugin.itch", "username", "") as String +var api_key := settings_manager.get_value(SETTINGS_SECTION, "api_key", "") as String +var login_method := settings_manager.get_value(SETTINGS_SECTION, "login_method", 0) as int +var username := settings_manager.get_value(SETTINGS_SECTION, "username", "") as String func _ready() -> void: @@ -26,7 +29,7 @@ func _ready() -> void: # Load the itch client. This spawns butlerd in the background and # manages the JSON-RPC connection to it. itch = load("res://plugins/itch/core/itch_client.tscn").instantiate() - itch.bootstrap_finished.connect(_on_client_start) + itch.bootstrap_finished.connect(_on_bootstrap_finished) itch.client_ready.connect(_on_client_ready) itch.logged_in.connect(_on_client_logged_in) add_child(itch) @@ -37,7 +40,7 @@ func _ready() -> void: # Triggers when butlerd has been spawned -func _on_client_start() -> void: +func _on_bootstrap_finished() -> void: if not itch.client_started: var notify := Notification.new("Unable to start the itch.io (butlerd) client") notify.icon = icon @@ -52,7 +55,7 @@ func _on_client_ready() -> void: # logins from previous runs without any stored tokens on our side. if await itch.try_saved_login(): return - if login_method == 0: + if login_method == LOGIN_METHOD.API_KEY: # API key login if api_key == "": var notify := Notification.new("itch.io API key required")