Godot Version
4.7.2
Question
Hi everyone,
I’m starting to develop my first plugin, but I’ve encountered my first big obstacle while stress-testing the enable/disable feature of it.The plugin is structured to act as a parent connecting its sub-plugins, which have all a modular structure.
Here is the GDScript file:
@tool
extends EditorPlugin
const PLUGIN_NAME = "plug_and_dev"
var restart_dialog: ConfirmationDialog
func _enable_plugin() -> void:
E...other sub-plugins
EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/ui", true)
_show_restart_dialog()
func _disable_plugin() -> void:
...other sub-plugins...
EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/ui", false)
_show_restart_dialog()
func _show_restart_dialog() -> void:
restart_dialog = ConfirmationDialog.new()
restart_dialog.title = "Restart Required"
restart_dialog.dialog_text = "Warning: the engine needs to restart, otherwise the plugin won't work properly. \nRestart now?"
restart_dialog.ok_button_text = "Restart"
restart_dialog.cancel_button_text = "Later"
if not restart_dialog.confirmed.is_connected(func(): EditorInterface.restart_editor(true)):
restart_dialog.confirmed.connect(func(): EditorInterface.restart_editor(true))
if not restart_dialog.canceled.is_connected(func(): restart_dialog.queue_free()):
restart_dialog.canceled.connect(func(): restart_dialog.queue_free())
var parent_window = get_window()
if Engine.is_editor_hint():
parent_window = EditorInterface.get_base_control().get_window()
parent_window.add_child(restart_dialog)
restart_dialog.exclusive = false
restart_dialog.popup_centered.call_deferred()
The main problem I’m having is with the “input” module.
Following, the GDScript file for the sub-plugin:
@tool
extends EditorPlugin
const BACKUP_FILE_PATH := "plugin_input_backup.cfg"
const INPUT_CONFIG_LIST := [
{
"name": "pad_ui_select",
"kbm": [KEY_ENTER],
"gamepad": [JOY_BUTTON_A],
"axis": [],
},
{
"name": "pad_ui_back",
"kbm": [KEY_ESCAPE],
"gamepad": [JOY_BUTTON_B],
"axis": [],
},
...other buttons...
]
func _enable_plugin() -> void:
_backup_inputs()
for action in INPUT_CONFIG_LIST:
var events: Array = []
for input_id in action.kbm:
if input_id == MOUSE_BUTTON_WHEEL_UP or input_id == MOUSE_BUTTON_WHEEL_DOWN:
var event_mouse := InputEventMouseButton.new()
event_mouse.button_index = input_id
events.append(event_mouse)
else:
var event_key := InputEventKey.new()
event_key.physical_keycode = input_id
events.append(event_key)
for button_id in action.gamepad:
var event_joypad := InputEventJoypadButton.new()
event_joypad.button_index = button_id
events.append(event_joypad)
if action.axis.size() >= 2:
var event_axis := InputEventJoypadMotion.new()
event_axis.axis = action.axis[0]
event_axis.axis_value = action.axis[1]
events.append(event_axis)
var action_data := {
"deadzone": 0.5,
"events": events
}
ProjectSettings.set_setting("input/" + action.name, action_data)
ProjectSettings.save()
func _disable_plugin() -> void:
for action in INPUT_CONFIG_LIST:
ProjectSettings.set_setting("input/" + action.name, null)
var properties: Array = ProjectSettings.get_property_list()
for prop in properties:
var property_name: String = prop["name"]
if property_name.begins_with("input/ui_"):
ProjectSettings.set_setting(property_name, null)
ProjectSettings.save()
_restore_inputs()
func _backup_inputs() -> void:
var config := ConfigFile.new()
var properties: Array = ProjectSettings.get_property_list()
for prop in properties:
var property_name: String = prop["name"]
if property_name.begins_with("input/ui_"):
var original_data = ProjectSettings.get_setting(property_name)
config.set_value("builtin_inputs", property_name, original_data)
var cleared_data := {
"deadzone": 0.5,
"events": []
}
ProjectSettings.set_setting(property_name, cleared_data)
config.save(BACKUP_FILE_PATH)
func _restore_inputs() -> void:
var config := ConfigFile.new()
if config.load(BACKUP_FILE_PATH) == OK:
if config.has_section("builtin_inputs"):
var keys = config.get_section_keys("builtin_inputs")
for property_name in keys:
var original_data = config.get_value("builtin_inputs", property_name)
ProjectSettings.set_setting(property_name, original_data)
ProjectSettings.save()
var dir := DirAccess.open("")
if dir:
dir.remove(BACKUP_FILE_PATH.get_file())
When I enable the plugin, the engine restarts and all the changes get correctly applied. The “plugin_input_backup.cfg” gets created and written properly.
On the other hand, when disabling the plugin, sometimes it gets disabled correctly, the engine restarts and the backup file gets deleted correctly. Other times, my custom input remiain active, the original ones are left empty and the backup file is still there. The wierd thing is that it mostly happens the second time I make this test (after manually resetting the project file via text editor), while the first time is usually a complete success (but sometimes even that fails).
If it helps, sometimes (with no specific criteria), when I uncheck the plugin to enable it and the confirmation popup appears I see the following error in the console:
“Condition “!p_enabled && !addon_name_to_plugin.has(addon_path)” is true.”
I don’t know if it’s relevant, but I saw it was (apparently) an absolute path problem. I checked and made sure that all paths are relative.
