diff options
-rw-r--r-- | core/script_debugger_remote.cpp | 14 | ||||
-rw-r--r-- | core/script_debugger_remote.h | 2 | ||||
-rw-r--r-- | core/script_language.cpp | 2 | ||||
-rw-r--r-- | core/script_language.h | 7 | ||||
-rw-r--r-- | modules/gdscript/gd_compiler.cpp | 76 | ||||
-rw-r--r-- | modules/gdscript/gd_compiler.h | 4 | ||||
-rw-r--r-- | modules/gdscript/gd_script.cpp | 125 | ||||
-rw-r--r-- | modules/gdscript/gd_script.h | 16 | ||||
-rw-r--r-- | tools/editor/editor_node.cpp | 6 | ||||
-rw-r--r-- | tools/editor/editor_node.h | 1 | ||||
-rw-r--r-- | tools/editor/script_editor_debugger.cpp | 10 | ||||
-rw-r--r-- | tools/editor/script_editor_debugger.h | 2 |
12 files changed, 247 insertions, 18 deletions
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index 011b1f78a0..99d1e22c07 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -291,6 +291,8 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script,bool p_can_continue) { _set_object_property(cmd[1],cmd[2],cmd[3]); + } else if (command=="reload_scripts") { + reload_all_scripts=true; } else if (command=="breakpoint") { bool set = cmd[3]; @@ -698,7 +700,8 @@ void ScriptDebuggerRemote::_poll_events() { profiling=false; _send_profiling_data(false); print_line("PROFILING END!"); - + } else if (command=="reload_scripts") { + reload_all_scripts=true; } else if (command=="breakpoint") { bool set = cmd[3]; @@ -863,6 +866,14 @@ void ScriptDebuggerRemote::idle_poll() { } } + if (reload_all_scripts) { + + for(int i=0;i<ScriptServer::get_language_count();i++) { + ScriptServer::get_language(i)->reload_all_scripts(); + } + reload_all_scripts=false; + } + _poll_events(); } @@ -1012,6 +1023,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() { profile_info_ptrs.resize(profile_info.size()); profiling=false; max_frame_functions=16; + reload_all_scripts=false; } diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h index 8fdab47013..c6a00e189f 100644 --- a/core/script_debugger_remote.h +++ b/core/script_debugger_remote.h @@ -60,6 +60,7 @@ class ScriptDebuggerRemote : public ScriptDebugger { bool profiling; int max_frame_functions; bool skip_profile_frame; + bool reload_all_scripts; Ref<StreamPeerTCP> tcp_client; @@ -168,6 +169,7 @@ public: virtual void profiling_end(); virtual void profiling_set_frame_times(float p_frame_time,float p_idle_time,float p_fixed_time,float p_fixed_frame_time); + ScriptDebuggerRemote(); ~ScriptDebuggerRemote(); }; diff --git a/core/script_language.cpp b/core/script_language.cpp index 1503418269..b3116a0297 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -51,7 +51,7 @@ void Script::_bind_methods() { ObjectTypeDB::bind_method(_MD("has_source_code"),&Script::has_source_code); ObjectTypeDB::bind_method(_MD("get_source_code"),&Script::get_source_code); ObjectTypeDB::bind_method(_MD("set_source_code","source"),&Script::set_source_code); - ObjectTypeDB::bind_method(_MD("reload"),&Script::reload); + ObjectTypeDB::bind_method(_MD("reload","keep_state"),&Script::reload,DEFVAL(false)); } diff --git a/core/script_language.h b/core/script_language.h index bd76107acf..d8b4c61b6c 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -87,7 +87,7 @@ public: virtual bool has_source_code() const=0; virtual String get_source_code() const=0; virtual void set_source_code(const String& p_code)=0; - virtual Error reload()=0; + virtual Error reload(bool p_keep_state=false)=0; virtual bool is_tool() const=0; @@ -127,6 +127,8 @@ public: virtual Ref<Script> get_script() const=0; + virtual bool is_placeholder() const { return false; } + virtual ScriptLanguage *get_language()=0; virtual ~ScriptInstance(); }; @@ -189,6 +191,7 @@ public: virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); } + virtual void reload_all_scripts()=0; /* LOADER FUNCTIONS */ virtual void get_recognized_extensions(List<String> *p_extensions) const=0; @@ -248,6 +251,8 @@ public: void update(const List<PropertyInfo> &p_properties,const Map<StringName,Variant>& p_values); //likely changed in editor + virtual bool is_placeholder() const { return true; } + PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script,Object *p_owner); ~PlaceHolderScriptInstance(); diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index 5a6299bcf8..7a2e9c9638 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -1414,8 +1414,13 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * -Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class) { +Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDParser::ClassNode *p_class, bool p_keep_state) { + Map<StringName,Ref<GDScript> > old_subclasses; + + if (p_keep_state) { + old_subclasses=p_script->subclasses; + } p_script->native=Ref<GDNativeClass>(); p_script->base=Ref<GDScript>(); @@ -1429,6 +1434,7 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars p_script->member_indices.clear(); p_script->member_info.clear(); p_script->initializer=NULL; + p_script->subclasses.clear(); p_script->_owner=p_owner; p_script->tool=p_class->tool; @@ -1662,9 +1668,15 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars for(int i=0;i<p_class->subclasses.size();i++) { StringName name = p_class->subclasses[i]->name; - Ref<GDScript> subclass = memnew( GDScript ); + Ref<GDScript> subclass; + + if (old_subclasses.has(name)) { + subclass=old_subclasses[name]; + } else { + subclass.instance(); + } - Error err = _parse_class(subclass.ptr(),p_script,p_class->subclasses[i]); + Error err = _parse_class(subclass.ptr(),p_script,p_class->subclasses[i],p_keep_state); if (err) return err; @@ -1755,13 +1767,65 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars } } + + //validate instances if keeping state + + if (p_keep_state) { + + print_line("RELOAD KEEP "+p_script->path); + for (Set<Object*>::Element *E=p_script->instances.front();E;) { + + Set<Object*>::Element *N = E->next(); + + ScriptInstance *si = E->get()->get_script_instance(); + if (si->is_placeholder()) { + PlaceHolderScriptInstance *psi = static_cast<PlaceHolderScriptInstance*>(si); + + if (p_script->is_tool()) { + //re-create as an instance + p_script->placeholders.erase(psi); //remove placeholder + + GDInstance* instance = memnew( GDInstance ); + instance->base_ref=E->get()->cast_to<Reference>(); + instance->members.resize(p_script->member_indices.size()); + instance->script=Ref<GDScript>(p_script); + instance->owner=E->get(); + + //needed for hot reloading + for(Map<StringName,GDScript::MemberInfo>::Element *E=p_script->member_indices.front();E;E=E->next()) { + instance->member_indices_cache[E->key()]=E->get().index; + } + instance->owner->set_script_instance(instance); + + + /* STEP 2, INITIALIZE AND CONSRTUCT */ + + Variant::CallError ce; + p_script->initializer->call(instance,NULL,0,ce); + + if (ce.error!=Variant::CallError::CALL_OK) { + //well, tough luck, not goinna do anything here + } + } + } else { + print_line("RELOAD MEMBERS"); + GDInstance *gi = static_cast<GDInstance*>(si); + gi->reload_members(); + } + + E=N; + + } + + + } #endif p_script->valid=true; return OK; } -Error GDCompiler::compile(const GDParser *p_parser,GDScript *p_script) { +Error GDCompiler::compile(const GDParser *p_parser,GDScript *p_script,bool p_keep_state) { err_line=-1; err_column=-1; @@ -1772,9 +1836,7 @@ Error GDCompiler::compile(const GDParser *p_parser,GDScript *p_script) { source=p_script->get_path(); - - - Error err = _parse_class(p_script,NULL,static_cast<const GDParser::ClassNode*>(root)); + Error err = _parse_class(p_script,NULL,static_cast<const GDParser::ClassNode*>(root),p_keep_state); if (err) return err; diff --git a/modules/gdscript/gd_compiler.h b/modules/gdscript/gd_compiler.h index 32e18c6dcf..7cf575e3d6 100644 --- a/modules/gdscript/gd_compiler.h +++ b/modules/gdscript/gd_compiler.h @@ -144,7 +144,7 @@ class GDCompiler { int _parse_expression(CodeGen& codegen,const GDParser::Node *p_expression, int p_stack_level,bool p_root=false,bool p_initializer=false); Error _parse_block(CodeGen& codegen,const GDParser::BlockNode *p_block,int p_stack_level=0,int p_break_addr=-1,int p_continue_addr=-1); Error _parse_function(GDScript *p_script,const GDParser::ClassNode *p_class,const GDParser::FunctionNode *p_func,bool p_for_ready=false); - Error _parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class); + Error _parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class,bool p_keep_state); int err_line; int err_column; StringName source; @@ -152,7 +152,7 @@ class GDCompiler { public: - Error compile(const GDParser *p_parser,GDScript *p_script); + Error compile(const GDParser *p_parser, GDScript *p_script, bool p_keep_state=false); String get_error() const; int get_error_line() const; diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index 7672faf620..dcd0641f76 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -101,6 +101,12 @@ GDInstance* GDScript::_create_instance(const Variant** p_args,int p_argcount,Obj instance->members.resize(member_indices.size()); instance->script=Ref<GDScript>(this); instance->owner=p_owner; +#ifdef DEBUG_ENABLED + //needed for hot reloading + for(Map<StringName,MemberInfo>::Element *E=member_indices.front();E;E=E->next()) { + instance->member_indices_cache[E->key()]=E->get().index; + } +#endif instance->owner->set_script_instance(instance); /* STEP 2, INITIALIZE AND CONSRTUCT */ @@ -500,10 +506,10 @@ void GDScript::_set_subclass_path(Ref<GDScript>& p_sc,const String& p_path) { } } -Error GDScript::reload() { +Error GDScript::reload(bool p_keep_state) { - ERR_FAIL_COND_V(instances.size(),ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_keep_state && instances.size(),ERR_ALREADY_IN_USE); String basedir=path; @@ -531,7 +537,7 @@ Error GDScript::reload() { bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool_script(); GDCompiler compiler; - err = compiler.compile(&parser,this); + err = compiler.compile(&parser,this,p_keep_state); if (err) { @@ -837,7 +843,7 @@ void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { } -GDScript::GDScript() { +GDScript::GDScript() : script_list(this) { _static_ref=this; @@ -851,12 +857,33 @@ GDScript::GDScript() { source_changed_cache=false; #endif +#ifdef DEBUG_ENABLED + if (GDScriptLanguage::get_singleton()->lock) { + GDScriptLanguage::get_singleton()->lock->lock(); + } + GDScriptLanguage::get_singleton()->script_list.add(&script_list); + + if (GDScriptLanguage::get_singleton()->lock) { + GDScriptLanguage::get_singleton()->lock->unlock(); + } +#endif } GDScript::~GDScript() { for (Map<StringName,GDFunction*>::Element *E=member_functions.front();E;E=E->next()) { memdelete( E->get() ); } + +#ifdef DEBUG_ENABLED + if (GDScriptLanguage::get_singleton()->lock) { + GDScriptLanguage::get_singleton()->lock->lock(); + } + GDScriptLanguage::get_singleton()->script_list.remove(&script_list); + + if (GDScriptLanguage::get_singleton()->lock) { + GDScriptLanguage::get_singleton()->lock->unlock(); + } +#endif } @@ -1218,6 +1245,37 @@ ScriptLanguage *GDInstance::get_language() { return GDScriptLanguage::get_singleton(); } +void GDInstance::reload_members() { + +#ifdef DEBUG_ENABLED + + members.resize(script->member_indices.size()); //resize + + Vector<Variant> new_members; + new_members.resize(script->member_indices.size()); + + //pass the values to the new indices + for(Map<StringName,GDScript::MemberInfo>::Element *E=script->member_indices.front();E;E=E->next()) { + + if (member_indices_cache.has(E->key())) { + Variant value = members[member_indices_cache[E->key()]]; + new_members[E->get().index]=value; + } + + } + + //apply + members=new_members; + + //pass the values to the new indices + member_indices_cache.clear(); + for(Map<StringName,GDScript::MemberInfo>::Element *E=script->member_indices.front();E;E=E->next()) { + + member_indices_cache[E->key()]=E->get().index; + } + +#endif +} GDInstance::GDInstance() { owner=NULL; @@ -1441,6 +1499,65 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr,int p_i } +struct GDScriptDepSort { + + //must support sorting so inheritance works properly (parent must be reloaded first) + bool operator()(const Ref<GDScript> &A, const Ref<GDScript>& B) const { + + if (A==B) + return false; //shouldn't happen but.. + const GDScript *I=B->get_base().ptr(); + while(I) { + if (I==A.ptr()) { + // A is a base of B + return true; + } + + I=I->get_base().ptr(); + } + + return false; //not a base + } +}; + +void GDScriptLanguage::reload_all_scripts() { + + + +#ifdef DEBUG_ENABLED + print_line("RELOAD ALL SCRIPTS"); + if (lock) { + lock->lock(); + } + + List<Ref<GDScript> > scripts; + + SelfList<GDScript> *elem=script_list.first(); + while(elem) { + if (elem->self()->get_path().is_resource_file()) { + print_line("FOUND: "+elem->self()->get_path()); + scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident + } + elem=elem->next(); + } + + if (lock) { + lock->unlock(); + } + + //as scripts are going to be reloaded, must proceed without locking here + + scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order + + for(List<Ref<GDScript> >::Element *E=scripts.front();E;E=E->next()) { + + print_line("RELOADING: "+E->get()->get_path()); + E->get()->load_source_code(E->get()->get_path()); + E->get()->reload(true); + } +#endif +} + void GDScriptLanguage::frame() { // print_line("calls: "+itos(calls)); diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index 05e1646408..f052d13685 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -107,6 +107,7 @@ friend class GDScriptLanguage; String source; String path; String name; + SelfList<GDScript> script_list; GDInstance* _create_instance(const Variant** p_args,int p_argcount,Object *p_owner,bool p_isref,Variant::CallError &r_error); @@ -165,7 +166,7 @@ public: virtual void set_source_code(const String& p_code); virtual void update_exports(); - virtual Error reload(); + virtual Error reload(bool p_keep_state=false); virtual String get_node_type() const; void set_script_path(const String& p_path) { path=p_path; } //because subclasses need a path too... @@ -186,9 +187,13 @@ class GDInstance : public ScriptInstance { friend class GDScript; friend class GDFunction; friend class GDFunctions; +friend class GDCompiler; Object *owner; Ref<GDScript> script; +#ifdef DEBUG_ENABLED + Map<StringName,int> member_indices_cache; //used only for hot script reloading +#endif Vector<Variant> members; bool base_ref; @@ -209,7 +214,7 @@ 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); - Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; } + Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; } virtual void notification(int p_notification); @@ -219,6 +224,7 @@ public: void set_path(const String& p_path); + void reload_members(); GDInstance(); ~GDInstance(); @@ -256,6 +262,10 @@ class GDScriptLanguage : public ScriptLanguage { Mutex *lock; + +friend class GDScript; + + SelfList<GDScript>::List script_list; friend class GDFunction; SelfList<GDFunction>::List function_list; @@ -378,6 +388,8 @@ public: virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1); virtual String debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems=-1,int p_max_depth=-1); + virtual void reload_all_scripts(); + virtual void frame(); virtual void get_public_functions(List<MethodInfo> *p_functions) const; diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp index 40dce6cbaa..a0251936bd 100644 --- a/tools/editor/editor_node.cpp +++ b/tools/editor/editor_node.cpp @@ -2795,6 +2795,10 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { run_native->set_debug_navigation(!ischecked); editor_run.set_debug_navigation(!ischecked); } break; + case RUN_RELOAD_SCRIPTS: { + + ScriptEditor::get_singleton()->get_debugger()->reload_scripts(); + } break; case SETTINGS_UPDATE_ALWAYS: { update_menu->get_popup()->set_item_checked(0,true); @@ -5717,6 +5721,8 @@ EditorNode::EditorNode() { p->add_separator(); p->add_check_item(TTR("Visible Collision Shapes"),RUN_DEBUG_COLLISONS); p->add_check_item(TTR("Visible Navigation"),RUN_DEBUG_NAVIGATION); + p->add_separator(); + p->add_item(TTR("Reload Scripts"),RUN_RELOAD_SCRIPTS); p->connect("item_pressed",this,"_menu_option"); /* diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h index e580931df3..5ffd7b964a 100644 --- a/tools/editor/editor_node.h +++ b/tools/editor/editor_node.h @@ -174,6 +174,7 @@ private: RUN_DEBUG_COLLISONS, RUN_DEBUG_NAVIGATION, RUN_DEPLOY_REMOTE_DEBUG, + RUN_RELOAD_SCRIPTS, SETTINGS_UPDATE_ALWAYS, SETTINGS_UPDATE_CHANGES, SETTINGS_IMPORT, diff --git a/tools/editor/script_editor_debugger.cpp b/tools/editor/script_editor_debugger.cpp index 2f4846d63a..37a90ba7be 100644 --- a/tools/editor/script_editor_debugger.cpp +++ b/tools/editor/script_editor_debugger.cpp @@ -1573,6 +1573,16 @@ void ScriptEditorDebugger::set_breakpoint(const String& p_path,int p_line,bool p } +void ScriptEditorDebugger::reload_scripts() { + + if (connection.is_valid()) { + Array msg; + msg.push_back("reload_scripts"); + ppeer->put_var(msg); + } +} + + void ScriptEditorDebugger::_error_selected(int p_idx) { error_stack->clear(); diff --git a/tools/editor/script_editor_debugger.h b/tools/editor/script_editor_debugger.h index 128ca16173..c4a7cea1b7 100644 --- a/tools/editor/script_editor_debugger.h +++ b/tools/editor/script_editor_debugger.h @@ -217,6 +217,8 @@ public: void set_tool_button(Button *p_tb) { debugger_button=p_tb; } + void reload_scripts(); + virtual Size2 get_minimum_size() const; ScriptEditorDebugger(EditorNode *p_editor=NULL); ~ScriptEditorDebugger(); |