Skip to content

Commit

Permalink
CraterCrashGH-444 Improve pin type-safety
Browse files Browse the repository at this point in the history
  • Loading branch information
Naros committed Jul 5, 2024
1 parent 2d5a2dd commit cb63102
Show file tree
Hide file tree
Showing 101 changed files with 2,166 additions and 911 deletions.
9 changes: 9 additions & 0 deletions cmake/extension_db.cpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ namespace godot
return ExtensionDB::_singleton->_global_enums[p_name];
}

EnumInfo ExtensionDB::get_global_enum_by_value(const StringName& p_name)
{
for (const KeyValue<StringName, EnumInfo>& E : ExtensionDB::_singleton->_global_enums)
for (const EnumValue& ev : E.value.values)
if (ev.name.match(p_name))
return E.value;
return {};
}

EnumValue ExtensionDB::get_global_enum_value(const StringName& p_name)
{
for (const KeyValue<StringName, EnumInfo>& E : ExtensionDB::_singleton->_global_enums)
Expand Down
1 change: 1 addition & 0 deletions cmake/generate_godot_extension_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ def create_db_header():
print_indent("static PackedStringArray get_global_enum_names();")
print_indent("static PackedStringArray get_global_enum_value_names();")
print_indent("static EnumInfo get_global_enum(const StringName& p_enum_name);")
print_indent("static EnumInfo get_global_enum_by_value(const StringName& p_name);")
print_indent("static EnumValue get_global_enum_value(const StringName& p_enum_value_name);")
print_indent("")
print_indent("static PackedStringArray get_math_constant_names();")
Expand Down
9 changes: 9 additions & 0 deletions src/api/extension_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4907,6 +4907,15 @@ namespace godot
return ExtensionDB::_singleton->_global_enums[p_name];
}

EnumInfo ExtensionDB::get_global_enum_by_value(const StringName& p_name)
{
for (const KeyValue<StringName, EnumInfo>& E : ExtensionDB::_singleton->_global_enums)
for (const EnumValue& ev : E.value.values)
if (ev.name.match(p_name))
return E.value;
return {};
}

EnumValue ExtensionDB::get_global_enum_value(const StringName& p_name)
{
for (const KeyValue<StringName, EnumInfo>& E : ExtensionDB::_singleton->_global_enums)
Expand Down
1 change: 1 addition & 0 deletions src/api/extension_db.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ namespace godot
static PackedStringArray get_global_enum_names();
static PackedStringArray get_global_enum_value_names();
static EnumInfo get_global_enum(const StringName& p_enum_name);
static EnumInfo get_global_enum_by_value(const StringName& p_name);
static EnumValue get_global_enum_value(const StringName& p_enum_value_name);

static PackedStringArray get_math_constant_names();
Expand Down
108 changes: 108 additions & 0 deletions src/common/property_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// This file is part of the Godot Orchestrator project.
//
// Copyright (c) 2023-present Vahera Studios LLC and its contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "common/property_utils.h"

#include "string_utils.h"

#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/templates/hash_map.hpp>

namespace PropertyUtils
{
static HashMap<uint32_t, String> get_property_usage_name_map()
{
HashMap<uint32_t, String> names;
names[PROPERTY_USAGE_NONE] = "None";
names[PROPERTY_USAGE_STORAGE] = "Storage";
names[PROPERTY_USAGE_EDITOR] = "Editor";
names[PROPERTY_USAGE_CLASS_IS_BITFIELD] ="ClassIsBitfield";
names[PROPERTY_USAGE_CLASS_IS_ENUM] = "ClassIsEnum";
names[PROPERTY_USAGE_NIL_IS_VARIANT] = "NilIsVariant";
names[PROPERTY_USAGE_DEFAULT] = "Default";
return names;
}

PropertyInfo create_object(const String& p_name, const String& p_class_name)
{
PropertyHint hint = PROPERTY_HINT_NONE;
String hint_string = "";
if (ClassDB::is_parent_class(p_class_name, RefCounted::get_class_static()))
{
hint = PROPERTY_HINT_RESOURCE_TYPE;
hint_string = p_class_name;
}
return PropertyInfo(Variant::OBJECT, p_name, hint, hint_string, PROPERTY_USAGE_DEFAULT, p_class_name);
}

PropertyInfo create_variant(const String& p_name)
{
return PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT);
}

PropertyInfo create_enum_class(const String& p_name, const String& p_class_name)
{
return PropertyInfo(Variant::INT, p_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CLASS_IS_ENUM, p_class_name);
}

PropertyInfo create_class_enum(const String& p_name, const String& p_class_name, const String& p_enum_name)
{
return create_enum_class(p_name, vformat("%s.%s", p_class_name, p_enum_name));
}

PropertyInfo create_typed(const String& p_name, Variant::Type p_type, const String& p_class_name)
{
if (p_type == Variant::NIL)
return create_variant(p_name);
else
return PropertyInfo(p_type, p_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, p_class_name);
}

PropertyInfo create_file(const String& p_name, const String& p_filters)
{
return PropertyInfo(Variant::STRING, p_name, PROPERTY_HINT_FILE, p_filters, PROPERTY_USAGE_DEFAULT);
}

String get_property_type_name(const PropertyInfo& p_property)
{
if (p_property.type == Variant::NIL && p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT)
return "Variant";

if (p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM || p_property.usage & PROPERTY_USAGE_CLASS_IS_BITFIELD)
return "Enum";

if (p_property.type == Variant::OBJECT)
{
if (!p_property.class_name.is_empty() && !p_property.class_name.contains("."))
return p_property.class_name;
}

return Variant::get_type_name(p_property.type);
}

String usage_to_string(uint32_t p_usage)
{
static HashMap<uint32_t, String> usage_names = get_property_usage_name_map();

PackedStringArray values;
for (const KeyValue<uint32_t, String>& E : usage_names)
if (E.key & p_usage)
values.push_back(E.value);

return StringUtils::join(", ", values);
}

}
81 changes: 81 additions & 0 deletions src/common/property_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// This file is part of the Godot Orchestrator project.
//
// Copyright (c) 2023-present Vahera Studios LLC and its contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef ORCHESTRATOR_PROPERTY_UTILS_H
#define ORCHESTRATOR_PROPERTY_UTILS_H

