# (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) class_name DeckHolder ## @experimental ## A static class holding references to all decks opened in the current session. ## List of decks opened this session. #static var decks: Array[Deck] static var decks: Dictionary # Dictionary[String -> id, (Deck|Dictionary[String -> instance_id, Deck])] static var groups_emitted: Array[String] static var logger := Logger.new() static var signals := Signals.new() static func _static_init() -> void: signals.deck_added.connect(RPCSignalLayer._on_deck_added) signals.deck_closed.connect(RPCSignalLayer._on_deck_closed) ## Returns a new empty deck and assigns a new random ID to it. static func add_empty_deck() -> Deck: var deck := Deck.new() var uuid := UUID.v4() decks[uuid] = deck deck.id = uuid deck.connect_rpc_signals() signals.deck_added.emit(uuid) return deck ## Opens a deck from the [param path]. static func open_deck_from_file(path: String) -> Deck: var f := FileAccess.open(path, FileAccess.READ) if f.get_error() != OK: return null var deck := open_deck_from_dict(JSON.parse_string(f.get_as_text()), path) return deck static func open_deck_from_dict(data: Dictionary, path := "") -> Deck: var deck := Deck.from_dict(data, path) decks[deck.id] = deck deck.connect_rpc_signals() signals.deck_added.emit(deck.id) return deck static func add_group_from_dict(data: Dictionary, deck_id: String, instance_id: String) -> Deck: var group := Deck.from_dict(data) group.instance_id = instance_id group.is_group = true group.group_input_node = data.deck.group_input_node group.group_output_node = data.deck.group_output_node var instances: Dictionary = decks.get(deck_id, {}) instances[instance_id] = group decks[deck_id] = instances connect_group_signals(group) if deck_id not in groups_emitted: group.connect_rpc_signals() signals.deck_added.emit(deck_id) groups_emitted.append(deck_id) return group static func make_new_group_instance(group_id: String) -> Deck: var group := get_deck(group_id) var data := group.to_dict() return add_group_from_dict(data, group_id, UUID.v4()) static func add_empty_group() -> Deck: var group := Deck.new() group.is_group = true group.id = UUID.v4() group.instance_id = UUID.v4() decks[group.id] = {group.instance_id: group} connect_group_signals(group) if group.id not in groups_emitted: group.connect_rpc_signals() signals.deck_added.emit(group.id) groups_emitted.append(group.id) return group static func connect_group_signals(group: Deck) -> void: group.node_added_to_group.connect(DeckHolder._on_node_added_to_group) group.node_removed_from_group.connect(DeckHolder._on_node_removed_from_group) group.nodes_connected_in_group.connect(DeckHolder._on_nodes_connected_in_group) group.nodes_disconnected_in_group.connect(DeckHolder._on_nodes_disconnected_in_group) group.node_port_value_updated.connect(DeckHolder._on_node_port_value_updated) group.node_renamed.connect(DeckHolder._on_node_renamed) group.node_moved.connect(DeckHolder._on_node_moved) static func get_deck(id: String) -> Deck: if not decks.has(id): return null if not decks[id] is Dictionary: return decks[id] else: return (decks[id] as Dictionary).values()[0] static func get_group_instance(group_id: String, instance_id: String) -> Deck: if not decks.has(group_id): return null if decks[group_id] is Dictionary: return (decks[group_id] as Dictionary).get(instance_id) else: return null static func close_group_instance(group_id: String, instance_id: String) -> void: # this is kinda dumb, but to close groups that may be dangling # when all instances are closed, we have to get that list # *before* we close the instance var dangling_groups := get_deck(group_id).get_referenced_groups() var group_instances: Dictionary = decks.get(group_id, {}) as Dictionary group_instances.erase(instance_id) if group_instances.is_empty(): for group in dangling_groups: close_all_group_instances(group) signals.deck_closed.emit(group_id) groups_emitted.erase(group_id) decks.erase(group_id) static func close_all_group_instances(group_id: String) -> void: if decks.get(group_id) is Dictionary: decks.erase(group_id) ## Unloads a deck. Returns a list of groups that are closed as a result of ## closing this deck. static func close_deck(deck_id: String) -> Array: if decks.get(deck_id) is Deck: var deck: Deck = decks[deck_id] as Deck var groups := deck.get_referenced_groups() for group in groups: close_all_group_instances(group) signals.deck_closed.emit(deck_id) decks.erase(deck_id) return groups return [] static func send_event(event_name: StringName, event_data: Dictionary = {}) -> void: for deck_id: String in decks: if decks[deck_id] is Deck: (decks[deck_id] as Deck).send_event(event_name, event_data) else: for deck_instance_id: String in decks[deck_id]: (decks[deck_id][deck_instance_id] as Deck).send_event(event_name, event_data) #region group signal callbacks static func _on_node_added_to_group(node: DeckNode, assign_id: String, assign_to_self: bool, deck: Deck) -> void: var group_id := deck.id for instance_id: String in decks[group_id]: if instance_id == deck.instance_id: continue var instance: Deck = get_group_instance(group_id, instance_id) instance.emit_group_signals = false var node_duplicate := DeckNode.from_dict(node.to_dict()) instance.add_node_inst(node_duplicate, assign_id, assign_to_self) instance.emit_group_signals = true static func _on_node_removed_from_group(node_id: String, remove_connections: bool, deck: Deck) -> void: var group_id := deck.id for instance_id: String in decks[group_id]: if instance_id == deck.instance_id: continue var instance: Deck = get_group_instance(group_id, instance_id) instance.emit_group_signals = false instance.remove_node(node_id, remove_connections) instance.emit_group_signals = true static func _on_nodes_connected_in_group(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int, deck: Deck) -> void: var group_id := deck.id for instance_id: String in decks[group_id]: if instance_id == deck.instance_id: continue var instance: Deck = get_group_instance(group_id, instance_id) instance.emit_group_signals = false instance.connect_nodes(from_node_id, to_node_id, from_output_port, to_input_port) instance.emit_group_signals = true static func _on_nodes_disconnected_in_group(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int, deck: Deck) -> void: var group_id := deck.id for instance_id: String in decks[group_id]: if instance_id == deck.instance_id: continue var instance: Deck = get_group_instance(group_id, instance_id) instance.emit_group_signals = false instance.disconnect_nodes(from_node_id, to_node_id, from_output_port, to_input_port) instance.emit_group_signals = true static func _on_node_port_value_updated(node_id: String, port_idx: int, new_value: Variant, deck: Deck) -> void: var group_id := deck.id for instance_id: String in decks[group_id]: if instance_id == deck.instance_id: continue var instance: Deck = get_group_instance(group_id, instance_id) instance.emit_group_signals = false instance.get_node(node_id).get_all_ports()[port_idx].set_value_no_signal(new_value) instance.emit_group_signals = true static func _on_node_renamed(node_id: String, new_name: String, deck: Deck) -> void: var group_id := deck.id for instance_id: String in decks[group_id]: if instance_id == deck.instance_id: continue var instance: Deck = get_group_instance(group_id, instance_id) instance.emit_group_signals = false instance.get_node(node_id).name = new_name instance.emit_group_signals = true static func _on_node_moved(node_id: String, new_position: Dictionary, deck: Deck) -> void: var group_id := deck.id for instance_id: String in decks[group_id]: if instance_id == deck.instance_id: continue var instance: Deck = get_group_instance(group_id, instance_id) instance.emit_group_signals = false instance.get_node(node_id).position = new_position.duplicate() instance.emit_group_signals = true #endregion class Signals: signal deck_added(deck_id: String) signal deck_closed(deck_id: String)