Skip to content

Commit

Permalink
Merge pull request #76264 from vnen/gdscript-static-variales
Browse files Browse the repository at this point in the history
Add support for static variables in GDScript
  • Loading branch information
akien-mga committed Apr 27, 2023
2 parents 190f158 + 0ba6048 commit c4a9d32
Show file tree
Hide file tree
Showing 36 changed files with 689 additions and 86 deletions.
3 changes: 3 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@
<member name="debug/gdscript/warnings/redundant_await" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a function that is not a coroutine is called with await.
</member>
<member name="debug/gdscript/warnings/redundant_static_unload" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when the [code]@static_unload[/code] annotation is used in a script without any static variables.
</member>
<member name="debug/gdscript/warnings/renamed_in_godot_4_hint" type="bool" setter="" getter="" default="1">
When enabled, using a property, enum, or function that was renamed since Godot 3 will produce a hint if an error occurs.
</member>
Expand Down
6 changes: 6 additions & 0 deletions modules/gdscript/doc_classes/@GDScript.xml
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,12 @@
[/codeblock]
</description>
</annotation>
<annotation name="@static_unload">
<return type="void" />
<description>
Make a script with static variables to not persist after all references are lost. If the script is loaded again the static variables will revert to their default values.
</description>
</annotation>
<annotation name="@tool">
<return type="void" />
<description>
Expand Down
127 changes: 121 additions & 6 deletions modules/gdscript/gdscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,49 @@ String GDScript::_get_debug_path() const {
}
}

Error GDScript::_static_init() {
if (static_initializer) {
Callable::CallError call_err;
static_initializer->call(nullptr, nullptr, 0, call_err);
if (call_err.error != Callable::CallError::CALL_OK) {
return ERR_CANT_CREATE;
}
}
Error err = OK;
for (KeyValue<StringName, Ref<GDScript>> &inner : subclasses) {
err = inner.value->_static_init();
if (err) {
break;
}
}
return err;
}

#ifdef TOOLS_ENABLED

void GDScript::_save_old_static_data() {
old_static_variables_indices = static_variables_indices;
old_static_variables = static_variables;
for (KeyValue<StringName, Ref<GDScript>> &inner : subclasses) {
inner.value->_save_old_static_data();
}
}

void GDScript::_restore_old_static_data() {
for (KeyValue<StringName, MemberInfo> &E : old_static_variables_indices) {
if (static_variables_indices.has(E.key)) {
static_variables.write[static_variables_indices[E.key].index] = old_static_variables[E.value.index];
}
}
old_static_variables_indices.clear();
old_static_variables.clear();
for (KeyValue<StringName, Ref<GDScript>> &inner : subclasses) {
inner.value->_restore_old_static_data();
}
}

#endif

Error GDScript::reload(bool p_keep_state) {
if (reloading) {
return OK;
Expand Down Expand Up @@ -696,6 +739,14 @@ Error GDScript::reload(bool p_keep_state) {
}
}

bool can_run = ScriptServer::is_scripting_enabled() || is_tool();

#ifdef DEBUG_ENABLED
if (p_keep_state && can_run && is_valid()) {
_save_old_static_data();
}
#endif

valid = false;
GDScriptParser parser;
Error err = parser.parse(source, path, false);
Expand Down Expand Up @@ -726,7 +777,7 @@ Error GDScript::reload(bool p_keep_state) {
return ERR_PARSE_ERROR;
}

bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool();
can_run = ScriptServer::is_scripting_enabled() || parser.is_tool();

GDScriptCompiler compiler;
err = compiler.compile(&parser, this, p_keep_state);
Expand Down Expand Up @@ -760,6 +811,19 @@ Error GDScript::reload(bool p_keep_state) {
}
#endif

if (can_run) {
err = _static_init();
if (err) {
return err;
}
}

#ifdef DEBUG_ENABLED
if (can_run && p_keep_state) {
_restore_old_static_data();
}
#endif

reloading = false;
return OK;
}
Expand Down Expand Up @@ -788,6 +852,10 @@ const Variant GDScript::get_rpc_config() const {
return rpc_config;
}

