summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdnative/gdnative.cpp3
-rw-r--r--modules/gdnative/godot/gdnative.cpp5
-rw-r--r--modules/gdnative/godot/gdnative.h5
-rw-r--r--modules/gdnative/register_types.cpp3
-rw-r--r--modules/gdscript/gd_editor.cpp4
-rw-r--r--modules/gdscript/gd_parser.cpp5
-rw-r--r--modules/gdscript/gd_tokenizer.cpp4
-rw-r--r--modules/nativescript/nativescript.cpp269
-rw-r--r--modules/nativescript/nativescript.h36
-rw-r--r--modules/nativescript/register_types.cpp33
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp2
-rw-r--r--modules/visual_script/visual_script_editor.cpp10
12 files changed, 323 insertions, 56 deletions
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index e810c33f1c..158f7fd94d 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -185,6 +185,8 @@ void GDNative::_bind_methods() {
}
void GDNative::set_library(Ref<GDNativeLibrary> p_library) {
+ ERR_EXPLAIN("Tried to change library of GDNative when it is already set");
+ ERR_FAIL_COND(library.is_valid());
library = p_library;
}
@@ -229,6 +231,7 @@ bool GDNative::initialize() {
options.core_api_hash = ClassDB::get_api_hash(ClassDB::API_CORE);
options.editor_api_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR);
options.no_api_hash = ClassDB::get_api_hash(ClassDB::API_NONE);
+ options.gd_native_library = (godot_object *)(get_library().ptr());
library_init_fpointer(&options);
diff --git a/modules/gdnative/godot/gdnative.cpp b/modules/gdnative/godot/gdnative.cpp
index 7b94b75a52..29b499ebab 100644
--- a/modules/gdnative/godot/gdnative.cpp
+++ b/modules/gdnative/godot/gdnative.cpp
@@ -33,6 +33,7 @@
#include "error_macros.h"
#include "gdnative.h"
#include "global_constants.h"
+#include "os/os.h"
#include "project_settings.h"
#include "variant.h"
@@ -89,6 +90,10 @@ godot_object GDAPI *godot_global_get_singleton(char *p_name) {
return (godot_object *)ProjectSettings::get_singleton()->get_singleton_object(String(p_name));
} // result shouldn't be freed
+void GDAPI *godot_get_stack_bottom() {
+ return OS::get_singleton()->get_stack_bottom();
+}
+
// MethodBind API
godot_method_bind GDAPI *godot_method_bind_get_method(const char *p_classname, const char *p_methodname) {
diff --git a/modules/gdnative/godot/gdnative.h b/modules/gdnative/godot/gdnative.h
index 4b79706b52..b0343272ef 100644
--- a/modules/gdnative/godot/gdnative.h
+++ b/modules/gdnative/godot/gdnative.h
@@ -245,6 +245,10 @@ void GDAPI godot_object_destroy(godot_object *p_o);
godot_object GDAPI *godot_global_get_singleton(char *p_name); // result shouldn't be freed
+////// OS API
+
+void GDAPI *godot_get_stack_bottom(); // returns stack bottom of the main thread
+
////// MethodBind API
typedef struct {
@@ -261,6 +265,7 @@ typedef struct {
uint64_t core_api_hash;
uint64_t editor_api_hash;
uint64_t no_api_hash;
+ godot_object *gd_native_library; // pointer to GDNativeLibrary that is being initialized
} godot_gdnative_init_options;
typedef struct {
diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp
index 20ac1ecc0c..d180d5aada 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -47,7 +47,8 @@ godot_variant cb_standard_varcall(void *handle, godot_string *p_procedure, godot
Error err = OS::get_singleton()->get_dynamic_library_symbol_handle(
handle,
*(String *)p_procedure,
- library_proc);
+ library_proc,
+ true); // we roll our own message
if (err != OK) {
ERR_PRINT((String("GDNative procedure \"" + *(String *)p_procedure) + "\" does not exists and can't be called").utf8().get_data());
godot_variant ret;
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
index 1e1327d72f..c88889767c 100644
--- a/modules/gdscript/gd_editor.cpp
+++ b/modules/gdscript/gd_editor.cpp
@@ -1334,8 +1334,8 @@ static void _find_identifiers(GDCompletionContext &context, int p_line, bool p_o
static const char *_type_names[Variant::VARIANT_MAX] = {
"null", "bool", "int", "float", "String", "Vector2", "Rect2", "Vector3", "Transform2D", "Plane", "Quat", "AABB", "Basis", "Transform",
- "Color", "NodePath", "RID", "Object", "Dictionary", "Array", "RawArray", "IntArray", "FloatArray", "StringArray",
- "Vector2Array", "Vector3Array", "ColorArray"
+ "Color", "NodePath", "RID", "Object", "Dictionary", "Array", "PoolByteArray", "PoolIntArray", "PoolRealArray", "PoolStringArray",
+ "PoolVector2Array", "PoolVector3Array", "PoolColorArray"
};
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 6e873c9197..36aa249398 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -314,9 +314,10 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool
path += String(tokenizer->get_token_literal());
tokenizer->advance();
need_identifier = false;
+ } else {
+ done = true;
}
- done = true;
break;
}
}
@@ -3283,7 +3284,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
break;
}
- if (tokenizer->is_token_literal(0, true)) {
+ if (!tokenizer->is_token_literal(0, true)) {
_set_error("Expected identifier in signal argument.");
return;
}
diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp
index 95774cb6d6..5803046185 100644
--- a/modules/gdscript/gd_tokenizer.cpp
+++ b/modules/gdscript/gd_tokenizer.cpp
@@ -159,7 +159,7 @@ static const _bit _type_list[] = {
{ Variant::ARRAY, "Array" },
{ Variant::POOL_BYTE_ARRAY, "PoolByteArray" },
{ Variant::POOL_INT_ARRAY, "PoolIntArray" },
- { Variant::POOL_REAL_ARRAY, "PoolFloatArray" },
+ { Variant::POOL_REAL_ARRAY, "PoolRealArray" },
{ Variant::POOL_STRING_ARRAY, "PoolStringArray" },
{ Variant::POOL_VECTOR2_ARRAY, "PoolVector2Array" },
{ Variant::POOL_VECTOR3_ARRAY, "PoolVector3Array" },
@@ -250,7 +250,7 @@ bool GDTokenizer::is_token_literal(int p_offset, bool variable_safe) const {
case TK_BUILT_IN_FUNC:
case TK_OP_IN:
- case TK_OP_NOT:
+ //case TK_OP_NOT:
//case TK_OP_OR:
//case TK_OP_AND:
diff --git a/modules/nativescript/nativescript.cpp b/modules/nativescript/nativescript.cpp
index 493f21d75e..e7445e6da9 100644
--- a/modules/nativescript/nativescript.cpp
+++ b/modules/nativescript/nativescript.cpp
@@ -40,6 +40,10 @@
#include "scene/main/scene_tree.h"
#include "scene/resources/scene_format_text.h"
+#ifndef NO_THREADS
+#include "os/thread.h"
+#endif
+
#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED)
#include "api_generator.h"
#endif
@@ -106,42 +110,16 @@ void NativeScript::set_library(Ref<GDNativeLibrary> p_library) {
return;
}
library = p_library;
-
- // See if this library was "registered" already.
-
lib_path = library->get_active_library_path();
- Map<String, Ref<GDNative> >::Element *E = NSL->library_gdnatives.find(lib_path);
-
- if (!E) {
- Ref<GDNative> gdn;
- gdn.instance();
- gdn->set_library(library);
- // TODO(karroffel): check the return value?
- gdn->initialize();
-
- NSL->library_gdnatives.insert(lib_path, gdn);
-
- NSL->library_classes.insert(lib_path, Map<StringName, NativeScriptDesc>());
-
- if (!NSL->library_script_users.has(lib_path))
- NSL->library_script_users.insert(lib_path, Set<NativeScript *>());
-
- NSL->library_script_users[lib_path].insert(this);
-
- void *args[1] = {
- (void *)&lib_path
- };
-
- // here the library registers all the classes and stuff.
- gdn->call_native_raw(NSL->_init_call_type,
- NSL->_init_call_name,
- NULL,
- 1,
- args,
- NULL);
- } else {
- // already initialized. Nice.
+#ifndef NO_THREADS
+ if (Thread::get_caller_ID() != Thread::get_main_ID()) {
+ NSL->defer_init_library(p_library, this);
+ } else
+#endif
+ {
+ NSL->init_library(p_library);
+ NSL->register_script(this);
}
}
@@ -225,7 +203,21 @@ ScriptInstance *NativeScript::instance_create(Object *p_this) {
nsi->userdata = script_data->create_func.create_func((godot_object *)p_this, script_data->create_func.method_data);
#endif
+#ifndef NO_THREADS
+ owners_lock->lock();
+#endif
+
instance_owners.insert(p_this);
+
+#ifndef NO_THREADS
+ owners_lock->unlock();
+#endif
+
+ // try to call _init
+ // we don't care if it doesn't exist, so we ignore errors.
+ Variant::CallError err;
+ call("_init", NULL, 0, err);
+
return nsi;
}
@@ -296,9 +288,13 @@ ScriptLanguage *NativeScript::get_language() const {
bool NativeScript::has_script_signal(const StringName &p_signal) const {
NativeScriptDesc *script_data = get_script_desc();
- if (!script_data)
- return false;
- return script_data->signals_.has(p_signal);
+
+ while (script_data) {
+ if (script_data->signals_.has(p_signal))
+ return true;
+ script_data = script_data->base_data;
+ }
+ return false;
}
void NativeScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
@@ -415,9 +411,6 @@ Variant NativeScript::_new(const Variant **p_args, int p_argcount, Variant::Call
ref = REF(r);
}
- // GDScript does it like this: _create_instance(p_args, p_argcount, owner, r != NULL, r_error);
- // TODO(karroffel): support varargs for constructors.
-
NativeScriptInstance *instance = (NativeScriptInstance *)instance_create(owner);
owner->set_script_instance(instance);
@@ -441,11 +434,18 @@ NativeScript::NativeScript() {
library = Ref<GDNative>();
lib_path = "";
class_name = "";
+#ifndef NO_THREADS
+ owners_lock = Mutex::create();
+#endif
}
// TODO(karroffel): implement this
NativeScript::~NativeScript() {
- NSL->library_script_users[lib_path].erase(this);
+ NSL->unregister_script(this);
+
+#ifndef NO_THREADS
+ memdelete(owners_lock);
+#endif
}
////// ScriptInstance stuff
@@ -502,7 +502,7 @@ bool NativeScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
if (P) {
godot_variant value;
value = P->get().getter.get_func((godot_object *)owner,
- P->get().setter.method_data,
+ P->get().getter.method_data,
userdata);
r_ret = *(Variant *)&value;
godot_variant_destroy(&value);
@@ -650,6 +650,28 @@ void NativeScriptInstance::notification(int p_notification) {
call_multilevel("_notification", args, 1);
}
+void NativeScriptInstance::refcount_incremented() {
+ Variant::CallError err;
+ call("_refcount_incremented", NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK && err.error != Variant::CallError::CALL_ERROR_INVALID_METHOD) {
+ ERR_PRINT("Failed to invoke _refcount_incremented - should not happen");
+ }
+}
+
+bool NativeScriptInstance::refcount_decremented() {
+ Variant::CallError err;
+ Variant ret = call("_refcount_decremented", NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK && err.error != Variant::CallError::CALL_ERROR_INVALID_METHOD) {
+ ERR_PRINT("Failed to invoke _refcount_decremented - should not happen");
+ return true; // assume we can destroy the object
+ }
+ if (err.error == Variant::CallError::CALL_ERROR_INVALID_METHOD) {
+ // the method does not exist, default is true
+ return true;
+ }
+ return ret;
+}
+
Ref<Script> NativeScriptInstance::get_script() const {
return script;
}
@@ -754,7 +776,16 @@ NativeScriptInstance::~NativeScriptInstance() {
script_data->destroy_func.destroy_func((godot_object *)owner, script_data->destroy_func.method_data, userdata);
if (owner) {
+
+#ifndef NO_THREADS
+ script->owners_lock->lock();
+#endif
+
script->instance_owners.erase(owner);
+
+#ifndef NO_THREADS
+ script->owners_lock->unlock();
+#endif
}
}
@@ -798,6 +829,9 @@ void NativeScriptLanguage::_unload_stuff() {
NativeScriptLanguage::NativeScriptLanguage() {
NativeScriptLanguage::singleton = this;
+#ifndef NO_THREADS
+ mutex = Mutex::create();
+#endif
}
// TODO(karroffel): implement this
@@ -811,6 +845,10 @@ NativeScriptLanguage::~NativeScriptLanguage() {
NSL->library_gdnatives.clear();
NSL->library_script_users.clear();
}
+
+#ifndef NO_THREADS
+ memdelete(mutex);
+#endif
}
String NativeScriptLanguage::get_name() const {
@@ -948,6 +986,134 @@ int NativeScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, in
return -1;
}
+#ifndef NO_THREADS
+void NativeScriptLanguage::defer_init_library(Ref<GDNativeLibrary> lib, NativeScript *script) {
+ MutexLock lock(mutex);
+ libs_to_init.insert(lib);
+ scripts_to_register.insert(script);
+ has_objects_to_register = true;
+}
+#endif
+
+void NativeScriptLanguage::init_library(const Ref<GDNativeLibrary> &lib) {
+#ifndef NO_THREADS
+ MutexLock lock(mutex);
+#endif
+ // See if this library was "registered" already.
+ const String &lib_path = lib->get_active_library_path();
+ Map<String, Ref<GDNative> >::Element *E = library_gdnatives.find(lib_path);
+
+ if (!E) {
+ Ref<GDNative> gdn;
+ gdn.instance();
+ gdn->set_library(lib);
+
+ // TODO(karroffel): check the return value?
+ gdn->initialize();
+
+ library_gdnatives.insert(lib_path, gdn);
+
+ library_classes.insert(lib_path, Map<StringName, NativeScriptDesc>());
+
+ if (!library_script_users.has(lib_path))
+ library_script_users.insert(lib_path, Set<NativeScript *>());
+
+ void *args[1] = {
+ (void *)&lib_path
+ };
+
+ // here the library registers all the classes and stuff.
+ gdn->call_native_raw(_init_call_type,
+ _init_call_name,
+ NULL,
+ 1,
+ args,
+ NULL);
+ } else {
+ // already initialized. Nice.
+ }
+}
+
+void NativeScriptLanguage::register_script(NativeScript *script) {
+#ifndef NO_THREADS
+ MutexLock lock(mutex);
+#endif
+ library_script_users[script->lib_path].insert(script);
+}
+
+void NativeScriptLanguage::unregister_script(NativeScript *script) {
+#ifndef NO_THREADS
+ MutexLock lock(mutex);
+#endif
+ Map<String, Set<NativeScript *> >::Element *S = library_script_users.find(script->lib_path);
+ if (S) {
+ S->get().erase(script);
+ if (S->get().size() == 0) {
+ library_script_users.erase(S);
+ }
+ }
+#ifndef NO_THREADS
+ scripts_to_register.erase(script);
+#endif
+}
+
+#ifndef NO_THREADS
+
+void NativeScriptLanguage::frame() {
+ if (has_objects_to_register) {
+ MutexLock lock(mutex);
+ for (Set<Ref<GDNativeLibrary> >::Element *L = libs_to_init.front(); L; L = L->next()) {
+ init_library(L->get());
+ }
+ libs_to_init.clear();
+ for (Set<NativeScript *>::Element *S = scripts_to_register.front(); S; S = S->next()) {
+ register_script(S->get());
+ }
+ scripts_to_register.clear();
+ has_objects_to_register = false;
+ }
+}
+
+void NativeScriptLanguage::thread_enter() {
+ Vector<Ref<GDNative> > libs;
+ {
+ MutexLock lock(mutex);
+ for (Map<String, Ref<GDNative> >::Element *L = library_gdnatives.front(); L; L = L->next()) {
+ libs.push_back(L->get());
+ }
+ }
+ for (int i = 0; i < libs.size(); ++i) {
+ libs[i]->call_native_raw(
+ _thread_cb_call_type,
+ _thread_enter_call_name,
+ NULL,
+ 0,
+ NULL,
+ NULL);
+ }
+}
+
+void NativeScriptLanguage::thread_exit() {
+ Vector<Ref<GDNative> > libs;
+ {
+ MutexLock lock(mutex);
+ for (Map<String, Ref<GDNative> >::Element *L = library_gdnatives.front(); L; L = L->next()) {
+ libs.push_back(L->get());
+ }
+ }
+ for (int i = 0; i < libs.size(); ++i) {
+ libs[i]->call_native_raw(
+ _thread_cb_call_type,
+ _thread_exit_call_name,
+ NULL,
+ 0,
+ NULL,
+ NULL);
+ }
+}
+
+#endif // NO_THREADS
+
void NativeReloadNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_notification"), &NativeReloadNode::_notification);
}
@@ -958,8 +1124,11 @@ void NativeReloadNode::_notification(int p_what) {
switch (p_what) {
case MainLoop::NOTIFICATION_WM_FOCUS_OUT: {
- print_line("unload");
-
+ if (unloaded)
+ break;
+#ifndef NO_THREADS
+ MutexLock lock(NSL->mutex);
+#endif
NSL->_unload_stuff();
for (Map<String, Ref<GDNative> >::Element *L = NSL->library_gdnatives.front(); L; L = L->next()) {
@@ -967,14 +1136,18 @@ void NativeReloadNode::_notification(int p_what) {
NSL->library_classes.erase(L->key());
}
+ unloaded = true;
+
} break;
case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
- print_line("load");
-
+ if (!unloaded)
+ break;
+#ifndef NO_THREADS
+ MutexLock lock(NSL->mutex);
+#endif
Set<StringName> libs_to_remove;
-
for (Map<String, Ref<GDNative> >::Element *L = NSL->library_gdnatives.front(); L; L = L->next()) {
if (!L->get()->initialize()) {
@@ -1010,6 +1183,8 @@ void NativeReloadNode::_notification(int p_what) {
}
}
+ unloaded = false;
+
for (Set<StringName>::Element *R = libs_to_remove.front(); R; R = R->next()) {
NSL->library_gdnatives.erase(R->get());
}
diff --git a/modules/nativescript/nativescript.h b/modules/nativescript/nativescript.h
index 5702ba2e8d..95b4954171 100644
--- a/modules/nativescript/nativescript.h
+++ b/modules/nativescript/nativescript.h
@@ -41,6 +41,10 @@
#include "godot_nativescript.h"
#include "modules/gdnative/gdnative.h"
+#ifndef NO_THREADS
+#include "os/mutex.h"
+#endif
+
struct NativeScriptDesc {
struct Method {
@@ -102,6 +106,9 @@ class NativeScript : public Script {
String class_name;
+#ifndef NO_THREADS
+ Mutex *owners_lock;
+#endif
Set<Object *> instance_owners;
protected:
@@ -181,6 +188,9 @@ public:
virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
+ virtual void refcount_incremented();
+ virtual bool refcount_decremented();
+
~NativeScriptInstance();
};
@@ -197,6 +207,19 @@ private:
void _unload_stuff();
+#ifndef NO_THREADS
+ Mutex *mutex;
+
+ Set<Ref<GDNativeLibrary> > libs_to_init;
+ Set<NativeScript *> scripts_to_register;
+ volatile bool has_objects_to_register; // so that we don't lock mutex every frame - it's rarely needed
+ void defer_init_library(Ref<GDNativeLibrary> lib, NativeScript *script);
+#endif
+
+ void init_library(const Ref<GDNativeLibrary> &lib);
+ void register_script(NativeScript *script);
+ void unregister_script(NativeScript *script);
+
public:
Map<String, Map<StringName, NativeScriptDesc> > library_classes;
Map<String, Ref<GDNative> > library_gdnatives;
@@ -206,6 +229,10 @@ public:
const StringName _init_call_type = "nativescript_init";
const StringName _init_call_name = "godot_nativescript_init";
+ const StringName _thread_cb_call_type = "godot_nativescript_thread_cb";
+ const StringName _thread_enter_call_name = "godot_nativescript_thread_enter";
+ const StringName _thread_exit_call_name = "godot_nativescript_thread_exit";
+
NativeScriptLanguage();
~NativeScriptLanguage();
@@ -215,6 +242,13 @@ public:
void _hacky_api_anchor();
+#ifndef NO_THREADS
+ virtual void thread_enter();
+ virtual void thread_exit();
+
+ virtual void frame();
+#endif
+
virtual String get_name() const;
virtual void init();
virtual String get_type() const;
@@ -259,6 +293,8 @@ inline NativeScriptDesc *NativeScript::get_script_desc() const {
class NativeReloadNode : public Node {
GDCLASS(NativeReloadNode, Node)
+ bool unloaded = false;
+
public:
static void _bind_methods();
void _notification(int p_what);
diff --git a/modules/nativescript/register_types.cpp b/modules/nativescript/register_types.cpp
index 6c88b04a56..dfa16d8a2a 100644
--- a/modules/nativescript/register_types.cpp
+++ b/modules/nativescript/register_types.cpp
@@ -50,7 +50,8 @@ void init_call_cb(void *p_handle, godot_string *p_proc_name, void *p_data, int p
Error err = OS::get_singleton()->get_dynamic_library_symbol_handle(
p_handle,
*(String *)p_proc_name,
- library_proc);
+ library_proc,
+ true); // we print our own message
if (err != OK) {
ERR_PRINT((String("GDNative procedure \"" + *(String *)p_proc_name) + "\" does not exists and can't be called").utf8().get_data());
return;
@@ -61,6 +62,33 @@ void init_call_cb(void *p_handle, godot_string *p_proc_name, void *p_data, int p
fn(args[0]);
}
+#ifndef NO_THREADS
+
+typedef void (*native_script_empty_callback)();
+
+void thread_call_cb(void *p_handle, godot_string *p_proc_name, void *p_data, int p_num_args, void **args, void *r_ret) {
+ if (p_handle == NULL) {
+ ERR_PRINT("No valid library handle, can't call nativescript thread enter/exit callback");
+ return;
+ }
+
+ void *library_proc;
+ Error err = OS::get_singleton()->get_dynamic_library_symbol_handle(
+ p_handle,
+ *(String *)p_proc_name,
+ library_proc,
+ true);
+ if (err != OK) {
+ // it's fine if thread callbacks are not present in the library.
+ return;
+ }
+
+ native_script_empty_callback fn = (native_script_empty_callback)library_proc;
+ fn();
+}
+
+#endif // NO_THREADS
+
ResourceFormatLoaderNativeScript *resource_loader_gdns = NULL;
ResourceFormatSaverNativeScript *resource_saver_gdns = NULL;
@@ -72,6 +100,9 @@ void register_nativescript_types() {
ScriptServer::register_language(native_script_language);
GDNativeCallRegistry::singleton->register_native_raw_call_type(native_script_language->_init_call_type, init_call_cb);
+#ifndef NO_THREADS
+ GDNativeCallRegistry::singleton->register_native_raw_call_type(native_script_language->_thread_cb_call_type, thread_call_cb);
+#endif
resource_saver_gdns = memnew(ResourceFormatSaverNativeScript);
ResourceSaver::add_resource_format_saver(resource_saver_gdns);
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
index c645a55703..7b8b2abebb 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
@@ -39,7 +39,7 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra
int todo = p_frames;
- while (todo) {
+ while (todo && active) {
int mixed = stb_vorbis_get_samples_float_interleaved(ogg_stream, 2, (float *)p_buffer, todo * 2);
todo -= mixed;
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index ba3463445d..35358d5a1f 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -2328,6 +2328,16 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot,
undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, p_from.to_int(), from_port, p_to.to_int());
undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int());
} else {
+
+ // disconect current, and connect the new one
+ if (script->is_input_value_port_connected(edited_func, p_to.to_int(), to_port)) {
+ int conn_from;
+ int conn_port;
+ script->get_input_value_port_connection_source(edited_func, p_to.to_int(), to_port, &conn_from, &conn_port);
+ undo_redo->add_do_method(script.ptr(), "data_disconnect", edited_func, conn_from, conn_port, p_to.to_int(), to_port);
+ undo_redo->add_undo_method(script.ptr(), "data_connect", edited_func, conn_from, conn_port, p_to.to_int(), to_port);
+ }
+
undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port);
undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port);
//update nodes in sgraph