#include <godot_cpp/core/property_info.hpp>

using namespace godot;

namespace PropertyUtils
{
/// Create a simple PropertyInfo for an object type with a given class name.
/// @param p_name the property name
/// @param p_class_name the object class name
/// @return the property info structure
PropertyInfo create_object(const String& p_name, const String& p_class_name = String());

/// Creates a simple PropertyInfo for a Variant type.
/// @param p_name the property name
/// @return the property info structure
PropertyInfo create_variant(const String& p_name);

/// Creates a property info for a global enum type.
/// @param p_name the property name
/// @param p_class_name the global enum class name
/// @return the property info structure
PropertyInfo create_enum_class(const String& p_name, const String& p_class_name);

/// Creates a property info for a nested class-specific enum type.
/// @param p_name the property name
/// @param p_class_name the class that owns the enum
/// @param p_enum_name the enumeration name
/// @return the property info structure
PropertyInfo create_class_enum(const String& p_name, const String& p_class_name, const String& p_enum_name);

/// Creates a property from a variant type
/// @param p_name the property name
/// @param p_type the variant type
/// @param p_class_name optional class name
/// @return the property info structure
PropertyInfo create_typed(const String& p_name, Variant::Type p_type, const String& p_class_name = String());

/// Create a property for a file selection
/// @param p_name the property name
/// @param p_filters optional file filters
/// @return the property info structure
PropertyInfo create_file(const String& p_name, const String& p_filters = String());

// Utility methods
_FORCE_INLINE_ bool is_nil(const PropertyInfo& p_property) { return p_property.type == Variant::NIL; }
_FORCE_INLINE_ bool is_object(const PropertyInfo& p_property) { return p_property.type == Variant::OBJECT; }
_FORCE_INLINE_ bool is_variant(const PropertyInfo& p_property) { return p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT; }
_FORCE_INLINE_ bool is_class_enum(const PropertyInfo& p_property) { return p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM; }
_FORCE_INLINE_ bool is_class_bitfield(const PropertyInfo& p_property) { return p_property.usage & PROPERTY_USAGE_CLASS_IS_BITFIELD; }

/// Get the type name for the specified property
/// @param p_property the property
/// @return the property type name
String get_property_type_name(const PropertyInfo& p_property);

/// Converts a property info's <code>usage</code> bitfield to a string.
/// @param p_usage the property usage flags bitfield value
/// @return comma-separated string of property usage flags
String usage_to_string(uint32_t p_usage);
};

#endif // ORCHESTRATOR_PROPERTY_UTILS_H
2 changes: 1 addition & 1 deletion src/editor/component_panels/variables_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void OrchestratorScriptVariablesComponentPanel::_create_variable_item(TreeItem*
item->set_button_disabled(0, index, true);
}

