Skip to content

Commit

Permalink
GDScript: Add onset
Browse files Browse the repository at this point in the history
  • Loading branch information
rune-scape committed Oct 2, 2024
1 parent e3213aa commit 6028f61
Show file tree
Hide file tree
Showing 17 changed files with 471 additions and 153 deletions.
15 changes: 6 additions & 9 deletions core/object/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1075,17 +1075,18 @@ void Object::add_user_signal(const MethodInfo &p_signal) {
ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), "Trying to add already existing signal '" + p_signal.name + "'.");
SignalData s;
s.user = p_signal;
s.removable = true;
signal_map[p_signal.name] = s;
}

bool Object::_has_user_signal(const StringName &p_name) const {
bool Object::has_user_signal(const StringName &p_name) const {
if (!signal_map.has(p_name)) {
return false;
}
return signal_map[p_name].user.name.length() > 0;
}

void Object::_remove_user_signal(const StringName &p_name) {
void Object::remove_user_signal(const StringName &p_name) {
SignalData *s = signal_map.getptr(p_name);
ERR_FAIL_NULL_MSG(s, "Provided signal does not exist.");
ERR_FAIL_COND_MSG(!s->removable, "Signal is not removable (not added with add_user_signal).");
Expand Down Expand Up @@ -1247,10 +1248,6 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) {
}

add_user_signal(mi);

if (signal_map.has(p_name)) {
signal_map.getptr(p_name)->removable = true;
}
}

TypedArray<Dictionary> Object::_get_signal_list() const {
Expand Down Expand Up @@ -1301,7 +1298,7 @@ bool Object::has_signal(const StringName &p_name) const {
return true;
}

if (_has_user_signal(p_name)) {
if (has_user_signal(p_name)) {
return true;
}

Expand Down Expand Up @@ -1672,8 +1669,8 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_meta_list"), &Object::_get_meta_list_bind);

ClassDB::bind_method(D_METHOD("add_user_signal", "signal", "arguments"), &Object::_add_user_signal, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("has_user_signal", "signal"), &Object::_has_user_signal);
ClassDB::bind_method(D_METHOD("remove_user_signal", "signal"), &Object::_remove_user_signal);
ClassDB::bind_method(D_METHOD("has_user_signal", "signal"), &Object::has_user_signal);
ClassDB::bind_method(D_METHOD("remove_user_signal", "signal"), &Object::remove_user_signal);

{
MethodInfo mi;
Expand Down
4 changes: 2 additions & 2 deletions core/object/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -634,8 +634,6 @@ class Object {
mutable const StringName *_class_name_ptr = nullptr;

void _add_user_signal(const String &p_name, const Array &p_args = Array());
bool _has_user_signal(const StringName &p_name) const;
void _remove_user_signal(const StringName &p_name);
Error _emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
TypedArray<Dictionary> _get_signal_list() const;
TypedArray<Dictionary> _get_signal_connection_list(const StringName &p_signal) const;
Expand Down Expand Up @@ -909,6 +907,8 @@ class Object {
void set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance);

void add_user_signal(const MethodInfo &p_signal);
bool has_user_signal(const StringName &p_name) const;
void remove_user_signal(const StringName &p_name);

template <typename... VarArgs>
Error emit_signal(const StringName &p_name, VarArgs... p_args) {
Expand Down
10 changes: 1 addition & 9 deletions core/string/string_name.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,7 @@ int StringName::length() const {
}

bool StringName::is_empty() const {
if (_data) {
if (_data->cname) {
return _data->cname[0] == 0;
} else {
return _data->name.is_empty();
}
}

return true;
return !_data;
}

StringName &StringName::operator=(const StringName &p_name) {
Expand Down
12 changes: 12 additions & 0 deletions modules/gdscript/doc_classes/@GDScript.xml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,18 @@
[b]Note:[/b] If [member ProjectSettings.editor/export/convert_text_resources_to_binary] is [code]true[/code], [method @GDScript.load] will not be able to read converted files in an exported project. If you rely on run-time loading of files present within the PCK, set [member ProjectSettings.editor/export/convert_text_resources_to_binary] to [code]false[/code].
</description>
</method>
<method name="onset">
<return type="Signal" />
<param index="0" name="member_expression" type="Variant" />
<description>
Returns a [Signal] that emits when [param member_expression] variable on base is set. The member expression is not evaluated
[codeblock]
# Observe variable being set
onset(obj.x).connect(func(old, new): print("x was set, old:", old, ", new: ", new))
[/codeblock]
[b]Note:[/b] [method onset] is a keyword, not a function. So you cannot access it as a [Callable].
</description>
</method>
<method name="preload">
<return type="Resource" />
<param index="0" name="path" type="String" />
Expand Down
5 changes: 3 additions & 2 deletions modules/gdscript/editor/gdscript_highlighter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,8 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
String word = str.substr(j, to - j);
Color col;
if (global_functions.has(word)) {
// "assert" and "preload" are reserved, so highlight even if not followed by a bracket.
if (word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::ASSERT) || word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::PRELOAD)) {
// "assert", "preload", and "onset" are reserved, so highlight even if not followed by a bracket.
if (word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::ASSERT) || word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::PRELOAD) || word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::ONSET)) {
col = global_function_color;
} else {
// For other global functions, check if followed by bracket.
Expand Down Expand Up @@ -760,6 +760,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
// "assert" and "preload" are not utility functions, but are global nonetheless, so insert them.
global_functions.insert(SNAME("assert"));
global_functions.insert(SNAME("preload"));
global_functions.insert(SNAME("onset"));
for (const StringName &E : global_function_list) {
global_functions.insert(E);
}
Expand Down
132 changes: 55 additions & 77 deletions modules/gdscript/gdscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@

#include "gdscript.h"

#include "core/error/error_macros.h"
#include "core/object/ref_counted.h"
#include "core/object/script_instance.h"
#include "core/variant/callable.h"
#include "gdscript_analyzer.h"
#include "gdscript_cache.h"
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
#include "gdscript_rpc_callable.h"
#include "gdscript_tokenizer_buffer.h"
#include "gdscript_warning.h"
#include "modules/gdscript/gdscript_function.h"

#ifdef TOOLS_ENABLED
#include "editor/gdscript_docgen.h"
Expand Down Expand Up @@ -696,12 +701,12 @@ void GDScript::_static_default_init() {
Array default_value;
const GDScriptDataType &element_type = type.get_container_element_type(0);
default_value.set_typed(element_type.builtin_type, element_type.native_type, element_type.script_type);
static_variables.write[E.value.index] = default_value;
static_variables.write[E.value.index].value = default_value;
} else {
Variant default_value;
Callable::CallError err;
Variant::construct(type.builtin_type, default_value, nullptr, 0, err);
static_variables.write[E.value.index] = default_value;
static_variables.write[E.value.index].value = default_value;
}
}
}
Expand Down Expand Up @@ -919,6 +924,16 @@ void GDScript::unload_static() const {
GDScriptCache::remove_script(fully_qualified_name);
}

Signal GDScript::_get_member_set_signal(Object *p_base, MemberData &p_member, const MemberInfo &p_member_info, const StringName &p_member_name) {
if (p_member.onset_signal_name.is_empty()) {
p_member.onset_signal_name = StringName(String("@onset(") + String(p_member_name) + ")");
if (!p_base->has_user_signal(p_member.onset_signal_name)) {
p_base->add_user_signal(MethodInfo(Variant::NIL, p_member.onset_signal_name, PropertyInfo(p_member_info.data_type.builtin_type, "old_value"), PropertyInfo(p_member_info.data_type.builtin_type, "new_value")));
}
}
return Signal(p_base, p_member.onset_signal_name);
}

Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *top = this;
while (top) {
Expand Down Expand Up @@ -963,7 +978,7 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = (ce.error == Callable::CallError::CALL_OK) ? ret : Variant();
return true;
}
r_ret = top->static_variables[E->value.index];
r_ret = top->static_variables[E->value.index].value;
return true;
}
}
Expand Down Expand Up @@ -1003,27 +1018,8 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) {

GDScript *top = this;
while (top) {
HashMap<StringName, MemberInfo>::ConstIterator E = top->static_variables_indices.find(p_name);
if (E) {
const MemberInfo *member = &E->value;
Variant value = p_value;
if (member->data_type.has_type && !member->data_type.is_type(value)) {
const Variant *args = &p_value;
Callable::CallError err;
Variant::construct(member->data_type.builtin_type, value, &args, 1, err);
if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) {
return false;
}
}
if (likely(top->valid) && member->setter) {
const Variant *args = &value;
Callable::CallError err;
callp(member->setter, &args, 1, err);
return err.error == Callable::CallError::CALL_OK;
} else {
top->static_variables.write[member->index] = value;
return true;
}
if (HashMap<StringName, MemberInfo>::ConstIterator E = top->static_variables_indices.find(p_name)) {
return top->_set_member(nullptr, top->static_variables, E->value, p_value);
}

top = top->_base;
Expand All @@ -1032,6 +1028,31 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) {
return false;
}

bool GDScript::_set_member(GDScriptInstance *p_instance, Vector<MemberData> &p_members, const MemberInfo &p_member_info, const Variant &p_value) {
Variant value = p_value;
if (p_member_info.data_type.has_type && !p_member_info.data_type.is_type(value)) {
const Variant *args = &p_value;
Callable::CallError err;
Variant::construct(p_member_info.data_type.builtin_type, value, &args, 1, err);
if (err.error != Callable::CallError::CALL_OK || !p_member_info.data_type.is_type(value)) {
return false;
}
}
if (likely(valid) && !p_member_info.setter.is_empty()) {
const Variant *args = &value;
Callable::CallError err;
if (p_instance) {
p_instance->callp(p_member_info.setter, &args, 1, err);
} else {
callp(p_member_info.setter, &args, 1, err);
}
return err.error == Callable::CallError::CALL_OK;
} else {
_set_member_data(p_instance ? p_instance->owner : this, p_members.write[p_member_info.index], value);
return true;
}
}

void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));

