Skip to content

Overlay mode: plugins initialize under an orphaned PluginManager and never enter the scene tree (_ready never fires) #535

Description

@matmartinez

Summary

In --overlay-mode (OGUI v0.46.0, as shipped on Bazzite 44 via gamescope-session-ogui-steam), quick-bar plugins load and log Initialized plugin: <id> — but the plugin node's _enter_tree/_ready never fire, so any plugin that builds its UI in _ready silently does nothing.

Root cause (as observed)

CardUIOverlayMode appears to be constructed twice at session startup. The logs show the full plugin load/init sequence running twice back-to-back, along with:

ERROR: Signal 'plugin_installed' is already connected to given callable 'Resource(PluginLoader)::_on_install_plugin' in that object.
ERROR: Signal 'plugin_uninstalled' is already connected to given callable 'Resource(PluginLoader)::_on_install_plugin' in that object.

CardUIOverlayMode._init() instantiates plugin_manager.tscn, and PluginManager._init() immediately calls PluginLoader.init(self) → plugins are instantiated and parent.add_child(instance)'d while that whole subtree is still out-of-tree. For the instance of CardUIOverlayMode that never becomes the live scene, the plugin nodes stay parented under an orphaned tree forever.

Verified from inside a plugin with a deferred self-report a few frames after _init:

report: in_tree=false
parent chain: my-plugin < PluginManager < CardUIOverlayMode < root <
report2 (2 frames later): in_tree=false

Meanwhile the live CardUIOverlayMode's PluginManager re-runs _load_plugins(), but initialize_plugin returns ERR_ALREADY_EXISTS, so the plugin instance is never re-homed into the real tree.

Impact

Any third-party quick-bar plugin doing setup in _ready (the documented pattern — e.g. the plugin template) never runs in overlay mode on Bazzite 44. Because release export templates suppress script diagnostics, this fails completely silently, which likely explains "my plugin loads but shows nothing" reports.

Workaround (plugin side)

In _init, defer a check: if the node hasn't entered the tree by the time the live quick bar exists, reparent into the live tree:

func _init() -> void:
	_rescue.call_deferred()

func _rescue() -> void:
	var tree := Engine.get_main_loop() as SceneTree
	if not tree: return
	for i in 300:
		if is_inside_tree(): return
		if tree.get_first_node_in_group("quick-bar"): break
		await tree.process_frame
	if is_inside_tree(): return
	logger.warn("Orphaned plugin node detected, reparenting into the live scene tree")
	if get_parent(): get_parent().remove_child(self)
	tree.root.add_child(self)

Suggested fixes

  • Don't initialize plugins from PluginManager._init() — defer to _ready (node is in-tree by then), or
  • have initialize_plugin detect that an "already initialized" plugin's node is not is_inside_tree() and re-home it, and
  • guard the plugin_installed/plugin_uninstalled signal connections in PluginLoader.init() against double connection (the double-construction itself may also be worth fixing).

Environment

OGUI 0.46.0 (Bazzite 44 44.20260820, opengamepadui-0.46.0-9.fc44), overlay mode over Steam, AYANEO 3. Found while building a quick-bar plugin for the AYANEO 3 magic modules (context: #528).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions