diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/SCsub | 42 | ||||
-rw-r--r-- | modules/gdscript/config.py | 10 | ||||
-rw-r--r-- | modules/gdscript/gd_compiler.cpp | 142 | ||||
-rw-r--r-- | modules/gdscript/gd_compiler.h | 4 | ||||
-rw-r--r-- | modules/gdscript/gd_editor.cpp | 58 | ||||
-rw-r--r-- | modules/gdscript/gd_function.cpp | 1429 | ||||
-rw-r--r-- | modules/gdscript/gd_function.h | 207 | ||||
-rw-r--r-- | modules/gdscript/gd_functions.cpp | 10 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.cpp | 2 | ||||
-rw-r--r-- | modules/gdscript/gd_script.cpp | 1736 | ||||
-rw-r--r-- | modules/gdscript/gd_script.h | 217 | ||||
-rw-r--r-- | modules/gridmap/config.py | 10 | ||||
-rw-r--r-- | modules/gridmap/grid_map.cpp | 119 | ||||
-rw-r--r-- | modules/gridmap/grid_map.h | 27 | ||||
-rw-r--r-- | modules/gridmap/grid_map_editor_plugin.cpp | 6 | ||||
-rw-r--r-- | modules/ik/SCsub | 3 | ||||
-rw-r--r-- | modules/ik/config.py | 11 | ||||
-rw-r--r-- | modules/ik/ik.cpp | 326 | ||||
-rw-r--r-- | modules/ik/ik.h | 74 | ||||
-rw-r--r-- | modules/ik/register_types.cpp | 47 | ||||
-rw-r--r-- | modules/ik/register_types.h | 30 |
21 files changed, 2822 insertions, 1688 deletions
diff --git a/modules/SCsub b/modules/SCsub index 9215bfd48f..f37c3a55c7 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -1,21 +1,21 @@ -Import('env')
-
-env_modules = env.Clone()
-
-Export('env_modules')
-
-env.modules_sources=[
- "register_module_types.cpp",
-]
-#env.add_source_files(env.modules_sources,"*.cpp")
-Export('env')
-
-for x in env.module_list:
- if (x in env.disabled_modules):
- continue
- env_modules.Append(CPPFLAGS=["-DMODULE_"+x.upper()+"_ENABLED"])
- SConscript(x+"/SCsub")
-
-lib = env_modules.Library("modules",env.modules_sources)
-
-env.Prepend(LIBS=[lib])
+Import('env') + +env_modules = env.Clone() + +Export('env_modules') + +env.modules_sources=[ + "register_module_types.cpp", +] +#env.add_source_files(env.modules_sources,"*.cpp") +Export('env') + +for x in env.module_list: + if (x in env.disabled_modules): + continue + env_modules.Append(CPPFLAGS=["-DMODULE_"+x.upper()+"_ENABLED"]) + SConscript(x+"/SCsub") + +lib = env_modules.Library("modules",env.modules_sources) + +env.Prepend(LIBS=[lib]) diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py index f9bd7da08d..ea7e83378a 100644 --- a/modules/gdscript/config.py +++ b/modules/gdscript/config.py @@ -2,10 +2,10 @@ def can_build(platform): return True - - + + def configure(env): pass - - - + + + diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index e8e8ce4e96..072c53fb9f 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -1290,8 +1290,8 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * // gdfunc = &p_script->initializer; //} else { //regular func - p_script->member_functions[func_name]=GDFunction(); - gdfunc = &p_script->member_functions[func_name]; + p_script->member_functions[func_name]=memnew(GDFunction); + gdfunc = p_script->member_functions[func_name]; //} if (p_func) @@ -1358,6 +1358,32 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * gdfunc->_stack_size=codegen.stack_max; gdfunc->_call_size=codegen.call_max; gdfunc->name=func_name; +#ifdef DEBUG_ENABLED + if (ScriptDebugger::get_singleton()){ + String signature; + //path + if (p_script->get_path()!=String()) + signature+=p_script->get_path(); + //loc + if (p_func) { + signature+="::"+itos(p_func->body->line); + } else { + signature+="::0"; + } + + //funciton and class + + if (p_class->name) { + signature+="::"+String(p_class->name)+"."+String(func_name);; + } else { + signature+="::"+String(func_name); + } + + + + gdfunc->profile.signature=signature; + } +#endif gdfunc->_script=p_script; gdfunc->source=source; @@ -1388,18 +1414,27 @@ 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>(); p_script->_base=NULL; p_script->members.clear(); p_script->constants.clear(); + for (Map<StringName,GDFunction*>::Element *E=p_script->member_functions.front();E;E=E->next()) { + memdelete(E->get()); + } p_script->member_functions.clear(); 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; @@ -1421,7 +1456,22 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars if (path.is_rel_path()) { - String base = p_script->get_path(); + String base; + + if (p_owner) { + GDScript *current_class = p_owner; + while (current_class != NULL) { + base=current_class->get_path(); + if (base=="") + current_class = current_class->_owner; + else + break; + } + } + else { + base = p_script->get_path(); + } + if (base=="" || base.is_rel_path()) { _set_error("Could not resolve relative path for parent class: "+path,p_class); return ERR_FILE_NOT_FOUND; @@ -1538,6 +1588,12 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars } + } else { + // without extends, implicitly extend Reference + int native_idx = GDScriptLanguage::get_singleton()->get_global_map()["Reference"]; + native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx]; + ERR_FAIL_COND_V(native.is_null(), ERR_BUG); + p_script->native=native; } @@ -1618,9 +1674,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; @@ -1675,7 +1737,7 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars for(int i=0;i<p_class->variables.size();i++) { if (p_class->variables[i].setter) { - const Map<StringName,GDFunction>::Element *E=p_script->get_member_functions().find(p_class->variables[i].setter); + const Map<StringName,GDFunction*>::Element *E=p_script->get_member_functions().find(p_class->variables[i].setter); if (!E) { _set_error("Setter function '"+String(p_class->variables[i].setter)+"' not found in class.",NULL); err_line=p_class->variables[i].line; @@ -1683,7 +1745,7 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars return ERR_PARSE_ERROR; } - if (E->get().is_static()) { + if (E->get()->is_static()) { _set_error("Setter function '"+String(p_class->variables[i].setter)+"' is static.",NULL); err_line=p_class->variables[i].line; @@ -1693,7 +1755,7 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars } if (p_class->variables[i].getter) { - const Map<StringName,GDFunction>::Element *E=p_script->get_member_functions().find(p_class->variables[i].getter); + const Map<StringName,GDFunction*>::Element *E=p_script->get_member_functions().find(p_class->variables[i].getter); if (!E) { _set_error("Getter function '"+String(p_class->variables[i].getter)+"' not found in class.",NULL); err_line=p_class->variables[i].line; @@ -1701,7 +1763,7 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars return ERR_PARSE_ERROR; } - if (E->get().is_static()) { + if (E->get()->is_static()) { _set_error("Getter function '"+String(p_class->variables[i].getter)+"' is static.",NULL); err_line=p_class->variables[i].line; @@ -1711,13 +1773,67 @@ 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()) { +#ifdef TOOLS_ENABLED + 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 + } + } +#endif + } else { + + 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; @@ -1728,9 +1844,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_editor.cpp b/modules/gdscript/gd_editor.cpp index ff19518ad5..7e5ff620c9 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -1310,9 +1310,9 @@ static void _find_identifiers_in_class(GDCompletionContext& context,bool p_stati } } - for (const Map<StringName,GDFunction>::Element *E=script->get_member_functions().front();E;E=E->next()) { - if (!p_static || E->get().is_static()) { - if (E->get().get_argument_count()) + for (const Map<StringName,GDFunction*>::Element *E=script->get_member_functions().front();E;E=E->next()) { + if (!p_static || E->get()->is_static()) { + if (E->get()->get_argument_count()) result.insert(E->key().operator String()+"("); else result.insert(E->key().operator String()+"()"); @@ -1536,10 +1536,10 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St if (scr) { while (scr) { - for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { - if (E->get().is_static() && p_method==E->get().get_name()) { + for (const Map<StringName,GDFunction*>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (E->get()->is_static() && p_method==E->get()->get_name()) { arghint="static func "+String(p_method)+"("; - for(int i=0;i<E->get().get_argument_count();i++) { + for(int i=0;i<E->get()->get_argument_count();i++) { if (i>0) arghint+=", "; else @@ -1547,12 +1547,12 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St if (i==p_argidx) { arghint+=String::chr(0xFFFF); } - arghint+="var "+E->get().get_argument_name(i); - int deffrom = E->get().get_argument_count()-E->get().get_default_argument_count(); + arghint+="var "+E->get()->get_argument_name(i); + int deffrom = E->get()->get_argument_count()-E->get()->get_default_argument_count(); if (i>=deffrom) { int defidx = deffrom-i; - if (defidx>=0 && defidx<E->get().get_default_argument_count()) { - arghint+="="+E->get().get_default_argument(defidx).get_construct_string(); + if (defidx>=0 && defidx<E->get()->get_default_argument_count()) { + arghint+="="+E->get()->get_default_argument(defidx).get_construct_string(); } } if (i==p_argidx) { @@ -1670,10 +1670,10 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St if (code=="") { - for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { - if (p_method==E->get().get_name()) { + for (const Map<StringName,GDFunction*>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (p_method==E->get()->get_name()) { arghint="func "+String(p_method)+"("; - for(int i=0;i<E->get().get_argument_count();i++) { + for(int i=0;i<E->get()->get_argument_count();i++) { if (i>0) arghint+=", "; else @@ -1681,12 +1681,12 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St if (i==p_argidx) { arghint+=String::chr(0xFFFF); } - arghint+="var "+E->get().get_argument_name(i); - int deffrom = E->get().get_argument_count()-E->get().get_default_argument_count(); + arghint+="var "+E->get()->get_argument_name(i); + int deffrom = E->get()->get_argument_count()-E->get()->get_default_argument_count(); if (i>=deffrom) { int defidx = deffrom-i; - if (defidx>=0 && defidx<E->get().get_default_argument_count()) { - arghint+="="+E->get().get_default_argument(defidx).get_construct_string(); + if (defidx>=0 && defidx<E->get()->get_default_argument_count()) { + arghint+="="+E->get()->get_default_argument(defidx).get_construct_string(); } } if (i==p_argidx) { @@ -1926,16 +1926,16 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No if (script.is_valid()) { - for (const Map<StringName,GDFunction>::Element *E=script->get_member_functions().front();E;E=E->next()) { + for (const Map<StringName,GDFunction*>::Element *E=script->get_member_functions().front();E;E=E->next()) { if (E->key()==id->name) { - if (context.function && context.function->_static && !E->get().is_static()) + if (context.function && context.function->_static && !E->get()->is_static()) continue; arghint = "func "+id->name.operator String()+String("("); - for(int i=0;i<E->get().get_argument_count();i++) { + for(int i=0;i<E->get()->get_argument_count();i++) { if (i>0) arghint+=", "; else @@ -1943,12 +1943,12 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No if (i==p_argidx) { arghint+=String::chr(0xFFFF); } - arghint+=E->get().get_argument_name(i); - int deffrom = E->get().get_argument_count()-E->get().get_default_argument_count(); + arghint+=E->get()->get_argument_name(i); + int deffrom = E->get()->get_argument_count()-E->get()->get_default_argument_count(); if (i>=deffrom) { int defidx = deffrom-i; - if (defidx>=0 && defidx<E->get().get_default_argument_count()) { - arghint+="="+E->get().get_default_argument(defidx).get_construct_string(); + if (defidx>=0 && defidx<E->get()->get_default_argument_count()) { + arghint+="="+E->get()->get_default_argument(defidx).get_construct_string(); } } if (i==p_argidx) { @@ -1956,7 +1956,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No } } - if (E->get().get_argument_count()>0) + if (E->get()->get_argument_count()>0) arghint+=" "; arghint+=")"; return; @@ -2178,8 +2178,8 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base options.insert(E->key()); } } - for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { - if (E->get().is_static()) + for (const Map<StringName,GDFunction*>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (E->get()->is_static()) options.insert(E->key()); } @@ -2266,8 +2266,8 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base options.insert(E->key()); } } - for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { - if (E->get().get_argument_count()) + for (const Map<StringName,GDFunction*>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (E->get()->get_argument_count()) options.insert(String(E->key())+"()"); else options.insert(String(E->key())+"("); diff --git a/modules/gdscript/gd_function.cpp b/modules/gdscript/gd_function.cpp new file mode 100644 index 0000000000..04522aadc2 --- /dev/null +++ b/modules/gdscript/gd_function.cpp @@ -0,0 +1,1429 @@ +#include "gd_function.h" +#include "gd_script.h" +#include "os/os.h" +#include "gd_functions.h" + +Variant *GDFunction::_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self, Variant *p_stack,String& r_error) const{ + + int address = p_address&ADDR_MASK; + + //sequential table (jump table generated by compiler) + switch((p_address&ADDR_TYPE_MASK)>>ADDR_BITS) { + + case ADDR_TYPE_SELF: { + + if (!p_instance) { + r_error="Cannot access self without instance."; + return NULL; + } + return &self; + } break; + case ADDR_TYPE_CLASS: { + + return &p_script->_static_ref; + } break; + case ADDR_TYPE_MEMBER: { + //member indexing is O(1) + if (!p_instance) { + r_error="Cannot access member without instance."; + return NULL; + } + return &p_instance->members[address]; + } break; + case ADDR_TYPE_CLASS_CONSTANT: { + + //todo change to index! + GDScript *o=p_script; + ERR_FAIL_INDEX_V(address,_global_names_count,NULL); + const StringName *sn = &_global_names_ptr[address]; + + while(o) { + GDScript *s=o; + while(s) { + + Map<StringName,Variant>::Element *E=s->constants.find(*sn); + if (E) { + return &E->get(); + } + s=s->_base; + } + o=o->_owner; + } + + + ERR_EXPLAIN("GDCompiler bug.."); + ERR_FAIL_V(NULL); + } break; + case ADDR_TYPE_LOCAL_CONSTANT: { + ERR_FAIL_INDEX_V(address,_constant_count,NULL); + return &_constants_ptr[address]; + } break; + case ADDR_TYPE_STACK: + case ADDR_TYPE_STACK_VARIABLE: { + ERR_FAIL_INDEX_V(address,_stack_size,NULL); + return &p_stack[address]; + } break; + case ADDR_TYPE_GLOBAL: { + + + ERR_FAIL_INDEX_V(address,GDScriptLanguage::get_singleton()->get_global_array_size(),NULL); + + + return &GDScriptLanguage::get_singleton()->get_global_array()[address]; + } break; + case ADDR_TYPE_NIL: { + return &nil; + } break; + } + + ERR_EXPLAIN("Bad Code! (Addressing Mode)"); + ERR_FAIL_V(NULL); + return NULL; +} + + +String GDFunction::_get_call_error(const Variant::CallError& p_err, const String& p_where,const Variant**argptrs) const { + + + + String err_text; + + if (p_err.error==Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { + int errorarg=p_err.argument; + err_text="Invalid type in "+p_where+". Cannot convert argument "+itos(errorarg+1)+" from "+Variant::get_type_name(argptrs[errorarg]->get_type())+" to "+Variant::get_type_name(p_err.expected)+"."; + } else if (p_err.error==Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { + err_text="Invalid call to "+p_where+". Expected "+itos(p_err.argument)+" arguments."; + } else if (p_err.error==Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { + err_text="Invalid call to "+p_where+". Expected "+itos(p_err.argument)+" arguments."; + } else if (p_err.error==Variant::CallError::CALL_ERROR_INVALID_METHOD) { + err_text="Invalid call. Nonexistent "+p_where+"."; + } else if (p_err.error==Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) { + err_text="Attempt to call "+p_where+" on a null instance."; + } else { + err_text="Bug, call error: #"+itos(p_err.error); + } + + return err_text; + +} + +static String _get_var_type(const Variant* p_type) { + + String basestr; + + if (p_type->get_type()==Variant::OBJECT) { + Object *bobj = *p_type; + if (!bobj) { + basestr = "null instance"; + } else { +#ifdef DEBUG_ENABLED + if (ObjectDB::instance_validate(bobj)) { + if (bobj->get_script_instance()) + basestr= bobj->get_type()+" ("+bobj->get_script_instance()->get_script()->get_path().get_file()+")"; + else + basestr = bobj->get_type(); + } else { + basestr="previously freed instance"; + } + +#else + basestr="Object"; +#endif + } + + } else { + basestr = Variant::get_type_name(p_type->get_type()); + } + + return basestr; + +} + +Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError& r_err, CallState *p_state) { + + + if (!_code_ptr) { + + return Variant(); + } + + r_err.error=Variant::CallError::CALL_OK; + + Variant self; + Variant retvalue; + Variant *stack = NULL; + Variant **call_args; + int defarg=0; + +#ifdef DEBUG_ENABLED + + //GDScriptLanguage::get_singleton()->calls++; + +#endif + + uint32_t alloca_size=0; + GDScript *_class; + int ip=0; + int line=_initial_line; + + + + if (p_state) { + //use existing (supplied) state (yielded) + stack=(Variant*)p_state->stack.ptr(); + call_args=(Variant**)&p_state->stack[sizeof(Variant)*p_state->stack_size]; + line=p_state->line; + ip=p_state->ip; + alloca_size=p_state->stack.size(); + _class=p_state->_class; + p_instance=p_state->instance; + defarg=p_state->defarg; + self=p_state->self; + //stack[p_state->result_pos]=p_state->result; //assign stack with result + + } else { + + if (p_argcount!=_argument_count) { + + if (p_argcount>_argument_count) { + + r_err.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_err.argument=_argument_count; + + + return Variant(); + } else if (p_argcount < _argument_count - _default_arg_count) { + + r_err.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_err.argument=_argument_count - _default_arg_count; + return Variant(); + } else { + + defarg=_argument_count-p_argcount; + } + } + + alloca_size = sizeof(Variant*)*_call_size + sizeof(Variant)*_stack_size; + + if (alloca_size) { + + uint8_t *aptr = (uint8_t*)alloca(alloca_size); + + if (_stack_size) { + + stack=(Variant*)aptr; + for(int i=0;i<p_argcount;i++) + memnew_placement(&stack[i],Variant(*p_args[i])); + for(int i=p_argcount;i<_stack_size;i++) + memnew_placement(&stack[i],Variant); + } else { + stack=NULL; + } + + if (_call_size) { + + call_args = (Variant**)&aptr[sizeof(Variant)*_stack_size]; + } else { + + call_args=NULL; + } + + + } else { + stack=NULL; + call_args=NULL; + } + + if (p_instance) { + if (p_instance->base_ref && static_cast<Reference*>(p_instance->owner)->is_referenced()) { + + self=REF(static_cast<Reference*>(p_instance->owner)); + } else { + self=p_instance->owner; + } + _class=p_instance->script.ptr(); + } else { + _class=_script; + } + } + + String err_text; + +#ifdef DEBUG_ENABLED + + if (ScriptDebugger::get_singleton()) + GDScriptLanguage::get_singleton()->enter_function(p_instance,this,stack,&ip,&line); + +#define CHECK_SPACE(m_space)\ + ERR_BREAK((ip+m_space)>_code_size) + +#define GET_VARIANT_PTR(m_v,m_code_ofs) \ + Variant *m_v; \ + m_v = _get_variant(_code_ptr[ip+m_code_ofs],p_instance,_class,self,stack,err_text);\ + if (!m_v)\ + break; + + +#else +#define CHECK_SPACE(m_space) +#define GET_VARIANT_PTR(m_v,m_code_ofs) \ + Variant *m_v; \ + m_v = _get_variant(_code_ptr[ip+m_code_ofs],p_instance,_class,self,stack,err_text); + +#endif + + +#ifdef DEBUG_ENABLED + + uint64_t function_start_time; + uint64_t function_call_time; + + if (GDScriptLanguage::get_singleton()->profiling) { + function_start_time=OS::get_singleton()->get_ticks_usec(); + function_call_time=0; + profile.call_count++; + profile.frame_call_count++; + } +#endif + bool exit_ok=false; + + while(ip<_code_size) { + + + int last_opcode=_code_ptr[ip]; + switch(_code_ptr[ip]) { + + case OPCODE_OPERATOR: { + + CHECK_SPACE(5); + + bool valid; + Variant::Operator op = (Variant::Operator)_code_ptr[ip+1]; + ERR_BREAK(op>=Variant::OP_MAX); + + GET_VARIANT_PTR(a,2); + GET_VARIANT_PTR(b,3); + GET_VARIANT_PTR(dst,4); + +#ifdef DEBUG_ENABLED + Variant ret; + Variant::evaluate(op,*a,*b,ret,valid); +#else + Variant::evaluate(op,*a,*b,*dst,valid); +#endif + + if (!valid) { +#ifdef DEBUG_ENABLED + + if (ret.get_type()==Variant::STRING) { + //return a string when invalid with the error + err_text=ret; + err_text += " in operator '"+Variant::get_operator_name(op)+"'."; + } else { + err_text="Invalid operands '"+Variant::get_type_name(a->get_type())+"' and '"+Variant::get_type_name(b->get_type())+"' in operator '"+Variant::get_operator_name(op)+"'."; + } +#endif + break; + + } +#ifdef DEBUG_ENABLED + *dst=ret; +#endif + + ip+=5; + + } continue; + case OPCODE_EXTENDS_TEST: { + + CHECK_SPACE(4); + + GET_VARIANT_PTR(a,1); + GET_VARIANT_PTR(b,2); + GET_VARIANT_PTR(dst,3); + +#ifdef DEBUG_ENABLED + + if (a->get_type()!=Variant::OBJECT || a->operator Object*()==NULL) { + + err_text="Left operand of 'extends' is not an instance of anything."; + break; + + } + if (b->get_type()!=Variant::OBJECT || b->operator Object*()==NULL) { + + err_text="Right operand of 'extends' is not a class."; + break; + + } +#endif + + + Object *obj_A = *a; + Object *obj_B = *b; + + + GDScript *scr_B = obj_B->cast_to<GDScript>(); + + bool extends_ok=false; + + if (scr_B) { + //if B is a script, the only valid condition is that A has an instance which inherits from the script + //in other situation, this shoul return false. + + if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language()==GDScriptLanguage::get_singleton()) { + + GDInstance *ins = static_cast<GDInstance*>(obj_A->get_script_instance()); + GDScript *cmp = ins->script.ptr(); + //bool found=false; + while(cmp) { + + if (cmp==scr_B) { + //inherits from script, all ok + extends_ok=true; + break; + + } + + cmp=cmp->_base; + } + + } + + + } else { + + GDNativeClass *nc= obj_B->cast_to<GDNativeClass>(); + + if (!nc) { + + err_text="Right operand of 'extends' is not a class (type: '"+obj_B->get_type()+"')."; + break; + } + + extends_ok=ObjectTypeDB::is_type(obj_A->get_type_name(),nc->get_name()); + } + + *dst=extends_ok; + ip+=4; + + } continue; + case OPCODE_SET: { + + CHECK_SPACE(3); + + GET_VARIANT_PTR(dst,1); + GET_VARIANT_PTR(index,2); + GET_VARIANT_PTR(value,3); + + bool valid; + dst->set(*index,*value,&valid); + + if (!valid) { + String v = index->operator String(); + if (v!="") { + v="'"+v+"'"; + } else { + v="of type '"+_get_var_type(index)+"'"; + } + err_text="Invalid set index "+v+" (on base: '"+_get_var_type(dst)+"')."; + break; + } + + ip+=4; + } continue; + case OPCODE_GET: { + + CHECK_SPACE(3); + + GET_VARIANT_PTR(src,1); + GET_VARIANT_PTR(index,2); + GET_VARIANT_PTR(dst,3); + + bool valid; +#ifdef DEBUG_ENABLED + //allow better error message in cases where src and dst are the same stack position + Variant ret = src->get(*index,&valid); +#else + *dst = src->get(*index,&valid); + +#endif + if (!valid) { + String v = index->operator String(); + if (v!="") { + v="'"+v+"'"; + } else { + v="of type '"+_get_var_type(index)+"'"; + } + err_text="Invalid get index "+v+" (on base: '"+_get_var_type(src)+"')."; + break; + } +#ifdef DEBUG_ENABLED + *dst=ret; +#endif + ip+=4; + } continue; + case OPCODE_SET_NAMED: { + + CHECK_SPACE(3); + + GET_VARIANT_PTR(dst,1); + GET_VARIANT_PTR(value,3); + + int indexname = _code_ptr[ip+2]; + + ERR_BREAK(indexname<0 || indexname>=_global_names_count); + const StringName *index = &_global_names_ptr[indexname]; + + bool valid; + dst->set_named(*index,*value,&valid); + + if (!valid) { + String err_type; + err_text="Invalid set index '"+String(*index)+"' (on base: '"+_get_var_type(dst)+"')."; + break; + } + + ip+=4; + } continue; + case OPCODE_GET_NAMED: { + + + CHECK_SPACE(3); + + GET_VARIANT_PTR(src,1); + GET_VARIANT_PTR(dst,3); + + int indexname = _code_ptr[ip+2]; + + ERR_BREAK(indexname<0 || indexname>=_global_names_count); + const StringName *index = &_global_names_ptr[indexname]; + + bool valid; +#ifdef DEBUG_ENABLED + //allow better error message in cases where src and dst are the same stack position + Variant ret = src->get_named(*index,&valid); + +#else + *dst = src->get_named(*index,&valid); +#endif + + if (!valid) { + if (src->has_method(*index)) { + err_text="Invalid get index '"+index->operator String()+"' (on base: '"+_get_var_type(src)+"'). Did you mean '."+index->operator String()+"()' ?"; + } else { + err_text="Invalid get index '"+index->operator String()+"' (on base: '"+_get_var_type(src)+"')."; + } + break; + } +#ifdef DEBUG_ENABLED + *dst=ret; +#endif + ip+=4; + } continue; + case OPCODE_ASSIGN: { + + CHECK_SPACE(3); + GET_VARIANT_PTR(dst,1); + GET_VARIANT_PTR(src,2); + + *dst = *src; + + ip+=3; + + } continue; + case OPCODE_ASSIGN_TRUE: { + + CHECK_SPACE(2); + GET_VARIANT_PTR(dst,1); + + *dst = true; + + ip+=2; + } continue; + case OPCODE_ASSIGN_FALSE: { + + CHECK_SPACE(2); + GET_VARIANT_PTR(dst,1); + + *dst = false; + + ip+=2; + } continue; + case OPCODE_CONSTRUCT: { + + CHECK_SPACE(2); + Variant::Type t=Variant::Type(_code_ptr[ip+1]); + int argc=_code_ptr[ip+2]; + CHECK_SPACE(argc+2); + Variant **argptrs = call_args; + for(int i=0;i<argc;i++) { + GET_VARIANT_PTR(v,3+i); + argptrs[i]=v; + } + + GET_VARIANT_PTR(dst,3+argc); + Variant::CallError err; + *dst = Variant::construct(t,(const Variant**)argptrs,argc,err); + + if (err.error!=Variant::CallError::CALL_OK) { + + err_text=_get_call_error(err,"'"+Variant::get_type_name(t)+"' constructor",(const Variant**)argptrs); + break; + } + + ip+=4+argc; + //construct a basic type + } continue; + case OPCODE_CONSTRUCT_ARRAY: { + + CHECK_SPACE(1); + int argc=_code_ptr[ip+1]; + Array array(true); //arrays are always shared + array.resize(argc); + CHECK_SPACE(argc+2); + + for(int i=0;i<argc;i++) { + GET_VARIANT_PTR(v,2+i); + array[i]=*v; + + } + + GET_VARIANT_PTR(dst,2+argc); + + *dst=array; + + ip+=3+argc; + + } continue; + case OPCODE_CONSTRUCT_DICTIONARY: { + + CHECK_SPACE(1); + int argc=_code_ptr[ip+1]; + Dictionary dict(true); //arrays are always shared + + CHECK_SPACE(argc*2+2); + + for(int i=0;i<argc;i++) { + + GET_VARIANT_PTR(k,2+i*2+0); + GET_VARIANT_PTR(v,2+i*2+1); + dict[*k]=*v; + + } + + GET_VARIANT_PTR(dst,2+argc*2); + + *dst=dict; + + ip+=3+argc*2; + + } continue; + case OPCODE_CALL_RETURN: + case OPCODE_CALL: { + + + CHECK_SPACE(4); + bool call_ret = _code_ptr[ip]==OPCODE_CALL_RETURN; + + int argc=_code_ptr[ip+1]; + GET_VARIANT_PTR(base,2); + int nameg=_code_ptr[ip+3]; + + ERR_BREAK(nameg<0 || nameg>=_global_names_count); + const StringName *methodname = &_global_names_ptr[nameg]; + + ERR_BREAK(argc<0); + ip+=4; + CHECK_SPACE(argc+1); + Variant **argptrs = call_args; + + for(int i=0;i<argc;i++) { + GET_VARIANT_PTR(v,i); + argptrs[i]=v; + } + +#ifdef DEBUG_ENABLED + uint64_t call_time; + + if (GDScriptLanguage::get_singleton()->profiling) { + call_time=OS::get_singleton()->get_ticks_usec(); + } + +#endif + Variant::CallError err; + if (call_ret) { + + GET_VARIANT_PTR(ret,argc); + *ret = base->call(*methodname,(const Variant**)argptrs,argc,err); + } else { + + base->call(*methodname,(const Variant**)argptrs,argc,err); + } +#ifdef DEBUG_ENABLED + if (GDScriptLanguage::get_singleton()->profiling) { + function_call_time+=OS::get_singleton()->get_ticks_usec() - call_time; + } +#endif + + if (err.error!=Variant::CallError::CALL_OK) { + + + String methodstr = *methodname; + String basestr = _get_var_type(base); + + if (methodstr=="call") { + if (argc>=1) { + methodstr=String(*argptrs[0])+" (via call)"; + if (err.error==Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { + err.argument-=1; + } + } + } if (methodstr=="free") { + + if (err.error==Variant::CallError::CALL_ERROR_INVALID_METHOD) { + + if (base->is_ref()) { + err_text="Attempted to free a reference."; + break; + } else if (base->get_type()==Variant::OBJECT) { + + err_text="Attempted to free a locked object (calling or emitting)."; + break; + } + } + } + err_text=_get_call_error(err,"function '"+methodstr+"' in base '"+basestr+"'",(const Variant**)argptrs); + break; + } + + //_call_func(NULL,base,*methodname,ip,argc,p_instance,stack); + ip+=argc+1; + + } continue; + case OPCODE_CALL_BUILT_IN: { + + CHECK_SPACE(4); + + GDFunctions::Function func = GDFunctions::Function(_code_ptr[ip+1]); + int argc=_code_ptr[ip+2]; + ERR_BREAK(argc<0); + + ip+=3; + CHECK_SPACE(argc+1); + Variant **argptrs = call_args; + + for(int i=0;i<argc;i++) { + GET_VARIANT_PTR(v,i); + argptrs[i]=v; + } + + GET_VARIANT_PTR(dst,argc); + + Variant::CallError err; + + GDFunctions::call(func,(const Variant**)argptrs,argc,*dst,err); + + if (err.error!=Variant::CallError::CALL_OK) { + + + String methodstr = GDFunctions::get_func_name(func); + err_text=_get_call_error(err,"built-in function '"+methodstr+"'",(const Variant**)argptrs); + break; + } + ip+=argc+1; + + } continue; + case OPCODE_CALL_SELF: { + + + } break; + case OPCODE_CALL_SELF_BASE: { + + CHECK_SPACE(2); + int self_fun = _code_ptr[ip+1]; +#ifdef DEBUG_ENABLED + + if (self_fun<0 || self_fun>=_global_names_count) { + + err_text="compiler bug, function name not found"; + break; + } +#endif + const StringName *methodname = &_global_names_ptr[self_fun]; + + int argc=_code_ptr[ip+2]; + + CHECK_SPACE(2+argc+1); + + Variant **argptrs = call_args; + + for(int i=0;i<argc;i++) { + GET_VARIANT_PTR(v,i+3); + argptrs[i]=v; + } + + GET_VARIANT_PTR(dst,argc+3); + + const GDScript *gds = _script; + + + const Map<StringName,GDFunction*>::Element *E=NULL; + while (gds->base.ptr()) { + gds=gds->base.ptr(); + E=gds->member_functions.find(*methodname); + if (E) + break; + } + + Variant::CallError err; + + if (E) { + + *dst=E->get()->call(p_instance,(const Variant**)argptrs,argc,err); + } else if (gds->native.ptr()) { + + if (*methodname!=GDScriptLanguage::get_singleton()->strings._init) { + + MethodBind *mb = ObjectTypeDB::get_method(gds->native->get_name(),*methodname); + if (!mb) { + err.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + } else { + *dst=mb->call(p_instance->owner,(const Variant**)argptrs,argc,err); + } + } else { + err.error=Variant::CallError::CALL_OK; + } + } else { + + if (*methodname!=GDScriptLanguage::get_singleton()->strings._init) { + err.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + } else { + err.error=Variant::CallError::CALL_OK; + } + } + + + if (err.error!=Variant::CallError::CALL_OK) { + + + String methodstr = *methodname; + err_text=_get_call_error(err,"function '"+methodstr+"'",(const Variant**)argptrs); + + break; + } + + ip+=4+argc; + + } continue; + case OPCODE_YIELD: + case OPCODE_YIELD_SIGNAL: { + + int ipofs=1; + if (_code_ptr[ip]==OPCODE_YIELD_SIGNAL) { + CHECK_SPACE(4); + ipofs+=2; + } else { + CHECK_SPACE(2); + + } + + Ref<GDFunctionState> gdfs = memnew( GDFunctionState ); + gdfs->function=this; + + gdfs->state.stack.resize(alloca_size); + //copy variant stack + for(int i=0;i<_stack_size;i++) { + memnew_placement(&gdfs->state.stack[sizeof(Variant)*i],Variant(stack[i])); + } + gdfs->state.stack_size=_stack_size; + gdfs->state.self=self; + gdfs->state.alloca_size=alloca_size; + gdfs->state._class=_class; + gdfs->state.ip=ip+ipofs; + gdfs->state.line=line; + //gdfs->state.result_pos=ip+ipofs-1; + gdfs->state.defarg=defarg; + gdfs->state.instance=p_instance; + gdfs->function=this; + + retvalue=gdfs; + + if (_code_ptr[ip]==OPCODE_YIELD_SIGNAL) { + GET_VARIANT_PTR(argobj,1); + GET_VARIANT_PTR(argname,2); + //do the oneshot connect + + if (argobj->get_type()!=Variant::OBJECT) { + err_text="First argument of yield() not of type object."; + break; + } + if (argname->get_type()!=Variant::STRING) { + err_text="Second argument of yield() not a string (for signal name)."; + break; + } + Object *obj=argobj->operator Object *(); + String signal = argname->operator String(); +#ifdef DEBUG_ENABLED + + if (!obj) { + err_text="First argument of yield() is null."; + break; + } + if (ScriptDebugger::get_singleton()) { + if (!ObjectDB::instance_validate(obj)) { + err_text="First argument of yield() is a previously freed instance."; + break; + } + } + if (signal.length()==0) { + + err_text="Second argument of yield() is an empty string (for signal name)."; + break; + } + +#endif + Error err = obj->connect(signal,gdfs.ptr(),"_signal_callback",varray(gdfs),Object::CONNECT_ONESHOT); + if (err!=OK) { + err_text="Error connecting to signal: "+signal+" during yield()."; + break; + } + + + } + + exit_ok=true; + + } break; + case OPCODE_YIELD_RESUME: { + + CHECK_SPACE(2); + if (!p_state) { + err_text=("Invalid Resume (bug?)"); + break; + } + GET_VARIANT_PTR(result,1); + *result=p_state->result; + ip+=2; + + } continue; + case OPCODE_JUMP: { + + CHECK_SPACE(2); + int to = _code_ptr[ip+1]; + + ERR_BREAK(to<0 || to>_code_size); + ip=to; + + } continue; + case OPCODE_JUMP_IF: { + + CHECK_SPACE(3); + + GET_VARIANT_PTR(test,1); + + bool valid; + bool result = test->booleanize(valid); +#ifdef DEBUG_ENABLED + if (!valid) { + + err_text="cannot evaluate conditional expression of type: "+Variant::get_type_name(test->get_type()); + break; + } +#endif + if (result) { + int to = _code_ptr[ip+2]; + ERR_BREAK(to<0 || to>_code_size); + ip=to; + continue; + } + ip+=3; + } continue; + case OPCODE_JUMP_IF_NOT: { + + CHECK_SPACE(3); + + GET_VARIANT_PTR(test,1); + + bool valid; + bool result = test->booleanize(valid); +#ifdef DEBUG_ENABLED + if (!valid) { + + err_text="cannot evaluate conditional expression of type: "+Variant::get_type_name(test->get_type()); + break; + } +#endif + if (!result) { + int to = _code_ptr[ip+2]; + ERR_BREAK(to<0 || to>_code_size); + ip=to; + continue; + } + ip+=3; + } continue; + case OPCODE_JUMP_TO_DEF_ARGUMENT: { + + CHECK_SPACE(2); + ip=_default_arg_ptr[defarg]; + + } continue; + case OPCODE_RETURN: { + + CHECK_SPACE(2); + GET_VARIANT_PTR(r,1); + retvalue=*r; + exit_ok=true; + + } break; + case OPCODE_ITERATE_BEGIN: { + + CHECK_SPACE(8); //space for this an regular iterate + + GET_VARIANT_PTR(counter,1); + GET_VARIANT_PTR(container,2); + + bool valid; + if (!container->iter_init(*counter,valid)) { + if (!valid) { + err_text="Unable to iterate on object of type "+Variant::get_type_name(container->get_type())+"'."; + break; + } + int jumpto=_code_ptr[ip+3]; + ERR_BREAK(jumpto<0 || jumpto>_code_size); + ip=jumpto; + continue; + } + GET_VARIANT_PTR(iterator,4); + + + *iterator=container->iter_get(*counter,valid); + if (!valid) { + err_text="Unable to obtain iterator object of type "+Variant::get_type_name(container->get_type())+"'."; + break; + } + + + ip+=5; //skip regular iterate which is always next + + } continue; + case OPCODE_ITERATE: { + + CHECK_SPACE(4); + + GET_VARIANT_PTR(counter,1); + GET_VARIANT_PTR(container,2); + + bool valid; + if (!container->iter_next(*counter,valid)) { + if (!valid) { + err_text="Unable to iterate on object of type "+Variant::get_type_name(container->get_type())+"' (type changed since first iteration?)."; + break; + } + int jumpto=_code_ptr[ip+3]; + ERR_BREAK(jumpto<0 || jumpto>_code_size); + ip=jumpto; + continue; + } + GET_VARIANT_PTR(iterator,4); + + *iterator=container->iter_get(*counter,valid); + if (!valid) { + err_text="Unable to obtain iterator object of type "+Variant::get_type_name(container->get_type())+"' (but was obtained on first iteration?)."; + break; + } + + ip+=5; //loop again + } continue; + case OPCODE_ASSERT: { + CHECK_SPACE(2); + GET_VARIANT_PTR(test,1); + +#ifdef DEBUG_ENABLED + bool valid; + bool result = test->booleanize(valid); + + + if (!valid) { + + err_text="cannot evaluate conditional expression of type: "+Variant::get_type_name(test->get_type()); + break; + } + + + if (!result) { + + err_text="Assertion failed."; + break; + } + +#endif + + ip+=2; + } continue; + case OPCODE_BREAKPOINT: { +#ifdef DEBUG_ENABLED + if (ScriptDebugger::get_singleton()) { + GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement",true); + } +#endif + ip+=1; + } continue; + case OPCODE_LINE: { + CHECK_SPACE(2); + + line=_code_ptr[ip+1]; + ip+=2; + + if (ScriptDebugger::get_singleton()) { + // line + bool do_break=false; + + if (ScriptDebugger::get_singleton()->get_lines_left()>0) { + + if (ScriptDebugger::get_singleton()->get_depth()<=0) + ScriptDebugger::get_singleton()->set_lines_left( ScriptDebugger::get_singleton()->get_lines_left() -1 ); + if (ScriptDebugger::get_singleton()->get_lines_left()<=0) + do_break=true; + } + + if (ScriptDebugger::get_singleton()->is_breakpoint(line,source)) + do_break=true; + + if (do_break) { + GDScriptLanguage::get_singleton()->debug_break("Breakpoint",true); + } + + ScriptDebugger::get_singleton()->line_poll(); + + } + } continue; + case OPCODE_END: { + + exit_ok=true; + break; + + } break; + default: { + + err_text="Illegal opcode "+itos(_code_ptr[ip])+" at address "+itos(ip); + } break; + + } + + if (exit_ok) + break; + //error + // function, file, line, error, explanation + String err_file; + if (p_instance) + err_file=p_instance->script->path; + else if (_class) + err_file=_class->path; + if (err_file=="") + err_file="<built-in>"; + String err_func = name; + if (p_instance && p_instance->script->name!="") + err_func=p_instance->script->name+"."+err_func; + int err_line=line; + if (err_text=="") { + err_text="Internal Script Error! - opcode #"+itos(last_opcode)+" (report please)."; + } + + if (!GDScriptLanguage::get_singleton()->debug_break(err_text,false)) { + // debugger break did not happen + + _err_print_error(err_func.utf8().get_data(),err_file.utf8().get_data(),err_line,err_text.utf8().get_data(),ERR_HANDLER_SCRIPT); + } + + + break; + } + +#ifdef DEBUG_ENABLED + if (GDScriptLanguage::get_singleton()->profiling) { + uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time; + profile.total_time+=time_taken; + profile.self_time+=time_taken-function_call_time; + profile.frame_total_time+=time_taken; + profile.frame_self_time+=time_taken-function_call_time; + GDScriptLanguage::get_singleton()->script_frame_time+=time_taken-function_call_time; + + } + +#endif + if (ScriptDebugger::get_singleton()) + GDScriptLanguage::get_singleton()->exit_function(); + + + if (_stack_size) { + //free stack + for(int i=0;i<_stack_size;i++) + stack[i].~Variant(); + } + + return retvalue; + +} + +const int* GDFunction::get_code() const { + + return _code_ptr; +} +int GDFunction::get_code_size() const{ + + return _code_size; +} + +Variant GDFunction::get_constant(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,constants.size(),"<errconst>"); + return constants[p_idx]; +} + +StringName GDFunction::get_global_name(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,global_names.size(),"<errgname>"); + return global_names[p_idx]; +} + +int GDFunction::get_default_argument_count() const { + + return default_arguments.size(); +} +int GDFunction::get_default_argument_addr(int p_arg) const{ + + ERR_FAIL_INDEX_V(p_arg,default_arguments.size(),-1); + return default_arguments[p_arg]; +} + + +StringName GDFunction::get_name() const { + + return name; +} + +int GDFunction::get_max_stack_size() const { + + return _stack_size; +} + +struct _GDFKC { + + int order; + List<int> pos; +}; + +struct _GDFKCS { + + int order; + StringName id; + int pos; + + bool operator<(const _GDFKCS &p_r) const { + + return order<p_r.order; + } +}; + +void GDFunction::debug_get_stack_member_state(int p_line,List<Pair<StringName,int> > *r_stackvars) const { + + + int oc=0; + Map<StringName,_GDFKC> sdmap; + for( const List<StackDebug>::Element *E=stack_debug.front();E;E=E->next()) { + + const StackDebug &sd=E->get(); + if (sd.line>p_line) + break; + + if (sd.added) { + + if (!sdmap.has(sd.identifier)) { + _GDFKC d; + d.order=oc++; + d.pos.push_back(sd.pos); + sdmap[sd.identifier]=d; + + } else { + sdmap[sd.identifier].pos.push_back(sd.pos); + } + } else { + + + ERR_CONTINUE(!sdmap.has(sd.identifier)); + + sdmap[sd.identifier].pos.pop_back(); + if (sdmap[sd.identifier].pos.empty()) + sdmap.erase(sd.identifier); + } + + } + + + List<_GDFKCS> stackpositions; + for(Map<StringName,_GDFKC>::Element *E=sdmap.front();E;E=E->next() ) { + + _GDFKCS spp; + spp.id=E->key(); + spp.order=E->get().order; + spp.pos=E->get().pos.back()->get(); + stackpositions.push_back(spp); + } + + stackpositions.sort(); + + for(List<_GDFKCS>::Element *E=stackpositions.front();E;E=E->next()) { + + Pair<StringName,int> p; + p.first=E->get().id; + p.second=E->get().pos; + r_stackvars->push_back(p); + } + + +} + +#if 0 +void GDFunction::clear() { + + name=StringName(); + constants.clear(); + _stack_size=0; + code.clear(); + _constants_ptr=NULL; + _constant_count=0; + _global_names_ptr=NULL; + _global_names_count=0; + _code_ptr=NULL; + _code_size=0; + +} +#endif +GDFunction::GDFunction() : function_list(this) { + + _stack_size=0; + _call_size=0; + name="<anonymous>"; +#ifdef DEBUG_ENABLED + _func_cname=NULL; + + if (GDScriptLanguage::get_singleton()->lock) { + GDScriptLanguage::get_singleton()->lock->lock(); + } + GDScriptLanguage::get_singleton()->function_list.add(&function_list); + + if (GDScriptLanguage::get_singleton()->lock) { + GDScriptLanguage::get_singleton()->lock->unlock(); + } + + profile.call_count=0; + profile.self_time=0; + profile.total_time=0; + profile.frame_call_count=0; + profile.frame_self_time=0; + profile.frame_total_time=0; + profile.last_frame_call_count=0; + profile.last_frame_self_time=0; + profile.last_frame_total_time=0; + +#endif +} + +GDFunction::~GDFunction() { +#ifdef DEBUG_ENABLED + if (GDScriptLanguage::get_singleton()->lock) { + GDScriptLanguage::get_singleton()->lock->lock(); + } + GDScriptLanguage::get_singleton()->function_list.remove(&function_list); + + if (GDScriptLanguage::get_singleton()->lock) { + GDScriptLanguage::get_singleton()->lock->unlock(); + } +#endif +} + +///////////////////// + + +Variant GDFunctionState::_signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error) { + + Variant arg; + r_error.error=Variant::CallError::CALL_OK; + + ERR_FAIL_COND_V(!function,Variant()); + + if (p_argcount==0) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=1; + return Variant(); + } else if (p_argcount==1) { + //noooneee + } else if (p_argcount==2) { + arg=*p_args[0]; + } else { + Array extra_args; + for(int i=0;i<p_argcount-1;i++) { + extra_args.push_back(*p_args[i]); + } + arg=extra_args; + } + + Ref<GDFunctionState> self = *p_args[p_argcount-1]; + + if (self.is_null()) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=p_argcount-1; + r_error.expected=Variant::OBJECT; + return Variant(); + } + + state.result=arg; + Variant ret = function->call(NULL,NULL,0,r_error,&state); + function=NULL; //cleaned up; + state.result=Variant(); + return ret; +} + + +bool GDFunctionState::is_valid() const { + + return function!=NULL; +} + +Variant GDFunctionState::resume(const Variant& p_arg) { + + ERR_FAIL_COND_V(!function,Variant()); + + state.result=p_arg; + Variant::CallError err; + Variant ret = function->call(NULL,NULL,0,err,&state); + function=NULL; //cleaned up; + state.result=Variant(); + return ret; +} + + +void GDFunctionState::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("resume:Variant","arg"),&GDFunctionState::resume,DEFVAL(Variant())); + ObjectTypeDB::bind_method(_MD("is_valid"),&GDFunctionState::is_valid); + ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&GDFunctionState::_signal_callback,MethodInfo("_signal_callback")); + +} + +GDFunctionState::GDFunctionState() { + + function=NULL; +} + +GDFunctionState::~GDFunctionState() { + + if (function!=NULL) { + //never called, deinitialize stack + for(int i=0;i<state.stack_size;i++) { + Variant *v=(Variant*)&state.stack[sizeof(Variant)*i]; + v->~Variant(); + } + } +} + diff --git a/modules/gdscript/gd_function.h b/modules/gdscript/gd_function.h new file mode 100644 index 0000000000..1f790eaadc --- /dev/null +++ b/modules/gdscript/gd_function.h @@ -0,0 +1,207 @@ +#ifndef GD_FUNCTION_H +#define GD_FUNCTION_H + +#include "self_list.h" +#include "os/thread.h" +#include "pair.h" +#include "variant.h" +#include "string_db.h" +#include "reference.h" + +class GDInstance; +class GDScript; + + +class GDFunction { +public: + + enum Opcode { + OPCODE_OPERATOR, + OPCODE_EXTENDS_TEST, + OPCODE_SET, + OPCODE_GET, + OPCODE_SET_NAMED, + OPCODE_GET_NAMED, + OPCODE_ASSIGN, + OPCODE_ASSIGN_TRUE, + OPCODE_ASSIGN_FALSE, + OPCODE_CONSTRUCT, //only for basic types!! + OPCODE_CONSTRUCT_ARRAY, + OPCODE_CONSTRUCT_DICTIONARY, + OPCODE_CALL, + OPCODE_CALL_RETURN, + OPCODE_CALL_BUILT_IN, + OPCODE_CALL_SELF, + OPCODE_CALL_SELF_BASE, + OPCODE_YIELD, + OPCODE_YIELD_SIGNAL, + OPCODE_YIELD_RESUME, + OPCODE_JUMP, + OPCODE_JUMP_IF, + OPCODE_JUMP_IF_NOT, + OPCODE_JUMP_TO_DEF_ARGUMENT, + OPCODE_RETURN, + OPCODE_ITERATE_BEGIN, + OPCODE_ITERATE, + OPCODE_ASSERT, + OPCODE_BREAKPOINT, + OPCODE_LINE, + OPCODE_END + }; + + enum Address { + ADDR_BITS=24, + ADDR_MASK=((1<<ADDR_BITS)-1), + ADDR_TYPE_MASK=~ADDR_MASK, + ADDR_TYPE_SELF=0, + ADDR_TYPE_CLASS=1, + ADDR_TYPE_MEMBER=2, + ADDR_TYPE_CLASS_CONSTANT=3, + ADDR_TYPE_LOCAL_CONSTANT=4, + ADDR_TYPE_STACK=5, + ADDR_TYPE_STACK_VARIABLE=6, + ADDR_TYPE_GLOBAL=7, + ADDR_TYPE_NIL=8 + }; + + struct StackDebug { + + int line; + int pos; + bool added; + StringName identifier; + }; + +private: +friend class GDCompiler; + + StringName source; + + mutable Variant nil; + mutable Variant *_constants_ptr; + int _constant_count; + const StringName *_global_names_ptr; + int _global_names_count; + const int *_default_arg_ptr; + int _default_arg_count; + const int *_code_ptr; + int _code_size; + int _argument_count; + int _stack_size; + int _call_size; + int _initial_line; + bool _static; + GDScript *_script; + + StringName name; + Vector<Variant> constants; + Vector<StringName> global_names; + Vector<int> default_arguments; + Vector<int> code; + +#ifdef TOOLS_ENABLED + Vector<StringName> arg_names; +#endif + + List<StackDebug> stack_debug; + + _FORCE_INLINE_ Variant *_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self,Variant *p_stack,String& r_error) const; + _FORCE_INLINE_ String _get_call_error(const Variant::CallError& p_err, const String& p_where,const Variant**argptrs) const; + +friend class GDScriptLanguage; + + SelfList<GDFunction> function_list; +#ifdef DEBUG_ENABLED + CharString func_cname; + const char*_func_cname; + + struct Profile { + StringName signature; + uint64_t call_count; + uint64_t self_time; + uint64_t total_time; + uint64_t frame_call_count; + uint64_t frame_self_time; + uint64_t frame_total_time; + uint64_t last_frame_call_count; + uint64_t last_frame_self_time; + uint64_t last_frame_total_time; + } profile; + +#endif + +public: + + + + struct CallState { + + GDInstance *instance; + Vector<uint8_t> stack; + int stack_size; + Variant self; + uint32_t alloca_size; + GDScript *_class; + int ip; + int line; + int defarg; + Variant result; + + }; + + _FORCE_INLINE_ bool is_static() const { return _static; } + + const int* get_code() const; //used for debug + int get_code_size() const; + Variant get_constant(int p_idx) const; + StringName get_global_name(int p_idx) const; + StringName get_name() const; + int get_max_stack_size() const; + int get_default_argument_count() const; + int get_default_argument_addr(int p_idx) const; + GDScript *get_script() const { return _script; } + + void debug_get_stack_member_state(int p_line,List<Pair<StringName,int> > *r_stackvars) const; + + _FORCE_INLINE_ bool is_empty() const { return _code_size==0; } + + int get_argument_count() const { return _argument_count; } + StringName get_argument_name(int p_idx) const { +#ifdef TOOLS_ENABLED + ERR_FAIL_INDEX_V(p_idx,arg_names.size(),StringName()); + return arg_names[p_idx]; +#endif + return StringName(); + + } + Variant get_default_argument(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx,default_arguments.size(),Variant()); + return default_arguments[p_idx]; + } + + Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err,CallState *p_state=NULL); + + GDFunction(); + ~GDFunction(); +}; + + +class GDFunctionState : public Reference { + + OBJ_TYPE(GDFunctionState,Reference); +friend class GDFunction; + GDFunction *function; + GDFunction::CallState state; + Variant _signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error); +protected: + static void _bind_methods(); +public: + + bool is_valid() const; + Variant resume(const Variant& p_arg=Variant()); + GDFunctionState(); + ~GDFunctionState(); +}; + + +#endif // GD_FUNCTION_H diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index 9b7d8eeac4..5ea5908c5f 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -1010,11 +1010,11 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va VALIDATE_ARG_NUM(1); VALIDATE_ARG_NUM(2); - Color color(*p_args[0],*p_args[1],*p_args[2]); + Color color((float)*p_args[0]/255.0f,(float)*p_args[1]/255.0f,(float)*p_args[2]/255.0f); if (p_arg_count==4) { VALIDATE_ARG_NUM(3); - color.a=*p_args[3]; + color.a=(float)*p_args[3]/255.0f; } r_ret=color; @@ -1046,7 +1046,7 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va } break; case FUNC_MAX: { - ERR_FAIL_V(); + ERR_FAIL(); } break; } @@ -1226,12 +1226,12 @@ MethodInfo GDFunctions::get_info(Function p_func) { return mi; } break; case MATH_ISNAN: { - MethodInfo mi("isnan",PropertyInfo(Variant::REAL,"s")); + MethodInfo mi("is_nan",PropertyInfo(Variant::REAL,"s")); mi.return_val.type=Variant::REAL; return mi; } break; case MATH_ISINF: { - MethodInfo mi("isinf",PropertyInfo(Variant::REAL,"s")); + MethodInfo mi("is_inf",PropertyInfo(Variant::REAL,"s")); mi.return_val.type=Variant::REAL; return mi; } break; diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index b713dc318f..9e90027a70 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -86,7 +86,7 @@ bool GDParser::_enter_indent_block(BlockNode* p_block) { while(true) { if (tokenizer->get_token()!=GDTokenizer::TK_NEWLINE) { - print_line("no newline"); + return false; //wtf } else if (tokenizer->get_token(1)!=GDTokenizer::TK_NEWLINE) { diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index beec314e44..12fc36d8c1 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -32,1363 +32,7 @@ #include "gd_compiler.h" #include "os/file_access.h" #include "io/file_access_encrypted.h" - - - - -Variant *GDFunction::_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self, Variant *p_stack,String& r_error) const{ - - int address = p_address&ADDR_MASK; - - //sequential table (jump table generated by compiler) - switch((p_address&ADDR_TYPE_MASK)>>ADDR_BITS) { - - case ADDR_TYPE_SELF: { - - if (!p_instance) { - r_error="Cannot access self without instance."; - return NULL; - } - return &self; - } break; - case ADDR_TYPE_CLASS: { - - return &p_script->_static_ref; - } break; - case ADDR_TYPE_MEMBER: { - //member indexing is O(1) - if (!p_instance) { - r_error="Cannot access member without instance."; - return NULL; - } - return &p_instance->members[address]; - } break; - case ADDR_TYPE_CLASS_CONSTANT: { - - //todo change to index! - GDScript *o=p_script; - ERR_FAIL_INDEX_V(address,_global_names_count,NULL); - const StringName *sn = &_global_names_ptr[address]; - - while(o) { - GDScript *s=o; - while(s) { - - Map<StringName,Variant>::Element *E=s->constants.find(*sn); - if (E) { - return &E->get(); - } - s=s->_base; - } - o=o->_owner; - } - - - ERR_EXPLAIN("GDCompiler bug.."); - ERR_FAIL_V(NULL); - } break; - case ADDR_TYPE_LOCAL_CONSTANT: { - ERR_FAIL_INDEX_V(address,_constant_count,NULL); - return &_constants_ptr[address]; - } break; - case ADDR_TYPE_STACK: - case ADDR_TYPE_STACK_VARIABLE: { - ERR_FAIL_INDEX_V(address,_stack_size,NULL); - return &p_stack[address]; - } break; - case ADDR_TYPE_GLOBAL: { - - - ERR_FAIL_INDEX_V(address,GDScriptLanguage::get_singleton()->get_global_array_size(),NULL); - - - return &GDScriptLanguage::get_singleton()->get_global_array()[address]; - } break; - case ADDR_TYPE_NIL: { - return &nil; - } break; - } - - ERR_EXPLAIN("Bad Code! (Addressing Mode)"); - ERR_FAIL_V(NULL); - return NULL; -} - - -String GDFunction::_get_call_error(const Variant::CallError& p_err, const String& p_where,const Variant**argptrs) const { - - - - String err_text; - - if (p_err.error==Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { - int errorarg=p_err.argument; - err_text="Invalid type in "+p_where+". Cannot convert argument "+itos(errorarg+1)+" from "+Variant::get_type_name(argptrs[errorarg]->get_type())+" to "+Variant::get_type_name(p_err.expected)+"."; - } else if (p_err.error==Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { - err_text="Invalid call to "+p_where+". Expected "+itos(p_err.argument)+" arguments."; - } else if (p_err.error==Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { - err_text="Invalid call to "+p_where+". Expected "+itos(p_err.argument)+" arguments."; - } else if (p_err.error==Variant::CallError::CALL_ERROR_INVALID_METHOD) { - err_text="Invalid call. Nonexistent "+p_where+"."; - } else if (p_err.error==Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) { - err_text="Attempt to call "+p_where+" on a null instance."; - } else { - err_text="Bug, call error: #"+itos(p_err.error); - } - - return err_text; - -} - -static String _get_var_type(const Variant* p_type) { - - String basestr; - - if (p_type->get_type()==Variant::OBJECT) { - Object *bobj = *p_type; - if (!bobj) { - basestr = "null instance"; - } else { -#ifdef DEBUG_ENABLED - if (ObjectDB::instance_validate(bobj)) { - if (bobj->get_script_instance()) - basestr= bobj->get_type()+" ("+bobj->get_script_instance()->get_script()->get_path().get_file()+")"; - else - basestr = bobj->get_type(); - } else { - basestr="previously freed instance"; - } - -#else - basestr="Object"; -#endif - } - - } else { - basestr = Variant::get_type_name(p_type->get_type()); - } - - return basestr; - -} - -Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError& r_err, CallState *p_state) { - - - if (!_code_ptr) { - - return Variant(); - } - - r_err.error=Variant::CallError::CALL_OK; - - Variant self; - Variant retvalue; - Variant *stack = NULL; - Variant **call_args; - int defarg=0; - -#ifdef DEBUG_ENABLED - - //GDScriptLanguage::get_singleton()->calls++; - -#endif - - uint32_t alloca_size=0; - GDScript *_class; - int ip=0; - int line=_initial_line; - - if (p_state) { - //use existing (supplied) state (yielded) - stack=(Variant*)p_state->stack.ptr(); - call_args=(Variant**)&p_state->stack[sizeof(Variant)*p_state->stack_size]; - line=p_state->line; - ip=p_state->ip; - alloca_size=p_state->stack.size(); - _class=p_state->_class; - p_instance=p_state->instance; - defarg=p_state->defarg; - self=p_state->self; - //stack[p_state->result_pos]=p_state->result; //assign stack with result - - } else { - - if (p_argcount!=_argument_count) { - - if (p_argcount>_argument_count) { - - r_err.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_err.argument=_argument_count; - - - return Variant(); - } else if (p_argcount < _argument_count - _default_arg_count) { - - r_err.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_err.argument=_argument_count - _default_arg_count; - return Variant(); - } else { - - defarg=_argument_count-p_argcount; - } - } - - alloca_size = sizeof(Variant*)*_call_size + sizeof(Variant)*_stack_size; - - if (alloca_size) { - - uint8_t *aptr = (uint8_t*)alloca(alloca_size); - - if (_stack_size) { - - stack=(Variant*)aptr; - for(int i=0;i<p_argcount;i++) - memnew_placement(&stack[i],Variant(*p_args[i])); - for(int i=p_argcount;i<_stack_size;i++) - memnew_placement(&stack[i],Variant); - } else { - stack=NULL; - } - - if (_call_size) { - - call_args = (Variant**)&aptr[sizeof(Variant)*_stack_size]; - } else { - - call_args=NULL; - } - - - } else { - stack=NULL; - call_args=NULL; - } - - if (p_instance) { - if (p_instance->base_ref && static_cast<Reference*>(p_instance->owner)->is_referenced()) { - - self=REF(static_cast<Reference*>(p_instance->owner)); - } else { - self=p_instance->owner; - } - _class=p_instance->script.ptr(); - } else { - _class=_script; - } - } - - String err_text; - -#ifdef DEBUG_ENABLED - - if (ScriptDebugger::get_singleton()) - GDScriptLanguage::get_singleton()->enter_function(p_instance,this,stack,&ip,&line); - -#define CHECK_SPACE(m_space)\ - ERR_BREAK((ip+m_space)>_code_size) - -#define GET_VARIANT_PTR(m_v,m_code_ofs) \ - Variant *m_v; \ - m_v = _get_variant(_code_ptr[ip+m_code_ofs],p_instance,_class,self,stack,err_text);\ - if (!m_v)\ - break; - - -#else -#define CHECK_SPACE(m_space) -#define GET_VARIANT_PTR(m_v,m_code_ofs) \ - Variant *m_v; \ - m_v = _get_variant(_code_ptr[ip+m_code_ofs],p_instance,_class,self,stack,err_text); - -#endif - - - - bool exit_ok=false; - - while(ip<_code_size) { - - - int last_opcode=_code_ptr[ip]; - switch(_code_ptr[ip]) { - - case OPCODE_OPERATOR: { - - CHECK_SPACE(5); - - bool valid; - Variant::Operator op = (Variant::Operator)_code_ptr[ip+1]; - ERR_BREAK(op>=Variant::OP_MAX); - - GET_VARIANT_PTR(a,2); - GET_VARIANT_PTR(b,3); - GET_VARIANT_PTR(dst,4); - -#ifdef DEBUG_ENABLED - Variant ret; - Variant::evaluate(op,*a,*b,ret,valid); -#else - Variant::evaluate(op,*a,*b,*dst,valid); -#endif - - if (!valid) { -#ifdef DEBUG_ENABLED - - if (ret.get_type()==Variant::STRING) { - //return a string when invalid with the error - err_text=ret; - err_text += " in operator '"+Variant::get_operator_name(op)+"'."; - } else { - err_text="Invalid operands '"+Variant::get_type_name(a->get_type())+"' and '"+Variant::get_type_name(b->get_type())+"' in operator '"+Variant::get_operator_name(op)+"'."; - } -#endif - break; - - } -#ifdef DEBUG_ENABLED - *dst=ret; -#endif - - ip+=5; - - } continue; - case OPCODE_EXTENDS_TEST: { - - CHECK_SPACE(4); - - GET_VARIANT_PTR(a,1); - GET_VARIANT_PTR(b,2); - GET_VARIANT_PTR(dst,3); - -#ifdef DEBUG_ENABLED - - if (a->get_type()!=Variant::OBJECT || a->operator Object*()==NULL) { - - err_text="Left operand of 'extends' is not an instance of anything."; - break; - - } - if (b->get_type()!=Variant::OBJECT || b->operator Object*()==NULL) { - - err_text="Right operand of 'extends' is not a class."; - break; - - } -#endif - - - Object *obj_A = *a; - Object *obj_B = *b; - - - GDScript *scr_B = obj_B->cast_to<GDScript>(); - - bool extends_ok=false; - - if (scr_B) { - //if B is a script, the only valid condition is that A has an instance which inherits from the script - //in other situation, this shoul return false. - - if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language()==GDScriptLanguage::get_singleton()) { - - GDInstance *ins = static_cast<GDInstance*>(obj_A->get_script_instance()); - GDScript *cmp = ins->script.ptr(); - //bool found=false; - while(cmp) { - - if (cmp==scr_B) { - //inherits from script, all ok - extends_ok=true; - break; - - } - - cmp=cmp->_base; - } - - } - - - } else { - - GDNativeClass *nc= obj_B->cast_to<GDNativeClass>(); - - if (!nc) { - - err_text="Right operand of 'extends' is not a class (type: '"+obj_B->get_type()+"')."; - break; - } - - extends_ok=ObjectTypeDB::is_type(obj_A->get_type_name(),nc->get_name()); - } - - *dst=extends_ok; - ip+=4; - - } continue; - case OPCODE_SET: { - - CHECK_SPACE(3); - - GET_VARIANT_PTR(dst,1); - GET_VARIANT_PTR(index,2); - GET_VARIANT_PTR(value,3); - - bool valid; - dst->set(*index,*value,&valid); - - if (!valid) { - String v = index->operator String(); - if (v!="") { - v="'"+v+"'"; - } else { - v="of type '"+_get_var_type(index)+"'"; - } - err_text="Invalid set index "+v+" (on base: '"+_get_var_type(dst)+"')."; - break; - } - - ip+=4; - } continue; - case OPCODE_GET: { - - CHECK_SPACE(3); - - GET_VARIANT_PTR(src,1); - GET_VARIANT_PTR(index,2); - GET_VARIANT_PTR(dst,3); - - bool valid; -#ifdef DEBUG_ENABLED -//allow better error message in cases where src and dst are the same stack position - Variant ret = src->get(*index,&valid); -#else - *dst = src->get(*index,&valid); - -#endif - if (!valid) { - String v = index->operator String(); - if (v!="") { - v="'"+v+"'"; - } else { - v="of type '"+_get_var_type(index)+"'"; - } - err_text="Invalid get index "+v+" (on base: '"+_get_var_type(src)+"')."; - break; - } -#ifdef DEBUG_ENABLED - *dst=ret; -#endif - ip+=4; - } continue; - case OPCODE_SET_NAMED: { - - CHECK_SPACE(3); - - GET_VARIANT_PTR(dst,1); - GET_VARIANT_PTR(value,3); - - int indexname = _code_ptr[ip+2]; - - ERR_BREAK(indexname<0 || indexname>=_global_names_count); - const StringName *index = &_global_names_ptr[indexname]; - - bool valid; - dst->set_named(*index,*value,&valid); - - if (!valid) { - String err_type; - err_text="Invalid set index '"+String(*index)+"' (on base: '"+_get_var_type(dst)+"')."; - break; - } - - ip+=4; - } continue; - case OPCODE_GET_NAMED: { - - - CHECK_SPACE(3); - - GET_VARIANT_PTR(src,1); - GET_VARIANT_PTR(dst,3); - - int indexname = _code_ptr[ip+2]; - - ERR_BREAK(indexname<0 || indexname>=_global_names_count); - const StringName *index = &_global_names_ptr[indexname]; - - bool valid; -#ifdef DEBUG_ENABLED -//allow better error message in cases where src and dst are the same stack position - Variant ret = src->get_named(*index,&valid); - -#else - *dst = src->get_named(*index,&valid); -#endif - - if (!valid) { - if (src->has_method(*index)) { - err_text="Invalid get index '"+index->operator String()+"' (on base: '"+_get_var_type(src)+"'). Did you mean '."+index->operator String()+"()' ?"; - } else { - err_text="Invalid get index '"+index->operator String()+"' (on base: '"+_get_var_type(src)+"')."; - } - break; - } -#ifdef DEBUG_ENABLED - *dst=ret; -#endif - ip+=4; - } continue; - case OPCODE_ASSIGN: { - - CHECK_SPACE(3); - GET_VARIANT_PTR(dst,1); - GET_VARIANT_PTR(src,2); - - *dst = *src; - - ip+=3; - - } continue; - case OPCODE_ASSIGN_TRUE: { - - CHECK_SPACE(2); - GET_VARIANT_PTR(dst,1); - - *dst = true; - - ip+=2; - } continue; - case OPCODE_ASSIGN_FALSE: { - - CHECK_SPACE(2); - GET_VARIANT_PTR(dst,1); - - *dst = false; - - ip+=2; - } continue; - case OPCODE_CONSTRUCT: { - - CHECK_SPACE(2); - Variant::Type t=Variant::Type(_code_ptr[ip+1]); - int argc=_code_ptr[ip+2]; - CHECK_SPACE(argc+2); - Variant **argptrs = call_args; - for(int i=0;i<argc;i++) { - GET_VARIANT_PTR(v,3+i); - argptrs[i]=v; - } - - GET_VARIANT_PTR(dst,3+argc); - Variant::CallError err; - *dst = Variant::construct(t,(const Variant**)argptrs,argc,err); - - if (err.error!=Variant::CallError::CALL_OK) { - - err_text=_get_call_error(err,"'"+Variant::get_type_name(t)+"' constructor",(const Variant**)argptrs); - break; - } - - ip+=4+argc; - //construct a basic type - } continue; - case OPCODE_CONSTRUCT_ARRAY: { - - CHECK_SPACE(1); - int argc=_code_ptr[ip+1]; - Array array(true); //arrays are always shared - array.resize(argc); - CHECK_SPACE(argc+2); - - for(int i=0;i<argc;i++) { - GET_VARIANT_PTR(v,2+i); - array[i]=*v; - - } - - GET_VARIANT_PTR(dst,2+argc); - - *dst=array; - - ip+=3+argc; - - } continue; - case OPCODE_CONSTRUCT_DICTIONARY: { - - CHECK_SPACE(1); - int argc=_code_ptr[ip+1]; - Dictionary dict(true); //arrays are always shared - - CHECK_SPACE(argc*2+2); - - for(int i=0;i<argc;i++) { - - GET_VARIANT_PTR(k,2+i*2+0); - GET_VARIANT_PTR(v,2+i*2+1); - dict[*k]=*v; - - } - - GET_VARIANT_PTR(dst,2+argc*2); - - *dst=dict; - - ip+=3+argc*2; - - } continue; - case OPCODE_CALL_RETURN: - case OPCODE_CALL: { - - - CHECK_SPACE(4); - bool call_ret = _code_ptr[ip]==OPCODE_CALL_RETURN; - - int argc=_code_ptr[ip+1]; - GET_VARIANT_PTR(base,2); - int nameg=_code_ptr[ip+3]; - - ERR_BREAK(nameg<0 || nameg>=_global_names_count); - const StringName *methodname = &_global_names_ptr[nameg]; - - ERR_BREAK(argc<0); - ip+=4; - CHECK_SPACE(argc+1); - Variant **argptrs = call_args; - - for(int i=0;i<argc;i++) { - GET_VARIANT_PTR(v,i); - argptrs[i]=v; - } - - Variant::CallError err; - if (call_ret) { - - GET_VARIANT_PTR(ret,argc); - *ret = base->call(*methodname,(const Variant**)argptrs,argc,err); - } else { - - base->call(*methodname,(const Variant**)argptrs,argc,err); - } - - if (err.error!=Variant::CallError::CALL_OK) { - - - String methodstr = *methodname; - String basestr = _get_var_type(base); - - if (methodstr=="call") { - if (argc>=1) { - methodstr=String(*argptrs[0])+" (via call)"; - if (err.error==Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { - err.argument-=1; - } - } - } if (methodstr=="free") { - - if (err.error==Variant::CallError::CALL_ERROR_INVALID_METHOD) { - - if (base->is_ref()) { - err_text="Attempted to free a reference."; - break; - } else if (base->get_type()==Variant::OBJECT) { - - err_text="Attempted to free a locked object (calling or emitting)."; - break; - } - } - } - err_text=_get_call_error(err,"function '"+methodstr+"' in base '"+basestr+"'",(const Variant**)argptrs); - break; - } - - //_call_func(NULL,base,*methodname,ip,argc,p_instance,stack); - ip+=argc+1; - - } continue; - case OPCODE_CALL_BUILT_IN: { - - CHECK_SPACE(4); - - GDFunctions::Function func = GDFunctions::Function(_code_ptr[ip+1]); - int argc=_code_ptr[ip+2]; - ERR_BREAK(argc<0); - - ip+=3; - CHECK_SPACE(argc+1); - Variant **argptrs = call_args; - - for(int i=0;i<argc;i++) { - GET_VARIANT_PTR(v,i); - argptrs[i]=v; - } - - GET_VARIANT_PTR(dst,argc); - - Variant::CallError err; - - GDFunctions::call(func,(const Variant**)argptrs,argc,*dst,err); - - if (err.error!=Variant::CallError::CALL_OK) { - - - String methodstr = GDFunctions::get_func_name(func); - err_text=_get_call_error(err,"built-in function '"+methodstr+"'",(const Variant**)argptrs); - break; - } - ip+=argc+1; - - } continue; - case OPCODE_CALL_SELF: { - - - } break; - case OPCODE_CALL_SELF_BASE: { - - CHECK_SPACE(2); - int self_fun = _code_ptr[ip+1]; -#ifdef DEBUG_ENABLED - - if (self_fun<0 || self_fun>=_global_names_count) { - - err_text="compiler bug, function name not found"; - break; - } -#endif - const StringName *methodname = &_global_names_ptr[self_fun]; - - int argc=_code_ptr[ip+2]; - - CHECK_SPACE(2+argc+1); - - Variant **argptrs = call_args; - - for(int i=0;i<argc;i++) { - GET_VARIANT_PTR(v,i+3); - argptrs[i]=v; - } - - GET_VARIANT_PTR(dst,argc+3); - - const GDScript *gds = _script; - - - const Map<StringName,GDFunction>::Element *E=NULL; - while (gds->base.ptr()) { - gds=gds->base.ptr(); - E=gds->member_functions.find(*methodname); - if (E) - break; - } - - Variant::CallError err; - - if (E) { - - *dst=((GDFunction*)&E->get())->call(p_instance,(const Variant**)argptrs,argc,err); - } else if (gds->native.ptr()) { - - if (*methodname!=GDScriptLanguage::get_singleton()->strings._init) { - - MethodBind *mb = ObjectTypeDB::get_method(gds->native->get_name(),*methodname); - if (!mb) { - err.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; - } else { - *dst=mb->call(p_instance->owner,(const Variant**)argptrs,argc,err); - } - } else { - err.error=Variant::CallError::CALL_OK; - } - } else { - - if (*methodname!=GDScriptLanguage::get_singleton()->strings._init) { - err.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; - } else { - err.error=Variant::CallError::CALL_OK; - } - } - - - if (err.error!=Variant::CallError::CALL_OK) { - - - String methodstr = *methodname; - err_text=_get_call_error(err,"function '"+methodstr+"'",(const Variant**)argptrs); - - break; - } - - ip+=4+argc; - - } continue; - case OPCODE_YIELD: - case OPCODE_YIELD_SIGNAL: { - - int ipofs=1; - if (_code_ptr[ip]==OPCODE_YIELD_SIGNAL) { - CHECK_SPACE(4); - ipofs+=2; - } else { - CHECK_SPACE(2); - - } - - Ref<GDFunctionState> gdfs = memnew( GDFunctionState ); - gdfs->function=this; - - gdfs->state.stack.resize(alloca_size); - //copy variant stack - for(int i=0;i<_stack_size;i++) { - memnew_placement(&gdfs->state.stack[sizeof(Variant)*i],Variant(stack[i])); - } - gdfs->state.stack_size=_stack_size; - gdfs->state.self=self; - gdfs->state.alloca_size=alloca_size; - gdfs->state._class=_class; - gdfs->state.ip=ip+ipofs; - gdfs->state.line=line; - //gdfs->state.result_pos=ip+ipofs-1; - gdfs->state.defarg=defarg; - gdfs->state.instance=p_instance; - gdfs->function=this; - - retvalue=gdfs; - - if (_code_ptr[ip]==OPCODE_YIELD_SIGNAL) { - GET_VARIANT_PTR(argobj,1); - GET_VARIANT_PTR(argname,2); - //do the oneshot connect - - if (argobj->get_type()!=Variant::OBJECT) { - err_text="First argument of yield() not of type object."; - break; - } - if (argname->get_type()!=Variant::STRING) { - err_text="Second argument of yield() not a string (for signal name)."; - break; - } - Object *obj=argobj->operator Object *(); - String signal = argname->operator String(); -#ifdef DEBUG_ENABLED - - if (!obj) { - err_text="First argument of yield() is null."; - break; - } - if (ScriptDebugger::get_singleton()) { - if (!ObjectDB::instance_validate(obj)) { - err_text="First argument of yield() is a previously freed instance."; - break; - } - } - if (signal.length()==0) { - - err_text="Second argument of yield() is an empty string (for signal name)."; - break; - } - -#endif - Error err = obj->connect(signal,gdfs.ptr(),"_signal_callback",varray(gdfs),Object::CONNECT_ONESHOT); - if (err!=OK) { - err_text="Error connecting to signal: "+signal+" during yield()."; - break; - } - - - } - - exit_ok=true; - - } break; - case OPCODE_YIELD_RESUME: { - - CHECK_SPACE(2); - if (!p_state) { - err_text=("Invalid Resume (bug?)"); - break; - } - GET_VARIANT_PTR(result,1); - *result=p_state->result; - ip+=2; - - } continue; - case OPCODE_JUMP: { - - CHECK_SPACE(2); - int to = _code_ptr[ip+1]; - - ERR_BREAK(to<0 || to>_code_size); - ip=to; - - } continue; - case OPCODE_JUMP_IF: { - - CHECK_SPACE(3); - - GET_VARIANT_PTR(test,1); - - bool valid; - bool result = test->booleanize(valid); -#ifdef DEBUG_ENABLED - if (!valid) { - - err_text="cannot evaluate conditional expression of type: "+Variant::get_type_name(test->get_type()); - break; - } -#endif - if (result) { - int to = _code_ptr[ip+2]; - ERR_BREAK(to<0 || to>_code_size); - ip=to; - continue; - } - ip+=3; - } continue; - case OPCODE_JUMP_IF_NOT: { - - CHECK_SPACE(3); - - GET_VARIANT_PTR(test,1); - - bool valid; - bool result = test->booleanize(valid); -#ifdef DEBUG_ENABLED - if (!valid) { - - err_text="cannot evaluate conditional expression of type: "+Variant::get_type_name(test->get_type()); - break; - } -#endif - if (!result) { - int to = _code_ptr[ip+2]; - ERR_BREAK(to<0 || to>_code_size); - ip=to; - continue; - } - ip+=3; - } continue; - case OPCODE_JUMP_TO_DEF_ARGUMENT: { - - CHECK_SPACE(2); - ip=_default_arg_ptr[defarg]; - - } continue; - case OPCODE_RETURN: { - - CHECK_SPACE(2); - GET_VARIANT_PTR(r,1); - retvalue=*r; - exit_ok=true; - - } break; - case OPCODE_ITERATE_BEGIN: { - - CHECK_SPACE(8); //space for this an regular iterate - - GET_VARIANT_PTR(counter,1); - GET_VARIANT_PTR(container,2); - - bool valid; - if (!container->iter_init(*counter,valid)) { - if (!valid) { - err_text="Unable to iterate on object of type "+Variant::get_type_name(container->get_type())+"'."; - break; - } - int jumpto=_code_ptr[ip+3]; - ERR_BREAK(jumpto<0 || jumpto>_code_size); - ip=jumpto; - continue; - } - GET_VARIANT_PTR(iterator,4); - - - *iterator=container->iter_get(*counter,valid); - if (!valid) { - err_text="Unable to obtain iterator object of type "+Variant::get_type_name(container->get_type())+"'."; - break; - } - - - ip+=5; //skip regular iterate which is always next - - } continue; - case OPCODE_ITERATE: { - - CHECK_SPACE(4); - - GET_VARIANT_PTR(counter,1); - GET_VARIANT_PTR(container,2); - - bool valid; - if (!container->iter_next(*counter,valid)) { - if (!valid) { - err_text="Unable to iterate on object of type "+Variant::get_type_name(container->get_type())+"' (type changed since first iteration?)."; - break; - } - int jumpto=_code_ptr[ip+3]; - ERR_BREAK(jumpto<0 || jumpto>_code_size); - ip=jumpto; - continue; - } - GET_VARIANT_PTR(iterator,4); - - *iterator=container->iter_get(*counter,valid); - if (!valid) { - err_text="Unable to obtain iterator object of type "+Variant::get_type_name(container->get_type())+"' (but was obtained on first iteration?)."; - break; - } - - ip+=5; //loop again - } continue; - case OPCODE_ASSERT: { - CHECK_SPACE(2); - GET_VARIANT_PTR(test,1); - -#ifdef DEBUG_ENABLED - bool valid; - bool result = test->booleanize(valid); - - - if (!valid) { - - err_text="cannot evaluate conditional expression of type: "+Variant::get_type_name(test->get_type()); - break; - } - - - if (!result) { - - err_text="Assertion failed."; - break; - } - -#endif - - ip+=2; - } continue; - case OPCODE_BREAKPOINT: { -#ifdef DEBUG_ENABLED - if (ScriptDebugger::get_singleton()) { - GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement",true); - } -#endif - ip+=1; - } continue; - case OPCODE_LINE: { - CHECK_SPACE(2); - - line=_code_ptr[ip+1]; - ip+=2; - - if (ScriptDebugger::get_singleton()) { - // line - bool do_break=false; - - if (ScriptDebugger::get_singleton()->get_lines_left()>0) { - - if (ScriptDebugger::get_singleton()->get_depth()<=0) - ScriptDebugger::get_singleton()->set_lines_left( ScriptDebugger::get_singleton()->get_lines_left() -1 ); - if (ScriptDebugger::get_singleton()->get_lines_left()<=0) - do_break=true; - } - - if (ScriptDebugger::get_singleton()->is_breakpoint(line,source)) - do_break=true; - - if (do_break) { - GDScriptLanguage::get_singleton()->debug_break("Breakpoint",true); - } - - ScriptDebugger::get_singleton()->line_poll(); - - } - } continue; - case OPCODE_END: { - - exit_ok=true; - break; - - } break; - default: { - - err_text="Illegal opcode "+itos(_code_ptr[ip])+" at address "+itos(ip); - } break; - - } - - if (exit_ok) - break; - //error - // function, file, line, error, explanation - String err_file; - if (p_instance) - err_file=p_instance->script->path; - else if (_class) - err_file=_class->path; - if (err_file=="") - err_file="<built-in>"; - String err_func = name; - if (p_instance && p_instance->script->name!="") - err_func=p_instance->script->name+"."+err_func; - int err_line=line; - if (err_text=="") { - err_text="Internal Script Error! - opcode #"+itos(last_opcode)+" (report please)."; - } - - if (!GDScriptLanguage::get_singleton()->debug_break(err_text,false)) { - // debugger break did not happen - - _err_print_error(err_func.utf8().get_data(),err_file.utf8().get_data(),err_line,err_text.utf8().get_data(),ERR_HANDLER_SCRIPT); - } - - - break; - } - - if (ScriptDebugger::get_singleton()) - GDScriptLanguage::get_singleton()->exit_function(); - - - if (_stack_size) { - //free stack - for(int i=0;i<_stack_size;i++) - stack[i].~Variant(); - } - - return retvalue; - -} - -const int* GDFunction::get_code() const { - - return _code_ptr; -} -int GDFunction::get_code_size() const{ - - return _code_size; -} - -Variant GDFunction::get_constant(int p_idx) const { - - ERR_FAIL_INDEX_V(p_idx,constants.size(),"<errconst>"); - return constants[p_idx]; -} - -StringName GDFunction::get_global_name(int p_idx) const { - - ERR_FAIL_INDEX_V(p_idx,global_names.size(),"<errgname>"); - return global_names[p_idx]; -} - -int GDFunction::get_default_argument_count() const { - - return default_arguments.size(); -} -int GDFunction::get_default_argument_addr(int p_arg) const{ - - ERR_FAIL_INDEX_V(p_arg,default_arguments.size(),-1); - return default_arguments[p_arg]; -} - - -StringName GDFunction::get_name() const { - - return name; -} - -int GDFunction::get_max_stack_size() const { - - return _stack_size; -} - -struct _GDFKC { - - int order; - List<int> pos; -}; - -struct _GDFKCS { - - int order; - StringName id; - int pos; - - bool operator<(const _GDFKCS &p_r) const { - - return order<p_r.order; - } -}; - -void GDFunction::debug_get_stack_member_state(int p_line,List<Pair<StringName,int> > *r_stackvars) const { - - - int oc=0; - Map<StringName,_GDFKC> sdmap; - for( const List<StackDebug>::Element *E=stack_debug.front();E;E=E->next()) { - - const StackDebug &sd=E->get(); - if (sd.line>p_line) - break; - - if (sd.added) { - - if (!sdmap.has(sd.identifier)) { - _GDFKC d; - d.order=oc++; - d.pos.push_back(sd.pos); - sdmap[sd.identifier]=d; - - } else { - sdmap[sd.identifier].pos.push_back(sd.pos); - } - } else { - - - ERR_CONTINUE(!sdmap.has(sd.identifier)); - - sdmap[sd.identifier].pos.pop_back(); - if (sdmap[sd.identifier].pos.empty()) - sdmap.erase(sd.identifier); - } - - } - - - List<_GDFKCS> stackpositions; - for(Map<StringName,_GDFKC>::Element *E=sdmap.front();E;E=E->next() ) { - - _GDFKCS spp; - spp.id=E->key(); - spp.order=E->get().order; - spp.pos=E->get().pos.back()->get(); - stackpositions.push_back(spp); - } - - stackpositions.sort(); - - for(List<_GDFKCS>::Element *E=stackpositions.front();E;E=E->next()) { - - Pair<StringName,int> p; - p.first=E->get().id; - p.second=E->get().pos; - r_stackvars->push_back(p); - } - - -} - -#if 0 -void GDFunction::clear() { - - name=StringName(); - constants.clear(); - _stack_size=0; - code.clear(); - _constants_ptr=NULL; - _constant_count=0; - _global_names_ptr=NULL; - _global_names_count=0; - _code_ptr=NULL; - _code_size=0; - -} -#endif -GDFunction::GDFunction() { - - _stack_size=0; - _call_size=0; - name="<anonymous>"; -#ifdef DEBUG_ENABLED - _func_cname=NULL; -#endif - -} - -///////////////////// - - -Variant GDFunctionState::_signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error) { - - Variant arg; - r_error.error=Variant::CallError::CALL_OK; - - ERR_FAIL_COND_V(!function,Variant()); - - if (p_argcount==0) { - r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument=1; - return Variant(); - } else if (p_argcount==1) { - //noooneee - } else if (p_argcount==2) { - arg=*p_args[0]; - } else { - Array extra_args; - for(int i=0;i<p_argcount-1;i++) { - extra_args.push_back(*p_args[i]); - } - arg=extra_args; - } - - Ref<GDFunctionState> self = *p_args[p_argcount-1]; - - if (self.is_null()) { - r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument=p_argcount-1; - r_error.expected=Variant::OBJECT; - return Variant(); - } - - state.result=arg; - Variant ret = function->call(NULL,NULL,0,r_error,&state); - function=NULL; //cleaned up; - state.result=Variant(); - return ret; -} - - -bool GDFunctionState::is_valid() const { - - return function!=NULL; -} - -Variant GDFunctionState::resume(const Variant& p_arg) { - - ERR_FAIL_COND_V(!function,Variant()); - - state.result=p_arg; - Variant::CallError err; - Variant ret = function->call(NULL,NULL,0,err,&state); - function=NULL; //cleaned up; - state.result=Variant(); - return ret; -} - - -void GDFunctionState::_bind_methods() { - - ObjectTypeDB::bind_method(_MD("resume:Variant","arg"),&GDFunctionState::resume,DEFVAL(Variant())); - ObjectTypeDB::bind_method(_MD("is_valid"),&GDFunctionState::is_valid); - ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&GDFunctionState::_signal_callback,MethodInfo("_signal_callback")); - -} - -GDFunctionState::GDFunctionState() { - - function=NULL; -} - -GDFunctionState::~GDFunctionState() { - - if (function!=NULL) { - //never called, deinitialize stack - for(int i=0;i<state.stack_size;i++) { - Variant *v=(Variant*)&state.stack[sizeof(Variant)*i]; - v->~Variant(); - } - } -} +#include "os/os.h" /////////////////////////// @@ -1457,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 */ @@ -1495,11 +145,8 @@ Variant GDScript::_new(const Variant** p_args,int p_argcount,Variant::CallError& _baseptr=_baseptr->_base; } - if (_baseptr->native.ptr()) { - owner=_baseptr->native->instance(); - } else { - owner=memnew( Reference ); //by default, no base means use reference - } + ERR_FAIL_COND_V(_baseptr->native.is_null(), Variant()); + owner=_baseptr->native->instance(); Reference *r=owner->cast_to<Reference>(); if (r) { @@ -1856,10 +503,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; @@ -1887,7 +534,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) { @@ -1935,14 +582,14 @@ Variant GDScript::call(const StringName& p_method,const Variant** p_args,int p_a GDScript *top=this; while(top) { - Map<StringName,GDFunction>::Element *E=top->member_functions.find(p_method); + Map<StringName,GDFunction*>::Element *E=top->member_functions.find(p_method); if (E) { - if (!E->get().is_static()) { + if (!E->get()->is_static()) { WARN_PRINT(String("Can't call non-static function: '"+String(p_method)+"' in script.").utf8().get_data()); } - return E->get().call(NULL,p_args,p_argcount,r_error); + return E->get()->call(NULL,p_args,p_argcount,r_error); } top=top->_base; } @@ -2127,7 +774,7 @@ Error GDScript::load_source_code(const String& p_path) { } -const Map<StringName,GDFunction>& GDScript::debug_get_member_functions() const { +const Map<StringName,GDFunction*>& GDScript::debug_get_member_functions() const { return member_functions; } @@ -2193,7 +840,7 @@ void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { } -GDScript::GDScript() { +GDScript::GDScript() : script_list(this) { _static_ref=this; @@ -2207,6 +854,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 } @@ -2242,14 +916,14 @@ bool GDInstance::set(const StringName& p_name, const Variant& p_value) { while(sptr) { - Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set); + Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set); if (E) { Variant name=p_name; const Variant *args[2]={&name,&p_value}; Variant::CallError err; - Variant ret = E->get().call(this,(const Variant**)args,2,err); + Variant ret = E->get()->call(this,(const Variant**)args,2,err); if (err.error==Variant::CallError::CALL_OK && ret.get_type()==Variant::BOOL && ret.operator bool()) return true; } @@ -2292,14 +966,14 @@ bool GDInstance::get(const StringName& p_name, Variant &r_ret) const { } { - const Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get); + const Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get); if (E) { Variant name=p_name; const Variant *args[1]={&name}; Variant::CallError err; - Variant ret = const_cast<GDFunction*>(&E->get())->call(const_cast<GDInstance*>(this),(const Variant**)args,1,err); + Variant ret = const_cast<GDFunction*>(E->get())->call(const_cast<GDInstance*>(this),(const Variant**)args,1,err); if (err.error==Variant::CallError::CALL_OK && ret.get_type()!=Variant::NIL) { r_ret=ret; return true; @@ -2341,12 +1015,12 @@ void GDInstance::get_property_list(List<PropertyInfo> *p_properties) const { while(sptr) { - const Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list); + const Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list); if (E) { Variant::CallError err; - Variant ret = const_cast<GDFunction*>(&E->get())->call(const_cast<GDInstance*>(this),NULL,0,err); + Variant ret = const_cast<GDFunction*>(E->get())->call(const_cast<GDInstance*>(this),NULL,0,err); if (err.error==Variant::CallError::CALL_OK) { if (ret.get_type()!=Variant::ARRAY) { @@ -2403,7 +1077,7 @@ void GDInstance::get_property_list(List<PropertyInfo> *p_properties) const { if (sptr->member_functions.has("_get_property_list")) { Variant::CallError err; - GDFunction *f = const_cast<GDFunction*>(&sptr->member_functions["_get_property_list"]); + GDFunction *f = const_cast<GDFunction*>(sptr->member_functions["_get_property_list"]); Variant plv = f->call(const_cast<GDInstance*>(this),NULL,0,err); if (plv.get_type()!=Variant::ARRAY) { @@ -2419,11 +1093,11 @@ void GDInstance::get_property_list(List<PropertyInfo> *p_properties) const { PropertyInfo pinfo; if (!p.has("name")) { ERR_PRINT("_get_property_list: expected 'name' key of type string.") - continue; + continue; } if (!p.has("type")) { ERR_PRINT("_get_property_list: expected 'type' key of type integer.") - continue; + continue; } pinfo.name=p["name"]; pinfo.type=Variant::Type(int(p["type"])); @@ -2457,12 +1131,12 @@ void GDInstance::get_method_list(List<MethodInfo> *p_list) const { const GDScript *sptr=script.ptr(); while(sptr) { - for (Map<StringName,GDFunction>::Element *E = sptr->member_functions.front();E;E=E->next()) { + for (Map<StringName,GDFunction*>::Element *E = sptr->member_functions.front();E;E=E->next()) { MethodInfo mi; mi.name=E->key(); mi.flags|=METHOD_FLAG_FROM_SCRIPT; - for(int i=0;i<E->get().get_argument_count();i++) + for(int i=0;i<E->get()->get_argument_count();i++) mi.arguments.push_back(PropertyInfo(Variant::NIL,"arg"+itos(i))); p_list->push_back(mi); } @@ -2475,7 +1149,7 @@ bool GDInstance::has_method(const StringName& p_method) const { const GDScript *sptr=script.ptr(); while(sptr) { - const Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(p_method); + const Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(p_method); if (E) return true; sptr = sptr->_base; @@ -2489,9 +1163,9 @@ Variant GDInstance::call(const StringName& p_method,const Variant** p_args,int p GDScript *sptr=script.ptr(); while(sptr) { - Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(p_method); + Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(p_method); if (E) { - return E->get().call(this,p_args,p_argcount,r_error); + return E->get()->call(this,p_args,p_argcount,r_error); } sptr = sptr->_base; } @@ -2505,9 +1179,9 @@ void GDInstance::call_multilevel(const StringName& p_method,const Variant** p_ar Variant::CallError ce; while(sptr) { - Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(p_method); + Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(p_method); if (E) { - E->get().call(this,p_args,p_argcount,ce); + E->get()->call(this,p_args,p_argcount,ce); } sptr = sptr->_base; } @@ -2522,9 +1196,9 @@ void GDInstance::_ml_call_reversed(GDScript *sptr,const StringName& p_method,con Variant::CallError ce; - Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(p_method); + Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(p_method); if (E) { - E->get().call(this,p_args,p_argcount,ce); + E->get()->call(this,p_args,p_argcount,ce); } } @@ -2544,10 +1218,10 @@ void GDInstance::notification(int p_notification) { GDScript *sptr=script.ptr(); while(sptr) { - Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification); + Map<StringName,GDFunction*>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification); if (E) { Variant::CallError err; - E->get().call(this,args,1,err); + E->get()->call(this,args,1,err); if (err.error!=Variant::CallError::CALL_OK) { //print error about notification call @@ -2568,6 +1242,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; @@ -2672,11 +1377,213 @@ void GDScriptLanguage::finish() { } +void GDScriptLanguage::profiling_start() { + +#ifdef DEBUG_ENABLED + if (lock) { + lock->lock(); + } + + SelfList<GDFunction> *elem=function_list.first(); + while(elem) { + elem->self()->profile.call_count=0; + elem->self()->profile.self_time=0; + elem->self()->profile.total_time=0; + elem->self()->profile.frame_call_count=0; + elem->self()->profile.frame_self_time=0; + elem->self()->profile.frame_total_time=0; + elem->self()->profile.last_frame_call_count=0; + elem->self()->profile.last_frame_self_time=0; + elem->self()->profile.last_frame_total_time=0; + elem=elem->next(); + } + + profiling=true; + if (lock) { + lock->unlock(); + } + +#endif + +} + +void GDScriptLanguage::profiling_stop() { + +#ifdef DEBUG_ENABLED + if (lock) { + lock->lock(); + } + + profiling=false; + if (lock) { + lock->unlock(); + } + +#endif +} + +int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr,int p_info_max) { + + int current=0; +#ifdef DEBUG_ENABLED + if (lock) { + lock->lock(); + } + + + SelfList<GDFunction> *elem=function_list.first(); + while(elem) { + if (current>=p_info_max) + break; + p_info_arr[current].call_count=elem->self()->profile.call_count; + p_info_arr[current].self_time=elem->self()->profile.self_time; + p_info_arr[current].total_time=elem->self()->profile.total_time; + p_info_arr[current].signature=elem->self()->profile.signature; + elem=elem->next(); + current++; + } + + + + if (lock) { + lock->unlock(); + } + + +#endif + + return current; + + +} + +int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr,int p_info_max) { + + int current=0; + +#ifdef DEBUG_ENABLED + if (lock) { + lock->lock(); + } + + + SelfList<GDFunction> *elem=function_list.first(); + while(elem) { + if (current>=p_info_max) + break; + if (elem->self()->profile.last_frame_call_count>0) { + p_info_arr[current].call_count=elem->self()->profile.last_frame_call_count; + p_info_arr[current].self_time=elem->self()->profile.last_frame_self_time; + p_info_arr[current].total_time=elem->self()->profile.last_frame_total_time; + p_info_arr[current].signature=elem->self()->profile.signature; + //print_line(String(elem->self()->profile.signature)+": "+itos(elem->self()->profile.last_frame_call_count)); + current++; + } + elem=elem->next(); + + } + + + if (lock) { + lock->unlock(); + } + + +#endif + + return current; + +} + + +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)); + // print_line("calls: "+itos(calls)); calls=0; + +#ifdef DEBUG_ENABLED + if (profiling) { + if (lock) { + lock->lock(); + } + + SelfList<GDFunction> *elem=function_list.first(); + while(elem) { + elem->self()->profile.last_frame_call_count=elem->self()->profile.frame_call_count; + elem->self()->profile.last_frame_self_time=elem->self()->profile.frame_self_time; + elem->self()->profile.last_frame_total_time=elem->self()->profile.frame_total_time; + elem->self()->profile.frame_call_count=0; + elem->self()->profile.frame_self_time=0; + elem->self()->profile.frame_total_time=0; + elem=elem->next(); + } + + + if (lock) { + lock->unlock(); + } + } + +#endif } /* EDITOR FUNCTIONS */ @@ -2724,7 +1631,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "pass", "return", "while", - 0}; + 0}; const char **w=_reserved_words; @@ -2756,30 +1663,43 @@ GDScriptLanguage::GDScriptLanguage() { _debug_parse_err_line=-1; _debug_parse_err_file=""; - _debug_call_stack_pos=0; - int dmcs=GLOBAL_DEF("debug/script_max_call_stack",1024); - if (ScriptDebugger::get_singleton()) { - //debugging enabled! +#ifdef NO_THREADS + lock=NULL; +#else + lock = Mutex::create(); +#endif + profiling=false; + script_frame_time=0; + + _debug_call_stack_pos=0; + int dmcs=GLOBAL_DEF("debug/script_max_call_stack",1024); + if (ScriptDebugger::get_singleton()) { + //debugging enabled! - _debug_max_call_stack = dmcs; - if (_debug_max_call_stack<1024) - _debug_max_call_stack=1024; - _call_stack = memnew_arr( CallLevel, _debug_max_call_stack+1 ); + _debug_max_call_stack = dmcs; + if (_debug_max_call_stack<1024) + _debug_max_call_stack=1024; + _call_stack = memnew_arr( CallLevel, _debug_max_call_stack+1 ); - } else { - _debug_max_call_stack=0; - _call_stack=NULL; - } + } else { + _debug_max_call_stack=0; + _call_stack=NULL; + } } GDScriptLanguage::~GDScriptLanguage() { - if (_call_stack) { - memdelete_arr(_call_stack); - } - singleton=NULL; + + if (lock) { + memdelete(lock); + lock=NULL; + } + if (_call_stack) { + memdelete_arr(_call_stack); + } + singleton=NULL; } /*************** RESOURCE ***************/ diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index 663fc985a7..f052d13685 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -32,185 +32,7 @@ #include "script_language.h" #include "io/resource_loader.h" #include "io/resource_saver.h" -#include "os/thread.h" -#include "pair.h" -class GDInstance; -class GDScript; - - - -class GDFunction { -public: - - enum Opcode { - OPCODE_OPERATOR, - OPCODE_EXTENDS_TEST, - OPCODE_SET, - OPCODE_GET, - OPCODE_SET_NAMED, - OPCODE_GET_NAMED, - OPCODE_ASSIGN, - OPCODE_ASSIGN_TRUE, - OPCODE_ASSIGN_FALSE, - OPCODE_CONSTRUCT, //only for basic types!! - OPCODE_CONSTRUCT_ARRAY, - OPCODE_CONSTRUCT_DICTIONARY, - OPCODE_CALL, - OPCODE_CALL_RETURN, - OPCODE_CALL_BUILT_IN, - OPCODE_CALL_SELF, - OPCODE_CALL_SELF_BASE, - OPCODE_YIELD, - OPCODE_YIELD_SIGNAL, - OPCODE_YIELD_RESUME, - OPCODE_JUMP, - OPCODE_JUMP_IF, - OPCODE_JUMP_IF_NOT, - OPCODE_JUMP_TO_DEF_ARGUMENT, - OPCODE_RETURN, - OPCODE_ITERATE_BEGIN, - OPCODE_ITERATE, - OPCODE_ASSERT, - OPCODE_BREAKPOINT, - OPCODE_LINE, - OPCODE_END - }; - - enum Address { - ADDR_BITS=24, - ADDR_MASK=((1<<ADDR_BITS)-1), - ADDR_TYPE_MASK=~ADDR_MASK, - ADDR_TYPE_SELF=0, - ADDR_TYPE_CLASS=1, - ADDR_TYPE_MEMBER=2, - ADDR_TYPE_CLASS_CONSTANT=3, - ADDR_TYPE_LOCAL_CONSTANT=4, - ADDR_TYPE_STACK=5, - ADDR_TYPE_STACK_VARIABLE=6, - ADDR_TYPE_GLOBAL=7, - ADDR_TYPE_NIL=8 - }; - - struct StackDebug { - - int line; - int pos; - bool added; - StringName identifier; - }; - -private: -friend class GDCompiler; - - StringName source; - - mutable Variant nil; - mutable Variant *_constants_ptr; - int _constant_count; - const StringName *_global_names_ptr; - int _global_names_count; - const int *_default_arg_ptr; - int _default_arg_count; - const int *_code_ptr; - int _code_size; - int _argument_count; - int _stack_size; - int _call_size; - int _initial_line; - bool _static; - GDScript *_script; - - StringName name; - Vector<Variant> constants; - Vector<StringName> global_names; - Vector<int> default_arguments; - Vector<int> code; -#ifdef DEBUG_ENABLED - CharString func_cname; - const char*_func_cname; -#endif - -#ifdef TOOLS_ENABLED - Vector<StringName> arg_names; -#endif - - List<StackDebug> stack_debug; - - _FORCE_INLINE_ Variant *_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self,Variant *p_stack,String& r_error) const; - _FORCE_INLINE_ String _get_call_error(const Variant::CallError& p_err, const String& p_where,const Variant**argptrs) const; - - -public: - - struct CallState { - - GDInstance *instance; - Vector<uint8_t> stack; - int stack_size; - Variant self; - uint32_t alloca_size; - GDScript *_class; - int ip; - int line; - int defarg; - Variant result; - - }; - - _FORCE_INLINE_ bool is_static() const { return _static; } - - const int* get_code() const; //used for debug - int get_code_size() const; - Variant get_constant(int p_idx) const; - StringName get_global_name(int p_idx) const; - StringName get_name() const; - int get_max_stack_size() const; - int get_default_argument_count() const; - int get_default_argument_addr(int p_idx) const; - GDScript *get_script() const { return _script; } - - void debug_get_stack_member_state(int p_line,List<Pair<StringName,int> > *r_stackvars) const; - - _FORCE_INLINE_ bool is_empty() const { return _code_size==0; } - - int get_argument_count() const { return _argument_count; } - StringName get_argument_name(int p_idx) const { -#ifdef TOOLS_ENABLED - ERR_FAIL_INDEX_V(p_idx,arg_names.size(),StringName()); - return arg_names[p_idx]; -#endif - return StringName(); - - } - Variant get_default_argument(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx,default_arguments.size(),Variant()); - return default_arguments[p_idx]; - } - - Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err,CallState *p_state=NULL); - - GDFunction(); -}; - - -class GDFunctionState : public Reference { - - OBJ_TYPE(GDFunctionState,Reference); -friend class GDFunction; - GDFunction *function; - GDFunction::CallState state; - Variant _signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error); -protected: - static void _bind_methods(); -public: - - bool is_valid() const; - Variant resume(const Variant& p_arg=Variant()); - GDFunctionState(); - ~GDFunctionState(); -}; - - +#include "gd_function.h" class GDNativeClass : public Reference { OBJ_TYPE(GDNativeClass,Reference); @@ -258,7 +80,7 @@ friend class GDScriptLanguage; Set<StringName> members; //members are just indices to the instanced script. Map<StringName,Variant> constants; - Map<StringName,GDFunction> member_functions; + Map<StringName,GDFunction*> member_functions; Map<StringName,MemberInfo> member_indices; //members are just indices to the instanced script. Map<StringName,Ref<GDScript> > subclasses; Map<StringName,Vector<StringName> > _signals; @@ -285,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); @@ -317,7 +140,7 @@ public: const Map<StringName,Ref<GDScript> >& get_subclasses() const { return subclasses; } const Map<StringName,Variant >& get_constants() const { return constants; } const Set<StringName>& get_members() const { return members; } - const Map<StringName,GDFunction>& get_member_functions() const { return member_functions; } + const Map<StringName,GDFunction*>& get_member_functions() const { return member_functions; } const Ref<GDNativeClass>& get_native() const { return native; } virtual bool has_script_signal(const StringName& p_signal) const; @@ -328,7 +151,7 @@ public: Ref<GDScript> get_base() const; const Map<StringName,MemberInfo>& debug_get_member_indices() const { return member_indices; } - const Map<StringName,GDFunction>& debug_get_member_functions() const; //this is debug only + const Map<StringName,GDFunction*>& debug_get_member_functions() const; //this is debug only StringName debug_get_member_by_index(int p_idx) const; Variant _new(const Variant** p_args,int p_argcount,Variant::CallError& r_error); @@ -343,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... @@ -357,18 +180,24 @@ public: virtual ScriptLanguage *get_language() const; GDScript(); + ~GDScript(); }; 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; + void _ml_call_reversed(GDScript *sptr,const StringName& p_method,const Variant** p_args,int p_argcount); public: @@ -385,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); @@ -395,6 +224,7 @@ public: void set_path(const String& p_path); + void reload_members(); GDInstance(); ~GDInstance(); @@ -431,8 +261,19 @@ class GDScriptLanguage : public ScriptLanguage { void _add_global(const StringName& p_name,const Variant& p_value); + Mutex *lock; + +friend class GDScript; + + SelfList<GDScript>::List script_list; +friend class GDFunction; + + SelfList<GDFunction>::List function_list; + bool profiling; + uint64_t script_frame_time; public: + int calls; bool debug_break(const String& p_error,bool p_allow_continue=true); @@ -547,11 +388,19 @@ 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; virtual void get_public_constants(List<Pair<String,Variant> > *p_constants) const; + virtual void profiling_start(); + virtual void profiling_stop(); + + virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr,int p_info_max); + virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr,int p_info_max); + /* LOADER FUNCTIONS */ virtual void get_recognized_extensions(List<String> *p_extensions) const; diff --git a/modules/gridmap/config.py b/modules/gridmap/config.py index f9bd7da08d..ea7e83378a 100644 --- a/modules/gridmap/config.py +++ b/modules/gridmap/config.py @@ -2,10 +2,10 @@ def can_build(platform): return True - - + + def configure(env): pass - - - + + + diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index e8b443a9e3..503e723de2 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -35,6 +35,7 @@ #include "io/marshalls.h" #include "scene/scene_string_names.h" #include "os/os.h" +#include "scene/resources/mesh_library.h" bool GridMap::_set(const StringName& p_name, const Variant& p_value) { @@ -450,6 +451,7 @@ void GridMap::set_cell_item(int p_x,int p_y,int p_z, int p_item,int p_rot){ if (theme.is_valid() && theme->has_item(p_item)) { ii.mesh=theme->get_item_mesh(p_item); ii.shape=theme->get_item_shape(p_item); + ii.navmesh=theme->get_item_navmesh(p_item); } ii.multimesh = Ref<MultiMesh>( memnew( MultiMesh ) ); ii.multimesh->set_mesh(ii.mesh); @@ -521,6 +523,52 @@ int GridMap::get_cell_item_orientation(int p_x,int p_y,int p_z) const{ } +void GridMap::_octant_enter_tree(const OctantKey &p_key){ + ERR_FAIL_COND(!octant_map.has(p_key)); + if(navigation){ + Octant&g = *octant_map[p_key]; + + Vector3 ofs(cell_size*0.5*int(center_x),cell_size*0.5*int(center_y),cell_size*0.5*int(center_z)); + _octant_clear_navmesh(p_key); + + for(Map<int,Octant::ItemInstances>::Element *E=g.items.front();E;E=E->next()) { + Octant::ItemInstances &ii=E->get(); + + for(Set<IndexKey>::Element *F=ii.cells.front();F;F=F->next()) { + + IndexKey ik=F->get(); + Map<IndexKey,Cell>::Element *C=cell_map.find(ik); + ERR_CONTINUE(!C); + + Vector3 cellpos = Vector3(ik.x,ik.y,ik.z ); + + Transform xform; + + if (clip && ( (clip_above && cellpos[clip_axis]>clip_floor) || (!clip_above && cellpos[clip_axis]<clip_floor))) { + + xform.basis.set_zero(); + + } else { + + xform.basis.set_orthogonal_index(C->get().rot); + } + + + xform.set_origin( cellpos*cell_size+ofs); + xform.basis.scale(Vector3(cell_scale,cell_scale,cell_scale)); + // add the item's navmesh at given xform to GridMap's Navigation ancestor + if(ii.navmesh.is_valid()){ + int nm_id = navigation->navmesh_create(ii.navmesh,xform,this); + Octant::NavMesh nm; + nm.id=nm_id; + nm.xform=xform; + g.navmesh_ids[ik]=nm; + } + } + } + } +} + void GridMap::_octant_enter_world(const OctantKey &p_key) { ERR_FAIL_COND(!octant_map.has(p_key)); @@ -560,7 +608,6 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) { } } } - } @@ -589,9 +636,20 @@ void GridMap::_octant_transform(const OctantKey &p_key) { } +void GridMap::_octant_clear_navmesh(const OctantKey &p_key){ + Octant&g = *octant_map[p_key]; + if (navigation) { + for(Map<IndexKey,Octant::NavMesh>::Element *E=g.navmesh_ids.front();E;E=E->next()) { + Octant::NavMesh *nvm = &E->get(); + if(nvm && nvm->id){ + navigation->navmesh_remove(E->get().id); + } + } + g.navmesh_ids.clear(); + } +} void GridMap::_octant_update(const OctantKey &p_key) { - ERR_FAIL_COND(!octant_map.has(p_key)); Octant&g = *octant_map[p_key]; if (!g.dirty) @@ -599,6 +657,7 @@ void GridMap::_octant_update(const OctantKey &p_key) { Ref<Mesh> mesh; + _octant_clear_navmesh(p_key); PhysicsServer::get_singleton()->body_clear_shapes(g.static_body); if (g.collision_debug.is_valid()) { @@ -608,11 +667,16 @@ void GridMap::_octant_update(const OctantKey &p_key) { DVector<Vector3> col_debug; + /* + * foreach item in this octant, + * set item's multimesh's instance count to number of cells which have this item + * and set said multimesh bounding box to one containing all cells which have this item + */ for(Map<int,Octant::ItemInstances>::Element *E=g.items.front();E;E=E->next()) { Octant::ItemInstances &ii=E->get(); - ii.multimesh->set_instance_count(ii.cells.size()); + ii.multimesh->set_instance_count(ii.cells.size()); AABB aabb; AABB mesh_aabb = ii.mesh.is_null()?AABB():ii.mesh->get_aabb(); @@ -622,6 +686,7 @@ void GridMap::_octant_update(const OctantKey &p_key) { //print_line("OCTANT, CELLS: "+itos(ii.cells.size())); int idx=0; + // foreach cell containing this item type for(Set<IndexKey>::Element *F=ii.cells.front();F;F=F->next()) { IndexKey ik=F->get(); Map<IndexKey,Cell>::Element *C=cell_map.find(ik); @@ -658,8 +723,9 @@ void GridMap::_octant_update(const OctantKey &p_key) { aabb.merge_with(xform.xform(mesh_aabb)); } + // add the item's shape at given xform to octant's static_body if (ii.shape.is_valid()) { - + // add the item's shape PhysicsServer::get_singleton()->body_add_shape(g.static_body,ii.shape->get_rid(),xform); if (g.collision_debug.is_valid()) { ii.shape->add_vertices_to_array(col_debug,xform); @@ -668,6 +734,17 @@ void GridMap::_octant_update(const OctantKey &p_key) { // print_line("PHIS x: "+xform); } + // add the item's navmesh at given xform to GridMap's Navigation ancestor + if(navigation){ + if(ii.navmesh.is_valid()){ + int nm_id = navigation->navmesh_create(ii.navmesh,xform,this); + Octant::NavMesh nm; + nm.id=nm_id; + nm.xform=xform; + g.navmesh_ids[ik]=nm; + } + } + idx++; } @@ -970,12 +1047,43 @@ void GridMap::_notification(int p_what) { } - //_queue_dirty_map(MAP_DIRTY_INSTANCES|MAP_DIRTY_TRANSFORMS); //_update_dirty_map_callback(); //_update_area_instances(); } break; + case NOTIFICATION_ENTER_TREE: { + + Spatial *c=this; + while(c) { + navigation=c->cast_to<Navigation>(); + if (navigation) { + break; + } + + c=c->get_parent()->cast_to<Spatial>(); + } + + if(navigation){ + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + if (navigation) { + _octant_enter_tree(E->key()); + } + } + } + + _queue_dirty_map(); + } break; + case NOTIFICATION_EXIT_TREE: { + for(Map<OctantKey,Octant*>::Element *E=octant_map.front();E;E=E->next()) { + if (navigation) { + _octant_clear_navmesh(E->key()); + } + } + + navigation=NULL; + + } break; } } @@ -1734,3 +1842,4 @@ GridMap::~GridMap() { clear(); } + diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 66d3e6b44a..0116ea094f 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -32,6 +32,7 @@ #include "scene/resources/mesh_library.h" #include "scene/3d/spatial.h" +#include "scene/3d/navigation.h" #include "scene/resources/multimesh.h" //heh heh, godotsphir!! this shares no code and the design is completely different with previous projects i've done.. @@ -67,6 +68,9 @@ class GridMap : public Spatial { IndexKey() { key=0; } }; + /** + * @brief A Cell is a single cell in the cube map space; it is defined by its coordinates and the populating Item, identified by int id. + */ union Cell { struct { @@ -79,16 +83,24 @@ class GridMap : public Spatial { Cell() { item=0; rot=0; layer=0; } }; + /** + * @brief An Octant is a prism containing Cells, and possibly belonging to an Area. + * A GridMap can have multiple Octants. + */ struct Octant { - struct ItemInstances { + struct NavMesh { + int id; + Transform xform; + }; + struct ItemInstances { Set<IndexKey> cells; Ref<Mesh> mesh; Ref<Shape> shape; Ref<MultiMesh> multimesh; RID multimesh_instance; - + Ref<NavigationMesh> navmesh; }; Ref<Mesh> baked; @@ -98,9 +110,8 @@ class GridMap : public Spatial { bool dirty; RID static_body; - Map<int,ItemInstances> items; - + Map<IndexKey,NavMesh> navmesh_ids; }; union OctantKey { @@ -131,7 +142,7 @@ class GridMap : public Spatial { bool center_x,center_y,center_z; bool bake; float cell_scale; - + Navigation *navigation; bool clip; bool clip_above; @@ -140,7 +151,9 @@ class GridMap : public Spatial { Vector3::Axis clip_axis; - + /** + * @brief An Area is something like a room: it has doors, and Octants can choose to belong to it. + */ struct Area { String name; @@ -188,10 +201,12 @@ class GridMap : public Spatial { } void _octant_enter_world(const OctantKey &p_key); + void _octant_enter_tree(const OctantKey &p_key); void _octant_exit_world(const OctantKey &p_key); void _octant_update(const OctantKey &p_key); void _octant_transform(const OctantKey &p_key); void _octant_clear_baked(const OctantKey &p_key); + void _octant_clear_navmesh(const GridMap::OctantKey&); void _octant_bake(const OctantKey &p_key,const Ref<TriangleMesh>& p_tmesh=RES(),const Vector<BakeLight> &p_lights=Vector<BakeLight>(),List<Vector3> *r_prebake=NULL); bool awaiting_update; diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 6043807db3..87afe3d5a4 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -1236,9 +1236,9 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { options->get_popup()->add_item("Cursor Rotate X",MENU_OPTION_CURSOR_ROTATE_X,KEY_A); options->get_popup()->add_item("Cursor Rotate Y",MENU_OPTION_CURSOR_ROTATE_Y,KEY_S); options->get_popup()->add_item("Cursor Rotate Z",MENU_OPTION_CURSOR_ROTATE_Z,KEY_D); - options->get_popup()->add_item("Cursor Back Rotate X",MENU_OPTION_CURSOR_ROTATE_X,KEY_MASK_SHIFT+KEY_A); - options->get_popup()->add_item("Cursor Back Rotate Y",MENU_OPTION_CURSOR_ROTATE_Y,KEY_MASK_SHIFT+KEY_S); - options->get_popup()->add_item("Cursor Back Rotate Z",MENU_OPTION_CURSOR_ROTATE_Z,KEY_MASK_SHIFT+KEY_D); + options->get_popup()->add_item("Cursor Back Rotate X",MENU_OPTION_CURSOR_BACK_ROTATE_X,KEY_MASK_SHIFT+KEY_A); + options->get_popup()->add_item("Cursor Back Rotate Y",MENU_OPTION_CURSOR_BACK_ROTATE_Y,KEY_MASK_SHIFT+KEY_S); + options->get_popup()->add_item("Cursor Back Rotate Z",MENU_OPTION_CURSOR_BACK_ROTATE_Z,KEY_MASK_SHIFT+KEY_D); options->get_popup()->add_item("Cursor Clear Rotation",MENU_OPTION_CURSOR_CLEAR_ROTATION,KEY_W); options->get_popup()->add_separator(); options->get_popup()->add_check_item("Duplicate Selects",MENU_OPTION_DUPLICATE_SELECTS); diff --git a/modules/ik/SCsub b/modules/ik/SCsub new file mode 100644 index 0000000000..211a043468 --- /dev/null +++ b/modules/ik/SCsub @@ -0,0 +1,3 @@ +Import('env') + +env.add_source_files(env.modules_sources,"*.cpp") diff --git a/modules/ik/config.py b/modules/ik/config.py new file mode 100644 index 0000000000..f9bd7da08d --- /dev/null +++ b/modules/ik/config.py @@ -0,0 +1,11 @@ + + +def can_build(platform): + return True + + +def configure(env): + pass + + + diff --git a/modules/ik/ik.cpp b/modules/ik/ik.cpp new file mode 100644 index 0000000000..6c383fdb55 --- /dev/null +++ b/modules/ik/ik.cpp @@ -0,0 +1,326 @@ +/*************************************************************************/ +/* ik.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* This file is Copyright (c) 2016 Sergey Lapin <slapinid@gmail.com> */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "ik.h" + +bool InverseKinematics::_get(const StringName& p_name,Variant &r_ret) const +{ + + if (String(p_name)=="ik_bone") { + + r_ret=get_bone_name(); + return true; + } + + return false; +} + +bool InverseKinematics::_set(const StringName& p_name, const Variant& p_value) +{ + + if (String(p_name)=="ik_bone") { + + set_bone_name(p_value); + changed = true; + return true; + } + + return false; +} + +void InverseKinematics::_get_property_list( List<PropertyInfo>* p_list ) const +{ + + Skeleton *parent=NULL; + if(get_parent()) + parent=get_parent()->cast_to<Skeleton>(); + + if (parent) { + + String names; + for(int i=0;i<parent->get_bone_count();i++) { + if(i>0) + names+=","; + names+=parent->get_bone_name(i); + } + + p_list->push_back(PropertyInfo(Variant::STRING,"ik_bone",PROPERTY_HINT_ENUM,names)); + } else { + + p_list->push_back(PropertyInfo(Variant::STRING,"ik_bone")); + + } + +} + +void InverseKinematics::_check_bind() +{ + + if (get_parent() && get_parent()->cast_to<Skeleton>()) { + Skeleton *sk = get_parent()->cast_to<Skeleton>(); + int idx = sk->find_bone(ik_bone); + if (idx!=-1) { + ik_bone_no = idx; + bound=true; + } + skel = sk; + } +} + +void InverseKinematics::_check_unbind() +{ + + if (bound) { + + if (get_parent() && get_parent()->cast_to<Skeleton>()) { + Skeleton *sk = get_parent()->cast_to<Skeleton>(); + int idx = sk->find_bone(ik_bone); + if (idx!=-1) + ik_bone_no = idx; + else + ik_bone_no = 0; + skel = sk; + + } + bound=false; + } +} + + +void InverseKinematics::set_bone_name(const String& p_name) +{ + + if (is_inside_tree()) + _check_unbind(); + + ik_bone=p_name; + + if (is_inside_tree()) + _check_bind(); + changed = true; +} + +String InverseKinematics::get_bone_name() const +{ + + return ik_bone; +} + +void InverseKinematics::set_iterations(int itn) +{ + + if (is_inside_tree()) + _check_unbind(); + + iterations=itn; + + if (is_inside_tree()) + _check_bind(); + changed = true; +} + +int InverseKinematics::get_iterations() const +{ + + return iterations; +} + +void InverseKinematics::set_chain_size(int cs) +{ + if (is_inside_tree()) + _check_unbind(); + + chain_size=cs; + chain.clear(); + if (bound) + update_parameters(); + + if (is_inside_tree()) + _check_bind(); + changed = true; +} + +int InverseKinematics::get_chain_size() const +{ + + return chain_size; +} + +void InverseKinematics::set_precision(float p) +{ + + if (is_inside_tree()) + _check_unbind(); + + precision=p; + + if (is_inside_tree()) + _check_bind(); + changed = true; +} + +float InverseKinematics::get_precision() const +{ + + return precision; +} + +void InverseKinematics::set_speed(float p) +{ + + if (is_inside_tree()) + _check_unbind(); + + speed=p; + + if (is_inside_tree()) + _check_bind(); + changed = true; +} + +float InverseKinematics::get_speed() const +{ + + return speed; +} + +void InverseKinematics::update_parameters() +{ + tail_bone = -1; + for (int i = 0; i < skel->get_bone_count(); i++) + if (skel->get_bone_parent(i) == ik_bone_no) + tail_bone = i; + int cur_bone = ik_bone_no; + int its = chain_size; + while (its > 0 && cur_bone >= 0) { + chain.push_back(cur_bone); + cur_bone = skel->get_bone_parent(cur_bone); + its--; + } +} + +void InverseKinematics::_notification(int p_what) +{ + + switch(p_what) { + + case NOTIFICATION_ENTER_TREE: { + + _check_bind(); + if (bound) { + update_parameters(); + changed = false; + set_process(true); + } + } break; + case NOTIFICATION_PROCESS: { + float delta = get_process_delta_time(); + Spatial *sksp = skel->cast_to<Spatial>(); + if (!bound) + break; + if (!sksp) + break; + if (changed) { + update_parameters(); + changed = false; + } + Vector3 to = get_translation(); + for (int hump = 0; hump < iterations; hump++) { + int depth = 0; + float olderr = 1000.0; + float psign = 1.0; + bool reached = false; + + for (List<int>::Element *b = chain.front(); b; b = b->next()) { + int cur_bone = b->get(); + Vector3 d = skel->get_bone_global_pose(tail_bone).origin; + Vector3 rg = to; + float err = d.distance_squared_to(rg); + if (err < precision) { + if (!reached && err < precision) + reached = true; + break; + } else + if (reached) + reached = false; + if (err > olderr) + psign = -psign; + Transform mod = skel->get_bone_global_pose(cur_bone); + Quat q1 = Quat(mod.basis).normalized(); + Transform mod2 = mod.looking_at(to, Vector3(0.0, 1.0, 0.0)); + Quat q2 = Quat(mod2.basis).normalized(); + if (psign < 0.0) + q2 = q2.inverse(); + Quat q = q1.slerp(q2, speed / (1.0 + 500.0 * depth)).normalized(); + Transform fin = Transform(q); + fin.origin = mod.origin; + skel->set_bone_global_pose(cur_bone, fin); + depth++; + } + if (reached) + break; + + } + + } break; + case NOTIFICATION_EXIT_TREE: { + set_process(false); + + _check_unbind(); + } break; + } +} +void InverseKinematics::_bind_methods() { + ObjectTypeDB::bind_method(_MD("set_bone_name","ik_bone"),&InverseKinematics::set_bone_name); + ObjectTypeDB::bind_method(_MD("get_bone_name"),&InverseKinematics::get_bone_name); + ObjectTypeDB::bind_method(_MD("set_iterations","iterations"),&InverseKinematics::set_iterations); + ObjectTypeDB::bind_method(_MD("get_iterations"),&InverseKinematics::get_iterations); + ObjectTypeDB::bind_method(_MD("set_chain_size","chain_size"),&InverseKinematics::set_chain_size); + ObjectTypeDB::bind_method(_MD("get_chain_size"),&InverseKinematics::get_chain_size); + ObjectTypeDB::bind_method(_MD("set_precision","precision"),&InverseKinematics::set_precision); + ObjectTypeDB::bind_method(_MD("get_precision"),&InverseKinematics::get_precision); + ObjectTypeDB::bind_method(_MD("set_speed","speed"),&InverseKinematics::set_speed); + ObjectTypeDB::bind_method(_MD("get_speed"),&InverseKinematics::get_speed); + ADD_PROPERTY(PropertyInfo(Variant::INT, "iterations"), _SCS("set_iterations"), _SCS("get_iterations")); + ADD_PROPERTY(PropertyInfo(Variant::INT, "chain_size"), _SCS("set_chain_size"), _SCS("get_chain_size")); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "precision"), _SCS("set_precision"), _SCS("get_precision")); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed"), _SCS("set_speed"), _SCS("get_speed")); +} + +InverseKinematics::InverseKinematics() +{ + bound=false; + chain_size = 2; + iterations = 100; + precision = 0.001; + speed = 0.2; + +} + diff --git a/modules/ik/ik.h b/modules/ik/ik.h new file mode 100644 index 0000000000..9daddb229d --- /dev/null +++ b/modules/ik/ik.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* ik.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* This file is (c) 2016 Sergey Lapin <slapinid@gmail.com> */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef IK_H +#define IK_H + +#include "scene/3d/skeleton.h" +class InverseKinematics : public Spatial { + OBJ_TYPE(InverseKinematics, Spatial); + bool bound; + String ik_bone; + int ik_bone_no; + int tail_bone; + int chain_size; + Skeleton *skel; + List<int> chain; + void _check_bind(); + void _check_unbind(); + int iterations; + float precision; + float speed; + bool changed; + +protected: + 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; + + void _notification(int p_what); + static void _bind_methods(); + void update_parameters(); +public: + Skeleton *get_skeleton(); + void set_bone_name(const String& p_name); + String get_bone_name() const; + void set_iterations(int itn); + int get_iterations() const; + void set_chain_size(int cs); + int get_chain_size() const; + void set_precision(float p); + float get_precision() const; + void set_speed(float p); + float get_speed() const; + InverseKinematics(); +}; + +#endif + diff --git a/modules/ik/register_types.cpp b/modules/ik/register_types.cpp new file mode 100644 index 0000000000..e7df7f55b2 --- /dev/null +++ b/modules/ik/register_types.cpp @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "register_types.h" +#ifndef _3D_DISABLED +#include "object_type_db.h" +#include "ik.h" +#endif + +void register_ik_types() { + +#ifndef _3D_DISABLED + ObjectTypeDB::register_type<InverseKinematics>(); +#endif +} + + + +void unregister_ik_types() { + + +} diff --git a/modules/ik/register_types.h b/modules/ik/register_types.h new file mode 100644 index 0000000000..828917ade7 --- /dev/null +++ b/modules/ik/register_types.h @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +void register_ik_types(); +void unregister_ik_types(); |