Skip to content

Commit

Permalink
Fix crash when opening graph resource with a missing function reference.
Browse files Browse the repository at this point in the history
Also try to not interpret the graph as "new" in the editor if it actually
failed, so don't load default preset.

Godot reports this to the user with a dialog when opening scenes, or when
opening resources for the first time, but not always: if you open a scene
with the missing dependency, it still loads the scene, and if you open
the graph after that, it opens without showing you the dialog again....
so we have to handle the mess ourselves in such cases.
  • Loading branch information
Zylann committed Jun 7, 2023
1 parent f74d075 commit 483e505
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 8 deletions.
6 changes: 3 additions & 3 deletions editor/graph/voxel_graph_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,9 @@ void VoxelGraphEditor::set_generator(Ref<VoxelGeneratorGraph> generator) {
Ref<VoxelGraphFunction> graph = generator->get_main_function();

// Load a default preset when creating new graphs.
// TODO Downside is, an empty graph cannot be seen.
// But Godot doesnt let us know if the resource has been created from the inspector or not
if (graph->get_nodes_count() == 0) {
// Downside is, an empty graph cannot be seen. But Godot doesnt let us know if the resource has been created
// from the inspector or not, so we had to introduce a special boolean...
if (graph->get_nodes_count() == 0 && graph->can_load_default_graph()) {
_generator->load_plane_preset();
}

Expand Down
18 changes: 13 additions & 5 deletions generators/graph/voxel_graph_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const char *VoxelGraphFunction::SIGNAL_NODE_NAME_CHANGED = "node_name_changed";
void VoxelGraphFunction::clear() {
unregister_subresources();
_graph.clear();
#ifdef TOOLS_ENABLED
_can_load_default_graph = false;
#endif
}

ProgramGraph::Node *create_node_internal(ProgramGraph &graph, VoxelGraphFunction::NodeTypeID type_id, Vector2 position,
Expand Down Expand Up @@ -843,7 +846,7 @@ static bool var_to_id(Variant v, uint32_t &out_id, uint32_t min = 0) {
return true;
}

static bool load_graph_from_variant_data(ProgramGraph &graph, Dictionary data) {
static bool load_graph_from_variant_data(ProgramGraph &graph, Dictionary data, String resource_path) {
Dictionary nodes_data = data["nodes"];
Array connections_data = data["connections"];
const NodeTypeDB &type_db = NodeTypeDB::get_singleton();
Expand Down Expand Up @@ -880,9 +883,14 @@ static bool load_graph_from_variant_data(ProgramGraph &graph, Dictionary data) {
const String func_key = ntype.params[0].name;
Ref<VoxelGraphFunction> function = node_data[func_key];
if (function.is_null()) {
ERR_PRINT(String("Unable to load external function referenced in {0}")
.format(varray(VoxelGraphFunction::get_class_static())));
continue;
ERR_PRINT(String("Unable to load external function referenced in {0} {}")
.format(varray(VoxelGraphFunction::get_class_static(), resource_path)));
// continue;
// Cancel, connections to that node cause crashes if we carry on loading. Perhaps we could try using a
// placeholder in the future so the graph can still be opened?
// We should also report the missing dependencies in the editor somehow, because Godot doesn't do it for
// us if the resource is opened in some cases
return false;
}
setup_function(*node, function);
// TODO Create a placeholder node in case a function isn't found to avoid loss of data?
Expand Down Expand Up @@ -970,7 +978,7 @@ static bool load_graph_from_variant_data(ProgramGraph &graph, Dictionary data) {
bool VoxelGraphFunction::load_graph_from_variant_data(Dictionary data) {
clear();

if (zylann::voxel::pg::load_graph_from_variant_data(_graph, data)) {
if (zylann::voxel::pg::load_graph_from_variant_data(_graph, data, get_path())) {
register_subresources();
return true;

Expand Down
13 changes: 13 additions & 0 deletions generators/graph/voxel_graph_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,12 @@ class VoxelGraphFunction : public Resource {

void update_function_nodes(std::vector<ProgramGraph::Connection> *removed_connections);

#ifdef TOOLS_ENABLED
bool can_load_default_graph() const {
return _can_load_default_graph;
}
#endif

private:
void register_subresource(Resource &resource);
void unregister_subresource(Resource &resource);
Expand All @@ -244,6 +250,13 @@ class VoxelGraphFunction : public Resource {
ProgramGraph _graph;
std::vector<Port> _inputs;
std::vector<Port> _outputs;
#ifdef TOOLS_ENABLED
// Godot doesn't make a difference between a resource newly created in the inspector and an empty one or one created
// from script... It is necessary to know that in order to load a "hello world" graph in the editor when creating a
// new graph. True by default after being created, but will become false if cleared (which means it's not a brand
// new instance).
bool _can_load_default_graph = true;
#endif
};

ProgramGraph::Node *create_node_internal(ProgramGraph &graph, VoxelGraphFunction::NodeTypeID type_id, Vector2 position,
Expand Down

0 comments on commit 483e505

Please sign in to comment.