소스 검색

[DLScript] in-editor reloading

Karroffel 8 년 전
부모
커밋
5695d9892e
2개의 변경된 파일194개의 추가작업 그리고 62개의 파일을 삭제
  1. 169 54
      modules/dlscript/dl_script.cpp
  2. 25 8
      modules/dlscript/dl_script.h

+ 169 - 54
modules/dlscript/dl_script.cpp

@@ -41,6 +41,10 @@
 #include "api_generator.h"
 #endif
 
+#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
+#endif
+
 Error NativeLibrary::initialize(NativeLibrary *&p_native_lib, const StringName p_path) {
 
 	if (DLScriptLanguage::get_singleton()->initialized_libraries.has(p_path)) {
@@ -115,6 +119,29 @@ Error NativeLibrary::terminate(NativeLibrary *&p_native_lib) {
 }
 
 // Script
+#ifdef TOOLS_ENABLED
+
+void DLScript::_update_placeholder(PlaceHolderScriptInstance *p_placeholder) {
+
+	List<PropertyInfo> pinfo;
+	Map<StringName, Variant> values;
+
+	for (Map<StringName, DLScriptData::Property>::Element *E = script_data->properties.front(); E; E = E->next()) {
+		PropertyInfo p = E->get().info;
+		p.name = String(E->key());
+		pinfo.push_back(p);
+		values[p.name] = E->get().default_value;
+	}
+
+	p_placeholder->update(pinfo, values);
+}
+
+void DLScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
+
+	placeholders.erase(p_placeholder);
+}
+
+#endif
 
 bool DLScript::can_instance() const {
 #ifdef DLSCRIPT_EDITOR_FEATURES
@@ -161,13 +188,10 @@ ScriptInstance *DLScript::instance_create(Object *p_this) {
 		PlaceHolderScriptInstance *sins = memnew(PlaceHolderScriptInstance(DLScriptLanguage::singleton, Ref<Script>((Script *)this), p_this));
 		placeholders.insert(sins);
 
-		List<PropertyInfo> pinfo;
-		Map<StringName, Variant> values;
-
 		if (!library.is_valid())
 			return sins;
 
-		if (!library->library) {
+		if (!library->native_library) {
 			Error err = library->_initialize();
 			if (err != OK) {
 				return sins;
@@ -177,20 +201,11 @@ ScriptInstance *DLScript::instance_create(Object *p_this) {
 		if (!script_data) {
 			script_data = library->get_script_data(script_name);
 		}
-		if (script_data)
+		if (script_data && script_data->create_func.create_func) {
 			script_data->create_func.create_func((godot_object *)p_this, script_data->create_func.method_data);
-
-		if (script_data) {
-			for (Map<StringName, DLScriptData::Property>::Element *E = script_data->properties.front(); E; E = E->next()) {
-
-				PropertyInfo p = E->get().info;
-				p.name = String(E->key());
-				pinfo.push_back(p);
-				values[p.name] = E->get().default_value;
-			}
 		}
 
-		sins->update(pinfo, values);
+		_update_placeholder(sins);
 
 		return sins;
 	}
@@ -393,7 +408,7 @@ void DLScript::set_library(Ref<DLLibrary> p_library) {
 		Error initalize_status = library->_initialize();
 		ERR_FAIL_COND(initalize_status != OK);
 		if (script_name) {
-			script_data = library->library->scripts[script_name];
+			script_data = library->native_library->scripts[script_name];
 			ERR_FAIL_COND(!script_data);
 		}
 	}
@@ -408,11 +423,11 @@ void DLScript::set_script_name(StringName p_script_name) {
 
 	if (library.is_valid()) {
 #ifdef DLSCRIPT_EDITOR_FEATURES
-		if (!library->library) {
+		if (!library->native_library) {
 			library->_initialize();
 		}
 #endif
-		if (library->library) {
+		if (library->native_library) {
 			script_data = library->get_script_data(script_name);
 			ERR_FAIL_COND(!script_data);
 		}
@@ -431,10 +446,12 @@ void DLScript::_bind_methods() {
 
 DLScript::DLScript() {
 	script_data = NULL;
+	DLScriptLanguage::get_singleton()->script_list.insert(this);
 }
 
 DLScript::~DLScript() {
 	//hmm
+	DLScriptLanguage::get_singleton()->script_list.erase(this);
 }
 
 // Library
@@ -513,7 +530,8 @@ Error DLLibrary::_initialize() {
 
 	DLLibrary::currently_initialized_library = this;
 
-	Error ret = NativeLibrary::initialize(library, path);
+	Error ret = NativeLibrary::initialize(native_library, path);
+	native_library->dllib = this;
 
 	DLLibrary::currently_initialized_library = NULL;
 
@@ -521,21 +539,49 @@ Error DLLibrary::_initialize() {
 }
 
 Error DLLibrary::_terminate() {
-	ERR_FAIL_COND_V(!library, ERR_BUG);
-	ERR_FAIL_COND_V(!library->handle, ERR_BUG);
+	ERR_FAIL_COND_V(!native_library, ERR_BUG);
+	ERR_FAIL_COND_V(!native_library->handle, ERR_BUG);
+
+	// de-init stuff
 
-	return NativeLibrary::terminate(library);
+	for (Map<StringName, DLScriptData *>::Element *E = native_library->scripts.front(); E; E = E->next()) {
+		for (Map<StringName, DLScriptData::Method>::Element *M = E->get()->methods.front(); M; M = M->next()) {
+			if (M->get().method.free_func) {
+				M->get().method.free_func(M->get().method.method_data);
+			}
+		}
+		if (E->get()->create_func.free_func) {
+			E->get()->create_func.free_func(E->get()->create_func.method_data);
+		}
+		if (E->get()->destroy_func.free_func) {
+			E->get()->destroy_func.free_func(E->get()->destroy_func.method_data);
+		}
+
+		for (Set<DLScript *>::Element *S = DLScriptLanguage::get_singleton()->script_list.front(); S; S = S->next()) {
+			if (S->get()->script_data == E->get()) {
+				S->get()->script_data = NULL;
+			}
+		}
+
+		memdelete(E->get());
+	}
+
+	Error ret = NativeLibrary::terminate(native_library);
+
+	native_library->scripts.clear();
+
+	return ret;
 }
 
 void DLLibrary::_register_script(const StringName p_name, const StringName p_base, godot_instance_create_func p_instance_func, godot_instance_destroy_func p_destroy_func) {
-	ERR_FAIL_COND(!library);
-	ERR_FAIL_COND(library->scripts.has(p_name));
+	ERR_FAIL_COND(!native_library);
+	ERR_FAIL_COND(native_library->scripts.has(p_name));
 
 	DLScriptData *s = memnew(DLScriptData);
 	s->base = p_base;
 	s->create_func = p_instance_func;
 	s->destroy_func = p_destroy_func;
-	Map<StringName, DLScriptData *>::Element *E = library->scripts.find(p_base);
+	Map<StringName, DLScriptData *>::Element *E = native_library->scripts.find(p_base);
 	if (E) {
 		s->base_data = E->get();
 		s->base_native_type = s->base_data->base_native_type;
@@ -548,19 +594,19 @@ void DLLibrary::_register_script(const StringName p_name, const StringName p_bas
 		s->base_native_type = p_base;
 	}
 
-	library->scripts.insert(p_name, s);
+	native_library->scripts.insert(p_name, s);
 }
 
 void DLLibrary::_register_tool_script(const StringName p_name, const StringName p_base, godot_instance_create_func p_instance_func, godot_instance_destroy_func p_destroy_func) {
-	ERR_FAIL_COND(!library);
-	ERR_FAIL_COND(library->scripts.has(p_name));
+	ERR_FAIL_COND(!native_library);
+	ERR_FAIL_COND(native_library->scripts.has(p_name));
 
 	DLScriptData *s = memnew(DLScriptData);
 	s->base = p_base;
 	s->create_func = p_instance_func;
 	s->destroy_func = p_destroy_func;
 	s->is_tool = true;
-	Map<StringName, DLScriptData *>::Element *E = library->scripts.find(p_base);
+	Map<StringName, DLScriptData *>::Element *E = native_library->scripts.find(p_base);
 	if (E) {
 		s->base_data = E->get();
 		s->base_native_type = s->base_data->base_native_type;
@@ -573,24 +619,24 @@ void DLLibrary::_register_tool_script(const StringName p_name, const StringName
 		s->base_native_type = p_base;
 	}
 
-	library->scripts.insert(p_name, s);
+	native_library->scripts.insert(p_name, s);
 }
 
 void DLLibrary::_register_script_method(const StringName p_name, const StringName p_method, godot_method_attributes p_attr, godot_instance_method p_func, MethodInfo p_info) {
-	ERR_FAIL_COND(!library);
-	ERR_FAIL_COND(!library->scripts.has(p_name));
+	ERR_FAIL_COND(!native_library);
+	ERR_FAIL_COND(!native_library->scripts.has(p_name));
 
 	p_info.name = p_method;
 	DLScriptData::Method method;
 
 	method = DLScriptData::Method(p_func, p_info, p_attr.rpc_type);
 
-	library->scripts[p_name]->methods.insert(p_method, method);
+	native_library->scripts[p_name]->methods.insert(p_method, method);
 }
 
 void DLLibrary::_register_script_property(const StringName p_name, const String p_path, godot_property_attributes *p_attr, godot_property_set_func p_setter, godot_property_get_func p_getter) {
-	ERR_FAIL_COND(!library);
-	ERR_FAIL_COND(!library->scripts.has(p_name));
+	ERR_FAIL_COND(!native_library);
+	ERR_FAIL_COND(!native_library->scripts.has(p_name));
 
 	DLScriptData::Property p;
 
@@ -603,12 +649,12 @@ void DLLibrary::_register_script_property(const StringName p_name, const String
 		p = DLScriptData::Property(p_setter, p_getter, pi, *(Variant *)&p_attr->default_value, p_attr->rset_type);
 	}
 
-	library->scripts[p_name]->properties.insert(p_path, p);
+	native_library->scripts[p_name]->properties.insert(p_path, p);
 }
 
 void DLLibrary::_register_script_signal(const StringName p_name, const godot_signal *p_signal) {
-	ERR_FAIL_COND(!library);
-	ERR_FAIL_COND(!library->scripts.has(p_name));
+	ERR_FAIL_COND(!native_library);
+	ERR_FAIL_COND(!native_library->scripts.has(p_name));
 	ERR_FAIL_COND(!p_signal);
 
 	DLScriptData::Signal signal;
@@ -648,15 +694,15 @@ void DLLibrary::_register_script_signal(const StringName p_name, const godot_sig
 		signal.signal.default_arguments = default_arguments;
 	}
 
-	library->scripts[p_name]->signals_.insert(*(String *)&p_signal->name, signal);
+	native_library->scripts[p_name]->signals_.insert(*(String *)&p_signal->name, signal);
 }
 
 DLScriptData *DLLibrary::get_script_data(const StringName p_name) {
-	ERR_FAIL_COND_V(!library, NULL);
+	ERR_FAIL_COND_V(!native_library, NULL);
 
-	ERR_FAIL_COND_V(!library->scripts.has(p_name), NULL);
+	ERR_FAIL_COND_V(!native_library->scripts.has(p_name), NULL);
 
-	return library->scripts[p_name];
+	return native_library->scripts[p_name];
 }
 
 bool DLLibrary::_set(const StringName &p_name, const Variant &p_value) {
@@ -732,25 +778,16 @@ void DLLibrary::_bind_methods() {
 }
 
 DLLibrary::DLLibrary() {
-	library = NULL;
+	native_library = NULL;
 }
 
 DLLibrary::~DLLibrary() {
 
-	if (!library) {
+	if (!native_library) {
 		return;
 	}
 
-	for (Map<StringName, DLScriptData *>::Element *E = library->scripts.front(); E; E = E->next()) {
-		for (Map<StringName, DLScriptData::Method>::Element *M = E->get()->methods.front(); M; M = M->next()) {
-			if (M->get().method.free_func) {
-				M->get().method.free_func(M->get().method.method_data);
-			}
-		}
-		memdelete(E->get());
-	}
-
-	if (library->handle) {
+	if (native_library->handle) {
 		_terminate();
 	}
 }
@@ -927,6 +964,13 @@ String DLScriptLanguage::get_name() const {
 	return "DLScript";
 }
 
+void _add_reload_node() {
+#ifdef TOOLS_ENABLED
+	DLReloadNode *rn = memnew(DLReloadNode);
+	EditorNode::get_singleton()->add_child(rn);
+#endif
+}
+
 void DLScriptLanguage::init() {
 	// TODO: Expose globals
 	GLOBAL_DEF("dlscript/default_dllibrary", "");
@@ -946,6 +990,12 @@ void DLScriptLanguage::init() {
 		}
 	}
 #endif
+
+#ifdef TOOLS_ENABLED
+	// if (SceneTree::get_singleton()->is_editor_hint()) {
+	EditorNode::add_init_callback(&_add_reload_node);
+// }
+#endif
 }
 
 String DLScriptLanguage::get_type() const {
@@ -1087,6 +1137,71 @@ DLScriptLanguage::~DLScriptLanguage() {
 	singleton = NULL;
 }
 
+// DLReloadNode
+
+void DLReloadNode::_bind_methods() {
+	ClassDB::bind_method("_notification", &DLReloadNode::_notification);
+}
+
+void DLReloadNode::_notification(int p_what) {
+#ifdef TOOLS_ENABLED
+
+	switch (p_what) {
+		case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
+
+			// break; // For now.
+
+			Set<NativeLibrary *> libs_to_reload;
+
+			for (Map<StringName, NativeLibrary *>::Element *L = DLScriptLanguage::get_singleton()->initialized_libraries.front(); L; L = L->next()) {
+				// check if file got modified at all
+				// @Todo
+
+				libs_to_reload.insert(L->get());
+			}
+
+			for (Set<NativeLibrary *>::Element *L = libs_to_reload.front(); L; L = L->next()) {
+
+				DLLibrary *lib = L->get()->dllib;
+
+				lib->_terminate();
+				lib->_initialize();
+
+				// update placeholders (if any)
+
+				DLScript *script = NULL;
+
+				for (Set<DLScript *>::Element *S = DLScriptLanguage::get_singleton()->script_list.front(); S; S = S->next()) {
+					if (lib->native_library->scripts.has(S->get()->get_script_name())) {
+						script = S->get();
+						script->script_data = lib->get_script_data(script->get_script_name());
+						break;
+					}
+				}
+
+				if (script == NULL) {
+					// new class, cool. Nothing to do here
+					continue;
+				}
+
+				if (script->placeholders.size() == 0)
+					continue;
+
+				for (Set<PlaceHolderScriptInstance *>::Element *P = script->placeholders.front(); P; P = P->next()) {
+					PlaceHolderScriptInstance *p = P->get();
+					script->_update_placeholder(p);
+				}
+			}
+
+		} break;
+		default: {
+		};
+	}
+#endif
+}
+
+// Resource loader/saver
+
 RES ResourceFormatLoaderDLScript::load(const String &p_path, const String &p_original_path, Error *r_error) {
 	ResourceFormatLoaderText rsflt;
 	return rsflt.load(p_path, p_original_path, r_error);

+ 25 - 8
modules/dlscript/dl_script.h

@@ -33,6 +33,7 @@
 #include "io/resource_saver.h"
 #include "os/thread_safe.h"
 #include "resource.h"
+#include "scene/main/node.h"
 #include "script_language.h"
 #include "self_list.h"
 
@@ -43,11 +44,14 @@
 #endif
 
 class DLScriptData;
+class DLLibrary;
 
 struct NativeLibrary {
 	StringName path;
 	void *handle;
 
+	DLLibrary *dllib;
+
 	Map<StringName, DLScriptData *> scripts;
 
 	static Error initialize(NativeLibrary *&p_native_lib, const StringName p_path);
@@ -129,7 +133,6 @@ struct DLScriptData {
 class DLLibrary;
 
 class DLScript : public Script {
-
 	GDCLASS(DLScript, Script);
 
 	Ref<DLLibrary> library;
@@ -140,12 +143,14 @@ class DLScript : public Script {
 
 #ifdef TOOLS_ENABLED
 	Set<PlaceHolderScriptInstance *> placeholders;
-// void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
-// virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
+	void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
+	virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
 #endif
 
 	friend class DLInstance;
 	friend class DLScriptLanguage;
+	friend class DLReloadNode;
+	friend class DLLibrary;
 
 protected:
 	static void _bind_methods();
@@ -199,18 +204,16 @@ class DLLibrary : public Resource {
 	OBJ_SAVE_TYPE(DLLibrary);
 
 	Map<StringName, String> platform_files;
-	NativeLibrary *library;
+	NativeLibrary *native_library;
 	static DLLibrary *currently_initialized_library;
 
 protected:
 	friend class DLScript;
 	friend class NativeLibrary;
+	friend class DLReloadNode;
 
 	DLScriptData *get_script_data(const StringName p_name);
 
-	Error _initialize();
-	Error _terminate();
-
 	bool _set(const StringName &p_name, const Variant &p_value);
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	void _get_property_list(List<PropertyInfo> *p_list) const;
@@ -218,6 +221,9 @@ protected:
 	static void _bind_methods();
 
 public:
+	Error _initialize();
+	Error _terminate();
+
 	static DLLibrary *get_currently_initialized_library();
 
 	void _register_script(const StringName p_name, const StringName p_base, godot_instance_create_func p_instance_func, godot_instance_destroy_func p_destroy_func);
@@ -277,9 +283,13 @@ public:
 	~DLInstance();
 };
 
+class DLReloadNode;
+
 class DLScriptLanguage : public ScriptLanguage {
 	friend class DLScript;
 	friend class DLInstance;
+	friend class DLReloadNode;
+	friend class DLLibrary;
 
 	static DLScriptLanguage *singleton;
 
@@ -292,7 +302,7 @@ class DLScriptLanguage : public ScriptLanguage {
 
 	Mutex *lock;
 
-	SelfList<DLScript>::List script_list;
+	Set<DLScript *> script_list;
 
 	bool profiling;
 	uint64_t script_frame_time;
@@ -384,6 +394,13 @@ public:
 	~DLScriptLanguage();
 };
 
+class DLReloadNode : public Node {
+	GDCLASS(DLReloadNode, Node)
+public:
+	static void _bind_methods();
+	void _notification(int p_what);
+};
+
 class ResourceFormatLoaderDLScript : public ResourceFormatLoader {
 public:
 	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);