deck and deck holder RPC scopes (#106)

Reviewed-on: https://codeberg.org/StreamGraph/StreamGraph/pulls/106
Co-authored-by: Lera Elvoé <yagich@poto.cafe>
Co-committed-by: Lera Elvoé <yagich@poto.cafe>
This commit is contained in:
Lera Elvoé 2024-03-15 06:02:04 +00:00 committed by yagich
parent f43c5ecafd
commit 8bdd6709b4
11 changed files with 281 additions and 18 deletions

View file

@ -26,8 +26,8 @@ static func add_empty_deck() -> Deck:
var uuid := UUID.v4() var uuid := UUID.v4()
decks[uuid] = deck decks[uuid] = deck
deck.id = uuid deck.id = uuid
signals.deck_added.emit(uuid)
deck.connect_rpc_signals() deck.connect_rpc_signals()
signals.deck_added.emit(uuid)
return deck return deck
@ -44,8 +44,8 @@ static func open_deck_from_file(path: String) -> Deck:
static func open_deck_from_dict(data: Dictionary, path := "") -> Deck: static func open_deck_from_dict(data: Dictionary, path := "") -> Deck:
var deck := Deck.from_dict(data, path) var deck := Deck.from_dict(data, path)
decks[deck.id] = deck decks[deck.id] = deck
signals.deck_added.emit(deck.id)
deck.connect_rpc_signals() deck.connect_rpc_signals()
signals.deck_added.emit(deck.id)
return deck return deck
@ -61,6 +61,7 @@ static func add_group_from_dict(data: Dictionary, deck_id: String, instance_id:
connect_group_signals(group) connect_group_signals(group)
if deck_id not in groups_emitted: if deck_id not in groups_emitted:
group.connect_rpc_signals()
signals.deck_added.emit(deck_id) signals.deck_added.emit(deck_id)
groups_emitted.append(deck_id) groups_emitted.append(deck_id)
@ -81,6 +82,7 @@ static func add_empty_group() -> Deck:
decks[group.id] = {group.instance_id: group} decks[group.id] = {group.instance_id: group}
connect_group_signals(group) connect_group_signals(group)
if group.id not in groups_emitted: if group.id not in groups_emitted:
group.connect_rpc_signals()
signals.deck_added.emit(group.id) signals.deck_added.emit(group.id)
groups_emitted.append(group.id) groups_emitted.append(group.id)
return group return group

View file

@ -18,7 +18,18 @@ func _ready() -> void:
b.pressed.connect(func(): b.pressed.connect(func():
item_pressed.emit(deck_id, "") item_pressed.emit(deck_id, "")
) )
add_child(b) var cb := Button.new()
cb.text = "Copy"
cb.pressed.connect(
func():
DisplayServer.clipboard_set(deck_id)
)
var hb := HBoxContainer.new()
hb.add_child(b)
hb.add_child(cb)
add_child(hb)
else: else:
for instance_id: String in DeckHolder.decks[deck_id]: for instance_id: String in DeckHolder.decks[deck_id]:
var b := Button.new() var b := Button.new()

View file

@ -0,0 +1,18 @@
# (c) 2023-present Eroax
# (c) 2023-present Yagich
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
extends VBoxContainer
class_name DebugNodesList
func build(deck: Deck) -> void:
get_children().map(func(e: Node): e.queue_free())
for node_id in deck.nodes:
var b := Button.new()
var node = deck.get_node(node_id)
b.text = "%s (%s)" % [node.name, node._id.left(8)]
b.pressed.connect(
func():
DisplayServer.clipboard_set(node_id)
)
add_child(b)

View file

@ -0,0 +1,8 @@
[gd_scene load_steps=2 format=3 uid="uid://cddfyvpf5nqq7"]
[ext_resource type="Script" path="res://graph_node_renderer/debug_nodes_list.gd" id="1_jciri"]
[node name="DebugNodesList" type="VBoxContainer"]
offset_right = 40.0
offset_bottom = 40.0
script = ExtResource("1_jciri")

View file

@ -11,6 +11,7 @@ class_name DeckHolderRenderer
## Reference to the base scene for [DeckRendererGraphEdit] ## Reference to the base scene for [DeckRendererGraphEdit]
@export var DECK_SCENE: PackedScene @export var DECK_SCENE: PackedScene
@export var DEBUG_DECKS_LIST: PackedScene @export var DEBUG_DECKS_LIST: PackedScene
@export var DEBUG_NODES_LIST: PackedScene
const PERSISTENCE_NAMESPACE := "default" const PERSISTENCE_NAMESPACE := "default"
@ -48,6 +49,7 @@ enum ConnectionsMenuId {
enum DebugMenuId { enum DebugMenuId {
DECKS, DECKS,
NODES,
EMBED_SUBWINDOWS, EMBED_SUBWINDOWS,
} }
@onready var debug_popup_menu: PopupMenu = %Debug @onready var debug_popup_menu: PopupMenu = %Debug
@ -377,6 +379,15 @@ func _on_debug_id_pressed(id: int) -> void:
debug_decks.item_pressed.connect(_on_debug_decks_viewer_item_pressed) debug_decks.item_pressed.connect(_on_debug_decks_viewer_item_pressed)
add_child(d) add_child(d)
d.popup_centered() d.popup_centered()
DebugMenuId.NODES:
var d := AcceptDialog.new()
var debug_nodes: DebugNodesList = DEBUG_NODES_LIST.instantiate()
d.add_child(debug_nodes)
d.canceled.connect(d.queue_free)
d.confirmed.connect(d.queue_free)
debug_nodes.build(get_active_deck())
add_child(d)
d.popup_centered()
DebugMenuId.EMBED_SUBWINDOWS: DebugMenuId.EMBED_SUBWINDOWS:
var c := debug_popup_menu.is_item_checked(id) var c := debug_popup_menu.is_item_checked(id)
debug_popup_menu.set_item_checked(id, not c) debug_popup_menu.set_item_checked(id, not c)

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=24 format=3 uid="uid://duaah5x0jhkn6"] [gd_scene load_steps=25 format=3 uid="uid://duaah5x0jhkn6"]
[ext_resource type="Script" path="res://graph_node_renderer/deck_holder_renderer.gd" id="1_67g2g"] [ext_resource type="Script" path="res://graph_node_renderer/deck_holder_renderer.gd" id="1_67g2g"]
[ext_resource type="PackedScene" uid="uid://b84f2ngtcm5b8" path="res://graph_node_renderer/tab_container_custom.tscn" id="1_s3ug2"] [ext_resource type="PackedScene" uid="uid://b84f2ngtcm5b8" path="res://graph_node_renderer/tab_container_custom.tscn" id="1_s3ug2"]
@ -8,6 +8,7 @@
[ext_resource type="Script" path="res://addons/no-obs-ws/NoOBSWS.gd" id="4_nu72u"] [ext_resource type="Script" path="res://addons/no-obs-ws/NoOBSWS.gd" id="4_nu72u"]
[ext_resource type="PackedScene" uid="uid://dm7sc6364j84i" path="res://graph_node_renderer/debug_decks_list.tscn" id="4_ux0jt"] [ext_resource type="PackedScene" uid="uid://dm7sc6364j84i" path="res://graph_node_renderer/debug_decks_list.tscn" id="4_ux0jt"]
[ext_resource type="Script" path="res://addons/no_twitch/twitch_connection.gd" id="5_3n36q"] [ext_resource type="Script" path="res://addons/no_twitch/twitch_connection.gd" id="5_3n36q"]
[ext_resource type="PackedScene" uid="uid://cddfyvpf5nqq7" path="res://graph_node_renderer/debug_nodes_list.tscn" id="5_pnfg8"]
[ext_resource type="PackedScene" uid="uid://eioso6jb42jy" path="res://graph_node_renderer/obs_websocket_setup_dialog.tscn" id="5_uo2gj"] [ext_resource type="PackedScene" uid="uid://eioso6jb42jy" path="res://graph_node_renderer/obs_websocket_setup_dialog.tscn" id="5_uo2gj"]
[ext_resource type="PackedScene" uid="uid://bq2lxmbnic4lc" path="res://graph_node_renderer/twitch_setup_dialog.tscn" id="7_7rhap"] [ext_resource type="PackedScene" uid="uid://bq2lxmbnic4lc" path="res://graph_node_renderer/twitch_setup_dialog.tscn" id="7_7rhap"]
[ext_resource type="PackedScene" uid="uid://cuwou2aa7qfc2" path="res://graph_node_renderer/unsaved_changes_dialog_single_deck.tscn" id="8_qf6ve"] [ext_resource type="PackedScene" uid="uid://cuwou2aa7qfc2" path="res://graph_node_renderer/unsaved_changes_dialog_single_deck.tscn" id="8_qf6ve"]
@ -71,6 +72,7 @@ theme = ExtResource("1_tgul2")
script = ExtResource("1_67g2g") script = ExtResource("1_67g2g")
DECK_SCENE = ExtResource("3_uf16c") DECK_SCENE = ExtResource("3_uf16c")
DEBUG_DECKS_LIST = ExtResource("4_ux0jt") DEBUG_DECKS_LIST = ExtResource("4_ux0jt")
DEBUG_NODES_LIST = ExtResource("5_pnfg8")
new_deck_shortcut = SubResource("Shortcut_30rq6") new_deck_shortcut = SubResource("Shortcut_30rq6")
open_deck_shortcut = SubResource("Shortcut_m48tj") open_deck_shortcut = SubResource("Shortcut_m48tj")
save_deck_shortcut = SubResource("Shortcut_xr6s4") save_deck_shortcut = SubResource("Shortcut_xr6s4")
@ -135,13 +137,15 @@ item_1/id = 1
[node name="Debug" type="PopupMenu" parent="MarginContainer/VSplitContainer/VBoxContainer/MenuBar"] [node name="Debug" type="PopupMenu" parent="MarginContainer/VSplitContainer/VBoxContainer/MenuBar"]
unique_name_in_owner = true unique_name_in_owner = true
item_count = 2 item_count = 3
item_0/text = "Decks..." item_0/text = "Decks..."
item_0/id = 0 item_0/id = 0
item_1/text = "Embed subwindows" item_1/text = "Nodes..."
item_1/checkable = 1
item_1/checked = true
item_1/id = 1 item_1/id = 1
item_2/text = "Embed subwindows"
item_2/checkable = 1
item_2/checked = true
item_2/id = 2
[node name="Help" type="PopupMenu" parent="MarginContainer/VSplitContainer/VBoxContainer/MenuBar"] [node name="Help" type="PopupMenu" parent="MarginContainer/VSplitContainer/VBoxContainer/MenuBar"]
unique_name_in_owner = true unique_name_in_owner = true

View file

@ -188,7 +188,9 @@ func _on_deck_node_removed(node: DeckNode) -> void:
for renderer: DeckNodeRendererGraphNode in get_children(): for renderer: DeckNodeRendererGraphNode in get_children():
if renderer.node != node: if renderer.node != node:
continue continue
# TODO: when multiple nodes are removed and they are connected, the renderer will break
# trying to get an invalid node. (GraphEdit, this is not on us.)
# consider a batch removed signal for Deck or a separate signal for grouping nodes in 0.0.6.
renderer.queue_free() renderer.queue_free()
break break
dirty = true dirty = true

View file

@ -105,10 +105,19 @@ func _on_ws_message(peer_id: int, message: Variant) -> void:
return return
var req := RPCRequest.from_dict(result.data, peer_id) var req := RPCRequest.from_dict(result.data, peer_id)
for scope in scopes: var scope_idx := -1
for i in scopes.size():
var scope := scopes[i]
if scope.name == req.scope:
scope_idx = i
break
if scope_idx == -1:
return # TODO: error
var scope := scopes[scope_idx]
if scope.can_handle_request(req): if scope.can_handle_request(req):
scope.handle_request(req) scope.handle_request(req)
return
func _on_ws_client_connected(peer_id: int) -> void: func _on_ws_client_connected(peer_id: int) -> void:

View file

@ -79,6 +79,20 @@ func create_generic_success(request: RPCRequest) -> RPCRequestResponse:
return r return r
func create_generic_failure(request: RPCRequest) -> RPCRequestResponse:
var r := RPCRequestResponse.new(request)
r.for_request = request.id
r.scope = name
r.kept = request.keep
r.peer_id = request.peer_id
r.data = {
"success": false
}
return r
func create_event(type: String, data: Variant, condition := {}) -> RPCEvent: func create_event(type: String, data: Variant, condition := {}) -> RPCEvent:
return RPCEvent.new(type, name, data, condition) return RPCEvent.new(type, name, data, condition)

View file

@ -9,6 +9,21 @@ func _init() -> void:
operation_types = { operation_types = {
"add_node": add_node, "add_node": add_node,
"remove_node": remove_node,
"get_node": get_node,
"is_valid_connection": is_valid_connection,
"connect_nodes": connect_nodes,
"disconnect_nodes": disconnect_nodes,
"group_nodes": group_nodes,
"set_variable": set_variable,
"get_variable": get_variable,
"get_variable_list": get_variable_list,
"send_event": send_event,
"get_referenced_groups": get_referenced_groups,
} }
RPCSignalLayer.signals.deck_node_added.connect(_on_deck_node_added) RPCSignalLayer.signals.deck_node_added.connect(_on_deck_node_added)
@ -16,6 +31,7 @@ func _init() -> void:
RPCSignalLayer.signals.deck_nodes_connected.connect(_on_deck_nodes_connected) RPCSignalLayer.signals.deck_nodes_connected.connect(_on_deck_nodes_connected)
RPCSignalLayer.signals.deck_nodes_disconnected.connect(_on_deck_nodes_disconnected) RPCSignalLayer.signals.deck_nodes_disconnected.connect(_on_deck_nodes_disconnected)
RPCSignalLayer.signals.deck_variables_updated.connect(_on_deck_variables_updated)
func add_node(r: RPCRequest) -> void: func add_node(r: RPCRequest) -> void:
@ -36,6 +52,174 @@ func add_node(r: RPCRequest) -> void:
) )
func remove_node(r: RPCRequest) -> void:
reconnect(
RPCSignalLayer.signals.deck_node_removed,
_on_deck_node_removed,
func():
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var node_id: String = r.operation.payload.node_id
deck.remove_node(node_id, true)
var node_partial := RPCNodePartial.new()
node_partial.deck_id = deck.id
node_partial.id = node_id
var resp := create_generic_success(r)
resp.create_event_counterpart(node_partial)
response.emit(resp)
)
func get_node(r: RPCRequest) -> void:
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var node := deck.get_node(r.operation.payload.node_id)
var node_data := node.to_dict()
var resp := create_response(r, {"node": node_data})
response.emit(resp)
func is_valid_connection(r: RPCRequest) -> void:
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var from_node_id: String = r.operation.payload.from_node_id
var from_output_port := int(r.operation.payload.from_output_port)
var to_node_id: String = r.operation.payload.to_node_id
var to_input_port := int(r.operation.payload.to_input_port)
var is_valid = deck.is_valid_connection(from_node_id, to_node_id, from_output_port, to_input_port)
var resp := create_response(r, {"valid": is_valid})
response.emit(resp)
func connect_nodes(r: RPCRequest) -> void:
reconnect(
RPCSignalLayer.signals.deck_nodes_connected,
_on_deck_nodes_connected,
func():
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var from_node_id: String = r.operation.payload.from_node_id
var from_output_port := int(r.operation.payload.from_output_port)
var to_node_id: String = r.operation.payload.to_node_id
var to_input_port := int(r.operation.payload.to_input_port)
var connected := deck.connect_nodes(from_node_id, to_node_id, from_output_port, to_input_port)
if connected:
var connection := RPCNodeConnection.new()
connection.from_node_id = from_node_id
connection.to_node_id = to_node_id
connection.from_output_port = from_output_port
connection.to_input_port = to_input_port
var resp := create_generic_success(r)
resp.create_event_counterpart(connection)
response.emit(resp)
else:
var err := create_generic_failure(r)
response.emit(err)
)
func disconnect_nodes(r: RPCRequest) -> void:
reconnect(
RPCSignalLayer.signals.deck_nodes_disconnected,
_on_deck_nodes_disconnected,
func():
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var from_node_id: String = r.operation.payload.from_node_id
var from_output_port := int(r.operation.payload.from_output_port)
var to_node_id: String = r.operation.payload.to_node_id
var to_input_port := int(r.operation.payload.to_input_port)
deck.disconnect_nodes(from_node_id, to_node_id, from_output_port, to_input_port)
var connection := RPCNodeConnection.new()
connection.from_node_id = from_node_id
connection.to_node_id = to_node_id
connection.from_output_port = from_output_port
connection.to_input_port = to_input_port
var resp := create_generic_success(r)
resp.create_event_counterpart(connection)
response.emit(resp)
)
func group_nodes(r: RPCRequest) -> void:
reconnect(
RPCSignalLayer.signals.deck_node_removed,
_on_deck_node_removed,
func():
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var node_ids: Array = r.operation.payload.nodes
var nodes := node_ids.map(
func(e: String):
return deck.get_node(e)
)
var group := deck.group_nodes(nodes)
if group == null:
var err := create_error(r, "Error grouping")
response.emit(err)
else:
var dp := RPCDeckPartial.new(group)
var resp := create_response(r, dp)
resp.create_event_counterpart(node_ids)
response.emit(resp)
)
func set_variable(r: RPCRequest) -> void:
reconnect(
RPCSignalLayer.signals.deck_variables_updated,
_on_deck_variables_updated,
func():
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var var_name: String = r.operation.payload.name
var var_value = r.operation.payload.value
deck.set_variable(var_name, var_value)
var resp := create_generic_success(r)
resp.create_event_counterpart({})
response.emit(resp)
)
func get_variable(r: RPCRequest) -> void:
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var var_name: String = r.operation.payload.name
var value = deck.variable_stack.get(var_name)
var resp := create_response(r, {"name": var_name, "value": value})
response.emit(resp)
func get_variable_list(r: RPCRequest) -> void:
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var resp := create_response(r, {"variables": deck.variable_stack.keys()})
response.emit(resp)
func send_event(r: RPCRequest) -> void:
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var event_name: StringName = r.operation.payload.event_name
var event_data: Dictionary = r.operation.payload.get("event_data", {})
deck.send_event(event_name, event_data)
var resp := create_generic_success(r)
response.emit(resp)
func get_referenced_groups(r: RPCRequest) -> void:
var deck := DeckHolder.get_deck(r.operation.condition.deck_id)
var groups := deck.get_referenced_groups()
var resp := create_response(r, {"groups": groups})
response.emit(resp)
func _on_deck_node_added(deck_id: String, node_id: String) -> void: func _on_deck_node_added(deck_id: String, node_id: String) -> void:
event.emit(create_event("node_added", {"node_id": node_id}, {"deck_id": deck_id})) event.emit(create_event("node_added", {"node_id": node_id}, {"deck_id": deck_id}))
@ -64,3 +248,8 @@ func _on_deck_nodes_disconnected(deck_id: String, from_node_id: String, to_node_
var ev := create_event("nodes_disconnected", connection, {"deck_id": deck_id}) var ev := create_event("nodes_disconnected", connection, {"deck_id": deck_id})
event.emit(ev) event.emit(ev)
func _on_deck_variables_updated(deck_id: String) -> void:
var ev := create_event("variables_updated", {}, {"deck_id": deck_id})
event.emit(ev)

View file

@ -11,17 +11,12 @@ func _init() -> void:
#"new_deck": new_deck, # TODO: evaluate later #"new_deck": new_deck, # TODO: evaluate later
"get_deck": get_deck, "get_deck": get_deck,
"send_event": send_event, "send_event": send_event,
"error": error,
} }
RPCSignalLayer.signals.deck_added.connect(_on_deck_holder_deck_added) RPCSignalLayer.signals.deck_added.connect(_on_deck_holder_deck_added)
RPCSignalLayer.signals.deck_closed.connect(_on_deck_holder_deck_closed) RPCSignalLayer.signals.deck_closed.connect(_on_deck_holder_deck_closed)
func error(r: RPCRequest) -> void:
response.emit(create_error(r, "random error"))
func new_deck(r: RPCRequest) -> void: func new_deck(r: RPCRequest) -> void:
reconnect( reconnect(
RPCSignalLayer.signals.deck_added, RPCSignalLayer.signals.deck_added,