Initial commit

This commit is contained in:
Melody Becker 2025-01-26 08:36:45 +00:00
commit cf22890c16
40 changed files with 2240 additions and 0 deletions

View file

@ -0,0 +1,306 @@
@tool
extends Control
signal gd_plug_loaded(gd_plug)
signal updated()
enum PLUGIN_STATUS {
PLUGGED, UNPLUGGED, INSTALLED, CHANGED, UPDATE
}
const PLUGIN_STATUS_ICON = [
preload("../../assets/icons/add.png"), preload("../../assets/icons/import_fail.png"),
preload("../../assets/icons/import_check.png"), preload("../../assets/icons/edit_internal.png"),
preload("../../assets/icons/refresh.png")
]
@onready var tree = $Tree
@onready var init_btn = $"%InitBtn"
@onready var check_for_update_btn = $"%CheckForUpdateBtn"
@onready var update_section = $"%UpdateSection"
@onready var force_check = $"%ForceCheck"
@onready var production_check = $"%ProductionCheck"
@onready var update_btn = $"%UpdateBtn"
@onready var loading_overlay = $"%LoadingOverlay"
@onready var loading_label = $"%LoadingLabel"
var gd_plug
var project_dir
var _is_executing = false
var _check_for_update_task_id = -1
func _ready():
project_dir = DirAccess.open("res://")
load_gd_plug()
update_plugin_list(get_plugged_plugins(), get_installed_plugins())
tree.set_column_title(0, "Name")
tree.set_column_title(1, "Arguments")
tree.set_column_title(2, "Status")
connect("visibility_changed", _on_visibility_changed)
func _process(delta):
if not is_instance_valid(gd_plug):
return
if "threadpool" in gd_plug:
gd_plug.threadpool.process(delta)
if _check_for_update_task_id >= 0:
if WorkerThreadPool.is_task_completed(_check_for_update_task_id):
_check_for_update_task_id = -1
show_overlay(false)
disable_ui(false)
func _notification(what):
match what:
NOTIFICATION_PREDELETE:
if is_instance_valid(gd_plug):
gd_plug.threadpool.stop()
gd_plug.free()
NOTIFICATION_APPLICATION_FOCUS_IN:
load_gd_plug()
update_plugin_list(get_plugged_plugins(), get_installed_plugins())
func load_gd_plug():
if is_instance_valid(gd_plug):
gd_plug.free() # Free instance in order to reload script
if project_dir.file_exists("plug.gd"):
init_btn.hide()
check_for_update_btn.show()
update_section.show()
update_btn.show() # Not sure why it is always hidden
var gd_plug_script = load("plug.gd")
gd_plug_script.reload(true) # Reload gd-plug script to get updated
gd_plug = gd_plug_script.new()
gd_plug._plug_start()
gd_plug._plugging()
else:
if project_dir.file_exists("addons/gd-plug/plug.gd"):
init_btn.show()
check_for_update_btn.hide()
update_section.hide()
gd_plug = load("addons/gd-plug/plug.gd").new()
else:
print("Missing dependency: gd-plug")
if is_instance_valid(gd_plug):
emit_signal("gd_plug_loaded", gd_plug)
func update_plugin_list(plugged, installed):
var plugin_names = []
for plugin_name in plugged.keys():
plugin_names.append(plugin_name)
for plugin_name in installed.keys():
if plugin_name in plugin_names:
continue
plugin_names.append(plugin_name)
tree.clear()
tree.create_item() # root
for plugin_name in plugin_names:
var plugin_plugged = plugged.get(plugin_name, {})
var plugin_installed = installed.get(plugin_name, {})
var plugin = plugin_plugged if plugin_name in plugged else plugin_installed
var plugin_status = get_plugin_status(plugin_name)
var plugin_args = []
for plugin_arg in plugin.keys():
var value = plugin[plugin_arg]
if value != null:
if not (value is bool):
if value.is_empty():
continue
else:
continue
match plugin_arg:
"install_root":
plugin_args.append("install root: %s" % str(value))
"include":
plugin_args.append("include %s" % str(value))
"exclude":
plugin_args.append("exclude %s" % str(value))
"branch":
plugin_args.append("branch: %s" % str(value))
"tag":
plugin_args.append("tag: %s" % str(value))
"commit":
plugin_args.append(str(value).left(8))
"dev":
if value:
plugin_args.append("dev")
"on_updated":
plugin_args.append("on_updated: %s" % str(value))
var plugin_args_text = ""
for i in plugin_args.size():
var text = plugin_args[i]
plugin_args_text += text
if i < plugin_args.size() - 1:
plugin_args_text += ", "
var child = tree.create_item(tree.get_root())
child.set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT)
child.set_text_alignment(1, HORIZONTAL_ALIGNMENT_CENTER)
child.set_text_alignment(2, HORIZONTAL_ALIGNMENT_CENTER)
child.set_meta("plugin", plugin)
child.set_text(0, plugin_name)
child.set_tooltip_text(0, plugin.url)
child.set_text(1, plugin_args_text)
child.set_tooltip_text(2, PLUGIN_STATUS.keys()[plugin_status].capitalize())
child.set_icon(2, PLUGIN_STATUS_ICON[plugin_status])
func disable_ui(disabled=true):
init_btn.disabled = disabled
check_for_update_btn.disabled = disabled
update_btn.disabled = disabled
func show_overlay(show=true, text=""):
loading_overlay.visible = show
loading_label.text = text
func gd_plug_execute_threaded(name):
if not is_instance_valid(gd_plug):
return
if _is_executing:
return
_is_executing = true
disable_ui(true)
gd_plug._plug_start()
gd_plug._plugging()
gd_plug.call(name)
await gd_plug.threadpool.all_thread_finished
# Make sure to use call_deferred for thread safe function calling while waiting thread to finish
gd_plug._plug_end()
call_deferred("disable_ui", false)
_is_executing = false
clear_environment()
call_deferred("update_plugin_list", get_plugged_plugins(), get_installed_plugins())
func gd_plug_execute(name):
if not is_instance_valid(gd_plug):
return
if _is_executing:
return
_is_executing = true
disable_ui(true)
gd_plug._plug_start()
gd_plug._plugging()
gd_plug.call(name)
gd_plug._plug_end()
disable_ui(false)
_is_executing = false
clear_environment()
update_plugin_list(get_plugged_plugins(), get_installed_plugins())
func clear_environment():
OS.unset_environment("production")
OS.unset_environment("test")
OS.unset_environment("force")
func _on_visibility_changed():
if visible:
load_gd_plug()
update_plugin_list(get_plugged_plugins(), get_installed_plugins())
func _on_Init_pressed():
gd_plug_execute("_plug_init")
load_gd_plug()
func _on_CheckForUpdateBtn_pressed():
var children = tree.get_root().get_children()
if tree.get_root().get_children().size() > 0:
show_overlay(true, "Checking for Updates...")
disable_ui(true)
if _check_for_update_task_id < 0:
var task_id = WorkerThreadPool.add_task(check_for_update.bind(children[0]))
_check_for_update_task_id = (task_id)
func _on_UpdateBtn_pressed():
if force_check.button_pressed:
OS.set_environment("force", "true")
if production_check.button_pressed:
OS.set_environment("production", "true")
show_overlay(true, "Updating...")
gd_plug_execute_threaded("_plug_install")
await gd_plug.threadpool.all_thread_finished
# Make sure to use call_deferred for thread safe function calling while waiting thread to finish
call_deferred("show_overlay", false)
call_deferred("emit_signal", "updated")
func get_plugged_plugins():
return gd_plug._plugged_plugins if is_instance_valid(gd_plug) else {}
func get_installed_plugins():
return gd_plug.installation_config.get_value("plugin", "installed", {}) if is_instance_valid(gd_plug) else {}
func get_plugin_status(plugin_name):
var plugged_plugins = get_plugged_plugins()
var installed_plugins = get_installed_plugins()
var plugin_plugged = plugged_plugins.get(plugin_name, {})
var plugin_installed = installed_plugins.get(plugin_name, {})
var plugin = plugin_plugged if plugin_name in plugged_plugins else plugin_installed
var is_plugged = plugin.name in plugged_plugins
var is_installed = plugin.name in installed_plugins
var changes = gd_plug.compare_plugins(plugin_plugged, plugin_installed) if is_installed else {}
var is_changed = changes.size() > 0
var plugin_status = 0
if is_installed:
if is_plugged:
if is_changed:
plugin_status = 3
else:
plugin_status = 2
else:
plugin_status = 1
else:
plugin_status = 0
return plugin_status
func has_update(plugin):
if not is_instance_valid(gd_plug):
return false
if plugin == null:
return false
var git = gd_plug._GitExecutable.new(ProjectSettings.globalize_path(plugin.plug_dir), gd_plug.logger)
var ahead_behind = []
if git.fetch("origin " + plugin.branch if plugin.branch else "origin").exit == OK:
ahead_behind = git.get_commit_comparison("HEAD", "origin/" + plugin.branch if plugin.branch else "origin")
var is_commit_behind = !!ahead_behind[1] if ahead_behind.size() == 2 else false
if is_commit_behind:
gd_plug.logger.info("%s %d commits behind, update required" % [plugin.name, ahead_behind[1]])
return true
else:
gd_plug.logger.info("%s up to date" % plugin.name)
return false
func check_for_update(child):
var plugin = child.get_meta("plugin")
var plugin_status = get_plugin_status(plugin.name)
if plugin_status == PLUGIN_STATUS.INSTALLED:
var has_update = has_update(plugin)
if has_update:
child.set_icon(2, PLUGIN_STATUS_ICON[PLUGIN_STATUS.UPDATE])
child.set_tooltip_text(2, PLUGIN_STATUS.keys()[PLUGIN_STATUS.UPDATE].capitalize())
if is_instance_valid(child):
var next_child = child.get_next()
if next_child:
check_for_update(next_child)

View file

@ -0,0 +1,112 @@
[gd_scene load_steps=4 format=3 uid="uid://hr5ks54660xu"]
[ext_resource type="Script" path="res://addons/gd-plug-ui/scene/plugin_settings/PluginSettings.gd" id="1"]
[ext_resource type="Texture2D" path="res://addons/gd-plug-ui/assets/icons/progress/progress_animated_texture.tres" id="2_30o7o"]
[sub_resource type="StyleBoxEmpty" id="1"]
[node name="gd-plug" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1")
[node name="Tree" type="Tree" parent="."]
layout_mode = 0
anchor_right = 1.0
anchor_bottom = 0.9
columns = 3
column_titles_visible = true
hide_root = true
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 0
anchor_top = 0.9
anchor_right = 1.0
anchor_bottom = 1.0
theme_override_constants/margin_left = 16
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 16
theme_override_constants/margin_bottom = 8
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
layout_mode = 2
[node name="InitBtn" type="Button" parent="MarginContainer/HBoxContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 4
text = "Init"
[node name="CheckForUpdateBtn" type="Button" parent="MarginContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 2
size_flags_vertical = 4
text = "Check for Update"
[node name="UpdateSection" type="HBoxContainer" parent="MarginContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 10
size_flags_vertical = 6
[node name="ForceCheck" type="CheckButton" parent="MarginContainer/HBoxContainer/UpdateSection"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 10
size_flags_vertical = 4
tooltip_text = "Force installation of plugin, may overwrite project files in case of conflicts."
theme_override_styles/focus = SubResource("1")
text = "Force"
[node name="ProductionCheck" type="CheckButton" parent="MarginContainer/HBoxContainer/UpdateSection"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 10
size_flags_vertical = 4
tooltip_text = "Install plugins not marked as \"dev\", and uninstall plugins marked as \"dev\""
theme_override_styles/focus = SubResource("1")
text = "Production"
[node name="UpdateBtn" type="Button" parent="MarginContainer/HBoxContainer/UpdateSection"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 10
size_flags_vertical = 4
text = "Update"
[node name="LoadingOverlay" type="PanelContainer" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = -1
anchor_right = 1.0
anchor_bottom = 0.9
grow_horizontal = 2
grow_vertical = 2
metadata/_edit_use_anchors_ = true
[node name="CenterContainer" type="CenterContainer" parent="LoadingOverlay"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="LoadingOverlay/CenterContainer"]
layout_mode = 2
[node name="TextureRect" type="TextureRect" parent="LoadingOverlay/CenterContainer/HBoxContainer"]
layout_mode = 2
texture = ExtResource("2_30o7o")
stretch_mode = 3
[node name="LoadingLabel" type="Label" parent="LoadingOverlay/CenterContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
[connection signal="pressed" from="MarginContainer/HBoxContainer/InitBtn" to="." method="_on_Init_pressed"]
[connection signal="pressed" from="MarginContainer/HBoxContainer/CheckForUpdateBtn" to="." method="_on_CheckForUpdateBtn_pressed"]
[connection signal="pressed" from="MarginContainer/HBoxContainer/UpdateSection/UpdateBtn" to="." method="_on_UpdateBtn_pressed"]