void GDScript::unload_static() const {
GDScriptCache::remove_script(fully_qualified_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 @@ -824,6 +892,19 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const {
return true;
}
}

{
HashMap<StringName, MemberInfo>::ConstIterator E = static_variables_indices.find(p_name);
if (E) {
if (E->value.getter) {
Callable::CallError ce;
r_ret = const_cast<GDScript *>(this)->callp(E->value.getter, nullptr, 0, ce);
return true;
}
r_ret = static_variables[E->value.index];
return true;
}
}
top = top->_base;
}

Expand All @@ -841,7 +922,32 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) {
set_source_code(p_value);
reload();
} else {
return false;
const GDScript *top = this;
while (top) {
HashMap<StringName, MemberInfo>::ConstIterator E = 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 (member->setter) {
const Variant *args = &value;
Callable::CallError err;
callp(member->setter, &args, 1, err);
return err.error == Callable::CallError::CALL_OK;
} else {
static_variables.write[member->index] = value;
return true;
}
}
top = top->_base;
}
}

return true;
Expand Down Expand Up @@ -1293,6 +1399,13 @@ void GDScript::clear(GDScript::ClearData *p_clear_data) {
E.value.data_type.script_type_ref = Ref<Script>();
}

for (KeyValue<StringName, GDScript::MemberInfo> &E : static_variables_indices) {
clear_data->scripts.insert(E.value.data_type.script_type_ref);
E.value.data_type.script_type_ref = Ref<Script>();
}
static_variables.clear();
static_variables_indices.clear();

if (implicit_initializer) {
clear_data->functions.insert(implicit_initializer);
implicit_initializer = nullptr;
Expand All @@ -1303,6 +1416,11 @@ void GDScript::clear(GDScript::ClearData *p_clear_data) {
implicit_ready = nullptr;
}

if (static_initializer) {
clear_data->functions.insert(static_initializer);
static_initializer = nullptr;
}

_save_orphaned_subclasses(clear_data);

#ifdef TOOLS_ENABLED
Expand Down Expand Up @@ -1357,10 +1475,6 @@ GDScript::~GDScript() {

GDScriptLanguage::get_singleton()->script_list.remove(&script_list);
}

if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
GDScriptCache::remove_script(get_path());
}
}

//////////////////////////////
Expand Down Expand Up @@ -2391,6 +2505,7 @@ GDScriptLanguage::GDScriptLanguage() {
ERR_FAIL_COND(singleton);
singleton = this;
strings._init = StaticCString::create("_init");
strings._static_init = StaticCString::create("_static_init");
strings._notification = StaticCString::create("_notification");
strings._set = StaticCString::create("_set");
strings._get = StaticCString::create("_get");
Expand Down
14 changes: 14 additions & 0 deletions modules/gdscript/gdscript.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class GDScript : public Script {

HashSet<StringName> members; //members are just indices to the instantiated script.
HashMap<StringName, Variant> constants;
HashMap<StringName, MemberInfo> static_variables_indices;
Vector<Variant> static_variables;
HashMap<StringName, GDScriptFunction *> member_functions;
HashMap<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script.
HashMap<StringName, Ref<GDScript>> subclasses;
Expand All @@ -102,6 +104,12 @@ class GDScript : public Script {

#ifdef TOOLS_ENABLED

// For static data storage during hot-reloading.
HashMap<StringName, MemberInfo> old_static_variables_indices;
Vector<Variant> old_static_variables;
void _save_old_static_data();
void _restore_old_static_data();

HashMap<StringName, int> member_lines;
HashMap<StringName, Variant> member_default_values;
List<PropertyInfo> members_cache;
Expand All @@ -123,6 +131,9 @@ class GDScript : public Script {
GDScriptFunction *implicit_initializer = nullptr;
GDScriptFunction *initializer = nullptr; //direct pointer to new , faster to locate
GDScriptFunction *implicit_ready = nullptr;
GDScriptFunction *static_initializer = nullptr;

Error _static_init();

int subclass_count = 0;
RBSet<Object *> instances;
Expand Down Expand Up @@ -268,6 +279,8 @@ class GDScript : public Script {

virtual const Variant get_rpc_config() const override;

void unload_static() const;

#ifdef TOOLS_ENABLED
virtual bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
#endif
Expand Down Expand Up @@ -439,6 +452,7 @@ class GDScriptLanguage : public ScriptLanguage {

struct {
StringName _init;
StringName _static_init;
StringName _notification;
StringName _set;
StringName _get;
Expand Down
Loading

0 comments on commit c4a9d32

Please sign in to comment.