Expand Down Expand Up @@ -1655,61 +1676,18 @@ GDScript::~GDScript() {
//////////////////////////////

bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
{
HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name);
if (E) {
const GDScript::MemberInfo *member = &E->value;
Variant value = p_value;
if (member->data_type.has_type && !member->data_type.is_type(value)) {
const Variant *args = &p_value;
Callable::CallError err;
Variant::construct(member->data_type.builtin_type, value, &args, 1, err);
if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) {
return false;
}
}
if (likely(script->valid) && member->setter) {
const Variant *args = &value;
Callable::CallError err;
callp(member->setter, &args, 1, err);
return err.error == Callable::CallError::CALL_OK;
} else {
members.write[member->index] = value;
return true;
}
}
if (HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name)) {
return script->_set_member(this, members, E->value, p_value);
}

GDScript *sptr = script.ptr();
while (sptr) {
{
HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = sptr->static_variables_indices.find(p_name);
if (E) {
const GDScript::MemberInfo *member = &E->value;
Variant value = p_value;
if (member->data_type.has_type && !member->data_type.is_type(value)) {
const Variant *args = &p_value;
Callable::CallError err;
Variant::construct(member->data_type.builtin_type, value, &args, 1, err);
if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) {
return false;
}
}
if (likely(sptr->valid) && member->setter) {
const Variant *args = &value;
Callable::CallError err;
callp(member->setter, &args, 1, err);
return err.error == Callable::CallError::CALL_OK;
} else {
sptr->static_variables.write[member->index] = value;
return true;
}
}
if (HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = sptr->static_variables_indices.find(p_name)) {
return sptr->_set_member(nullptr, sptr->static_variables, E->value, p_value);
}

if (likely(sptr->valid)) {
HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set);
if (E) {
if (HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set)) {
Variant name = p_name;
const Variant *args[2] = { &name, &p_value };

Expand Down Expand Up @@ -1737,7 +1715,7 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
r_ret = (err.error == Callable::CallError::CALL_OK) ? ret : Variant();
return true;
}
r_ret = members[E->value.index];
r_ret = members[E->value.index].value;
return true;
}
}
Expand All @@ -1761,7 +1739,7 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
r_ret = (ce.error == Callable::CallError::CALL_OK) ? ret : Variant();
return true;
}
r_ret = sptr->static_variables[E->value.index];
r_ret = sptr->static_variables[E->value.index].value;
return true;
}
}
Expand Down Expand Up @@ -2118,14 +2096,13 @@ const Variant GDScriptInstance::get_rpc_config() const {
void GDScriptInstance::reload_members() {
#ifdef DEBUG_ENABLED

Vector<Variant> new_members;
Vector<GDScript::MemberData> new_members;
new_members.resize(script->member_indices.size());

//pass the values to the new indices
for (KeyValue<StringName, GDScript::MemberInfo> &E : script->member_indices) {
if (member_indices_cache.has(E.key)) {
Variant value = members[member_indices_cache[E.key]];
new_members.write[E.value.index] = value;
new_members.write[E.value.index] = members[member_indices_cache[E.key]];
}
}

Expand Down Expand Up @@ -2768,6 +2745,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
// Functions (highlighter uses global function color instead).
"assert",
"preload",
"onset",
// Types (highlighter uses type color instead).
"void",
nullptr,
Expand Down
Loading

0 comments on commit 6028f61

Please sign in to comment.