mirror of
https://codeberg.org/StreamGraph/StreamGraph.git
synced 2024-11-13 19:49:55 +01:00
f720efcc72
saving decks that use lib groups will no longer save the whole file path to that library (at the expense of the structure needing to be the same) also some ui/ux improvements: - more menus in sidebar remember their collapsed state between deck/node switches - adding a lib group will name the group node appropriately - save dialog properly remembers the most recent path when invoked via ctrl+s and the deck hasn't been saved before Reviewed-on: https://codeberg.org/StreamGraph/StreamGraph/pulls/162 Co-authored-by: Lera Elvoé <yagich@poto.cafe> Co-committed-by: Lera Elvoé <yagich@poto.cafe>
286 lines
8.7 KiB
GDScript
286 lines
8.7 KiB
GDScript
# (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 NodeDB
|
|
|
|
## Path to search for node files.
|
|
const BASE_NODE_PATH := "res://classes/deck/nodes/"
|
|
## Filepath where the index of [NodeDB.NodeDescriptor]s are saved to avoid reloading
|
|
## everything each run.
|
|
## @experimental
|
|
const NODE_INDEX_CACHE_PATH := "user://nodes_index.json"
|
|
## Filepath where the the list of favorite nodes is stored.
|
|
const FAVORITE_NODES_PATH := "user://favorite_nodes.json"
|
|
|
|
## A map of [code]snake_case[/code] category names for proper capitalization.
|
|
const CATEGORY_CAPITALIZATION := {
|
|
"obs": "OBS"
|
|
}
|
|
|
|
## A list of nodes that the user marked favorite in the [AddNodeMenu].
|
|
static var favorite_nodes: Array[String]
|
|
|
|
# Dictionary[node_type, NodeDescriptor]
|
|
## The node index. Maps [member DeckNode.node_type]s to [NodeDB.NodeDescriptor].
|
|
static var nodes: Dictionary = {}
|
|
|
|
static var libraries: Dictionary = {}
|
|
|
|
|
|
static func init() -> void:
|
|
load_favorites()
|
|
#if load_node_index():
|
|
#return
|
|
|
|
create_descriptors(BASE_NODE_PATH)
|
|
|
|
reload_libraries()
|
|
|
|
save_node_index()
|
|
|
|
|
|
## Fills the [member nodes] index.
|
|
static func create_descriptors(path: String) -> void:
|
|
var dir := DirAccess.open(path)
|
|
if not dir:
|
|
return
|
|
dir.list_dir_begin()
|
|
var current_file := dir.get_next()
|
|
while current_file != "":
|
|
if dir.current_is_dir():
|
|
create_descriptors(path.path_join(current_file))
|
|
elif current_file.ends_with(".gd"):
|
|
var script_path := path.path_join(current_file)
|
|
var node: DeckNode = load(script_path).new() as DeckNode
|
|
var aliases: String = node.aliases.reduce(
|
|
func(accum, el):
|
|
return accum + el
|
|
, "")
|
|
var descriptor := NodeDescriptor.new(
|
|
script_path,
|
|
node.name,
|
|
node.node_type,
|
|
node.description,
|
|
aliases,
|
|
path.get_slice("/", path.get_slice_count("/") - 1),
|
|
node.appears_in_search,
|
|
false,
|
|
)
|
|
nodes[node.node_type] = descriptor
|
|
print_verbose("NodeDB: freeing node %s, id %s" % [node.node_type, node.get_instance_id()])
|
|
node.free()
|
|
current_file = dir.get_next()
|
|
|
|
|
|
## Fills the [member libraries] index.
|
|
static func create_lib_descriptors(path: String) -> void:
|
|
var dir := DirAccess.open(path)
|
|
if not dir:
|
|
return
|
|
dir.list_dir_begin()
|
|
var current_file := dir.get_next()
|
|
while current_file != "":
|
|
if dir.current_is_dir():
|
|
create_lib_descriptors(path.path_join(current_file))
|
|
elif current_file.ends_with(".deck"):
|
|
var load_path := path.path_join(current_file)
|
|
var f := FileAccess.open(load_path, FileAccess.READ)
|
|
var deck: Dictionary = JSON.parse_string(f.get_as_text())
|
|
if not deck.deck.has("library"):
|
|
current_file = dir.get_next()
|
|
continue
|
|
var type := current_file
|
|
if nodes.has(type):
|
|
DeckHolder.logger.toast_error("Library group '%s' collides with a node with the same type." % type)
|
|
current_file = dir.get_next()
|
|
continue
|
|
if libraries.has(type):
|
|
DeckHolder.logger.toast_error("Library group '%s' collides with a library group with the same type." % type)
|
|
current_file = dir.get_next()
|
|
continue
|
|
var lib = deck.deck.library
|
|
var aliases: String = lib.lib_aliases.reduce(
|
|
func(accum, el):
|
|
return accum + el
|
|
, "")
|
|
var descriptor := NodeDescriptor.new(
|
|
load_path,
|
|
lib.lib_name,
|
|
type,
|
|
lib.lib_description,
|
|
aliases,
|
|
path.get_slice("/", path.get_slice_count("/") - 1),
|
|
true,
|
|
true,
|
|
)
|
|
descriptor.added_by_library = path
|
|
libraries[type] = descriptor
|
|
current_file = dir.get_next()
|
|
|
|
|
|
static func reload_libraries() -> void:
|
|
libraries.clear()
|
|
for path in StreamGraphConfig.get_library_search_paths():
|
|
create_lib_descriptors(path)
|
|
|
|
|
|
## Instantiates a [DeckNode] from a given [param node_type]. See [member DeckNode.node_type].
|
|
static func instance_node(node_type: String) -> DeckNode:
|
|
if not nodes.has(node_type):
|
|
return null
|
|
|
|
return load(nodes[node_type]["script_path"]).new()
|
|
|
|
|
|
## Saves the index of all loaded nodes to [member NODE_INDEX_CACHE_PATH].
|
|
static func save_node_index() -> void:
|
|
var d := {}
|
|
for node_type in nodes:
|
|
var nd: NodeDescriptor = nodes[node_type] as NodeDescriptor
|
|
d[node_type] = nd.to_dictionary()
|
|
|
|
var json := JSON.stringify(d, "\t")
|
|
var f := FileAccess.open(NODE_INDEX_CACHE_PATH, FileAccess.WRITE)
|
|
f.store_string(json)
|
|
|
|
|
|
## Loads the node index from [member NODE_INDEX_CACHE_PATH]. Returns [code]true[/code]
|
|
## if the index was found on the file system.
|
|
static func load_node_index() -> bool:
|
|
var f := FileAccess.open(NODE_INDEX_CACHE_PATH, FileAccess.READ)
|
|
if f == null:
|
|
DeckHolder.logger.log_system("node index file does not exist", Logger.LogType.WARN)
|
|
return false
|
|
|
|
var data: Dictionary = JSON.parse_string(f.get_as_text()) as Dictionary
|
|
if data.is_empty():
|
|
DeckHolder.logger.log_system("node index file exists, but is empty", Logger.LogType.ERROR)
|
|
return false
|
|
|
|
for node_type in data:
|
|
var nd_dict: Dictionary = data[node_type]
|
|
var nd := NodeDescriptor.from_dictionary(nd_dict)
|
|
nodes[node_type] = nd
|
|
|
|
DeckHolder.logger.log_system("node index file exists, loaded")
|
|
return true
|
|
|
|
|
|
## Marks a [member DeckNode.node_type] as "favorite" for use in
|
|
## [AddNodeMenu].
|
|
static func set_node_favorite(node_type: String, favorite: bool) -> void:
|
|
if (favorite and node_type in favorite_nodes) or (not favorite and node_type not in favorite_nodes):
|
|
return
|
|
|
|
if favorite:
|
|
favorite_nodes.append(node_type)
|
|
else:
|
|
favorite_nodes.erase(node_type)
|
|
|
|
var f := FileAccess.open(FAVORITE_NODES_PATH, FileAccess.WRITE)
|
|
f.store_string(JSON.stringify(favorite_nodes, "\t"))
|
|
|
|
|
|
## Loads the list of favorite [memeber DeckNode.node_type]s from [member FAVORITE_NODES_PATH].
|
|
static func load_favorites() -> void:
|
|
var f := FileAccess.open(FAVORITE_NODES_PATH, FileAccess.READ)
|
|
if not f:
|
|
return
|
|
var data: Array = JSON.parse_string(f.get_as_text())
|
|
favorite_nodes.clear()
|
|
favorite_nodes.assign(data)
|
|
|
|
|
|
## Returns [code]true[/code] if the specified [member DeckNode.node_type] is marked favorite
|
|
## by the user.
|
|
static func is_node_favorite(node_type: String) -> bool:
|
|
return node_type in favorite_nodes
|
|
|
|
|
|
static func is_library(type: String) -> bool:
|
|
return libraries.has(type)
|
|
|
|
|
|
static func get_library_descriptor(type: String) -> NodeDescriptor:
|
|
return libraries.get(type, null) as NodeDescriptor
|
|
|
|
|
|
## Returns a capitalized category string.
|
|
static func get_category_capitalization(category: String) -> String:
|
|
return CATEGORY_CAPITALIZATION.get(category, category.capitalize())
|
|
|
|
|
|
## Used for storing the shorthand data of a [DeckNode].
|
|
class NodeDescriptor:
|
|
## Default name of the [DeckNode] type this is storing properties of.
|
|
var name: String
|
|
## The node type of the [DeckNode] reference. See [member DeckNode.node_type].
|
|
var type: String
|
|
## The description of the [DeckNode] reference. See [member DeckNode.description].
|
|
var description: String
|
|
## The aliases of the [DeckNode] reference. Stored as a flattened string of
|
|
## the [member DeckNode.aliases] array.
|
|
var aliases: String
|
|
## The category of the [DeckNode] reference. See [member DeckNode.category].
|
|
var category: String
|
|
## Whether this [DeckNode] reference will appear when searching. See [member DeckNode.appears_in_search].
|
|
var appears_in_search: bool
|
|
## Whether this describes a library group.
|
|
var is_library: bool
|
|
## If [member is_library] is [code]true[/code], this is the index that adds this library.
|
|
var added_by_library: String
|
|
|
|
## Stores the path to this node's script for later instantiation.
|
|
var script_path: String
|
|
|
|
func _init(
|
|
p_script_path: String,
|
|
p_name: String,
|
|
p_type: String,
|
|
p_description: String,
|
|
p_aliases: String,
|
|
p_category: String,
|
|
p_appears_in_search: bool,
|
|
p_is_library: bool,
|
|
) -> void:
|
|
script_path = p_script_path
|
|
|
|
name = p_name
|
|
type = p_type
|
|
description = p_description
|
|
aliases = p_aliases
|
|
category = p_category
|
|
appears_in_search = p_appears_in_search
|
|
is_library = p_is_library
|
|
|
|
|
|
## Returns a [Dictionary] representation of this node descriptor.
|
|
func to_dictionary() -> Dictionary:
|
|
var d := {
|
|
"name": name,
|
|
"type": type,
|
|
"description": description,
|
|
"aliases": aliases,
|
|
"script_path": script_path,
|
|
"category": category,
|
|
"appears_in_search": appears_in_search,
|
|
"is_library": is_library,
|
|
"added_by_library": added_by_library,
|
|
}
|
|
return d
|
|
|
|
|
|
## Creates a new [NodeDB.NodeDescriptor] from a given [Dictionary] of properties.
|
|
static func from_dictionary(data: Dictionary) -> NodeDescriptor:
|
|
var nd := NodeDescriptor.new(
|
|
data.get("script_path", ""),
|
|
data.get("name", ""),
|
|
data.get("type", ""),
|
|
data.get("description", ""),
|
|
data.get("aliases", ""),
|
|
data.get("category", ""),
|
|
data.get("appears_in_search", false),
|
|
data.get("is_library", false)
|
|
)
|
|
nd.added_by_library = data.get("added_by_library", "")
|
|
return nd
|