item->add_button(0, SceneUtils::get_editor_icon(p_variable->get_variable_type_name()), 2);
item->add_button(0, SceneUtils::get_class_icon(p_variable->get_variable_type_name()), 2);

if (!p_variable->get_description().is_empty())
{
Expand Down
6 changes: 5 additions & 1 deletion src/editor/graph/actions/default_action_registrar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,10 @@ void OrchestratorDefaultGraphActionRegistrar::_register_class_properties(const O
{
const PropertyInfo pi = DictionaryUtils::to_property(properties[i]);

// Exclude properties that are not included in the class reference
if (pi.usage & PROPERTY_USAGE_INTERNAL)
continue;

OrchestratorGraphActionSpec getter_spec;
// todo: remove "class/properties/"
getter_spec.category = vformat("Class/Properties/%s/get_%s", p_class_name, pi.name);
Expand Down Expand Up @@ -497,7 +501,7 @@ void OrchestratorDefaultGraphActionRegistrar::_register_class_methods(const Orch
if (OScriptNodeEvent::is_event_method(mi))
handler_ptr = memnew(OrchestratorGraphNodeSpawnerEvent(mi));
else
handler_ptr = memnew(OrchestratorGraphNodeSpawnerCallMemberFunction(mi));
handler_ptr = memnew(OrchestratorGraphNodeSpawnerCallMemberFunction(p_class_name, mi));

Ref<OrchestratorGraphActionHandler> handler(handler_ptr);
p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler)));
Expand Down
37 changes: 35 additions & 2 deletions src/editor/graph/autowire_selections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "common/scene_utils.h"
#include "common/settings.h"
#include "script/node.h"
#include "script/nodes/math/operator_node.h"

#include <godot_cpp/classes/button.hpp>
#include <godot_cpp/classes/tree.hpp>
Expand Down Expand Up @@ -73,6 +74,20 @@ void OrchestratorScriptAutowireSelections::_notification(int p_what)
}
}

Vector<Ref<OScriptNodePin>> OrchestratorScriptAutowireSelections::_get_choices_that_match_class(const Vector<Ref<OScriptNodePin>>& p_choices)
{
Vector<Ref<OScriptNodePin>> exact_matches;
for (const Ref<OScriptNodePin>& choice : p_choices)
if (choice->get_property_info().class_name.match(_pin->get_property_info().class_name))
exact_matches.push_back(choice);
return exact_matches;
}

void OrchestratorScriptAutowireSelections::_close_window()
{
get_ok_button()->call_deferred("emit_signal", "pressed");
}

void OrchestratorScriptAutowireSelections::popup_autowire()
{
get_ok_button()->set_disabled(true);
Expand All @@ -85,15 +100,33 @@ void OrchestratorScriptAutowireSelections::popup_autowire()
if (choices.size() == 1)
_choice = choices[0];

get_ok_button()->call_deferred("emit_signal", "pressed");
_close_window();
return;
}

// If the autowire selection dialog is disabled, then just return
OrchestratorSettings* settings = OrchestratorSettings::get_singleton();
if (!settings->get_setting("ui/graph/show_autowire_selection_dialog", true))
{
get_ok_button()->call_deferred("emit_signal", "pressed");
_close_window();
return;
}

// Handle unique cases where there is a scoring system, class ranks higher than types
const Vector<Ref<OScriptNodePin>> exact_matches = _get_choices_that_match_class(choices);
if (exact_matches.size() == 1)
{
_choice = exact_matches[0];
_close_window();
return;
}

// Operator nodes should always autowire to first match
Ref<OScriptNodeOperator> operator_node = _spawned;
if (operator_node.is_valid() && choices.size() > 1)
{
_choice = choices[0];
_close_window();
return;
}

Expand Down
8 changes: 8 additions & 0 deletions src/editor/graph/autowire_selections.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ class OrchestratorScriptAutowireSelections : public ConfirmationDialog
/// @param p_what the notification to be handled
void _notification(int p_what);

/// Returns a sublist of choices that match the pin's class type
/// @param p_choices list of eligible choices
/// @return exact choices that should be autowired
Vector<Ref<OScriptNodePin>> _get_choices_that_match_class(const Vector<Ref<OScriptNodePin>>& p_choices);

/// Closes the window
void _close_window();

public:

/// Get the node pin source
Expand Down
Loading

0 comments on commit cb63102

Please sign in to comment.