summaryrefslogtreecommitdiff
path: root/modules/visual_script/visual_script.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/visual_script/visual_script.cpp')
-rw-r--r--modules/visual_script/visual_script.cpp358
1 files changed, 280 insertions, 78 deletions
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 9dc91d82f1..9074a08bf4 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -279,7 +279,9 @@ void VisualScript::_node_ports_changed(int p_id) {
}
}
+ set_edited(true); //something changed, let's set as edited
emit_signal("node_ports_changed",function,p_id);
+
}
void VisualScript::add_node(const StringName& p_func,int p_id, const Ref<VisualScriptNode>& p_node, const Point2 &p_pos) {
@@ -1354,95 +1356,30 @@ bool VisualScriptInstance::has_method(const StringName& p_method) const{
//#define VSDEBUG(m_text) print_line(m_text)
#define VSDEBUG(m_text)
-Variant VisualScriptInstance::call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error){
-
- r_error.error=Variant::CallError::CALL_OK; //ok by default
+Variant VisualScriptInstance::_call_internal(const StringName& p_method, void* p_stack, int p_stack_size, VisualScriptNodeInstance* p_node, int p_flow_stack_pos, bool p_resuming_yield, Variant::CallError &r_error) {
Map<StringName,Function>::Element *F = functions.find(p_method);
- if (!F) {
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
-
- VSDEBUG("CALLING: "+String(p_method));
-
+ ERR_FAIL_COND_V(!F,Variant());
Function *f=&F->get();
- int total_stack_size=0;
-
- total_stack_size+=f->max_stack*sizeof(Variant); //variants
- total_stack_size+=f->node_count*sizeof(bool);
- total_stack_size+=(max_input_args+max_output_args)*sizeof(Variant*); //arguments
- total_stack_size+=f->flow_stack_size*sizeof(int); //flow
-
- VSDEBUG("STACK SIZE: "+itos(total_stack_size));
- VSDEBUG("STACK VARIANTS: : "+itos(f->max_stack));
- VSDEBUG("SEQBITS: : "+itos(f->node_count));
- VSDEBUG("MAX INPUT: "+itos(max_input_args));
- VSDEBUG("MAX OUTPUT: "+itos(max_output_args));
- VSDEBUG("FLOW STACK SIZE: "+itos(f->flow_stack_size));
-
- void *stack = alloca(total_stack_size);
-
- Variant *variant_stack=(Variant*)stack;
+ //this call goes separate, so it can e yielded and suspended
+ Variant *variant_stack=(Variant*)p_stack;
bool *sequence_bits = (bool*)(variant_stack + f->max_stack);
const Variant **input_args=(const Variant**)(sequence_bits+f->node_count);
Variant **output_args=(Variant**)(input_args + max_input_args);
int flow_max = f->flow_stack_size;
int* flow_stack = flow_max? (int*)(output_args + max_output_args) : (int*)NULL;
- for(int i=0;i<f->node_count;i++) {
- sequence_bits[i]=false; //all starts as false
- }
-
-
- Map<int,VisualScriptNodeInstance*>::Element *E = instances.find(f->node);
- if (!E) {
- r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
-
- ERR_EXPLAIN("No VisualScriptFunction node in function!");
- ERR_FAIL_V(Variant());
- }
-
- VisualScriptNodeInstance *node = E->get();
-
- int flow_stack_pos=0;
- if (flow_stack) {
- flow_stack[0]=node->get_id();
- }
-
- VSDEBUG("ARGUMENTS: "+itos(f->argument_count)=" RECEIVED: "+itos(p_argcount));
-
- if (p_argcount<f->argument_count) {
- r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument=node->get_input_port_count();
-
- return Variant();
- }
-
- if (p_argcount>f->argument_count) {
- r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument=node->get_input_port_count();
-
- return Variant();
- }
-
- //allocate variant stack
- for(int i=0;i<f->max_stack;i++) {
- memnew_placement(&variant_stack[i],Variant);
- }
-
- //allocate function arguments (must be copied for yield to work properly)
- for(int i=0;i<p_argcount;i++) {
- variant_stack[i]=*p_args[i];
- }
-
String error_str;
+ VisualScriptNodeInstance* node=p_node;
bool error=false;
int current_node_id=f->node;
Variant return_value;
Variant *working_mem=NULL;
+
+ int flow_stack_pos=p_flow_stack_pos;
+
#ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton()) {
VisualScriptLanguage::singleton->enter_function(this,&p_method,variant_stack,&working_mem,&current_node_id);
@@ -1518,12 +1455,19 @@ Variant VisualScriptInstance::call(const StringName& p_method,const Variant** p_
//do step
- bool start_sequence = flow_stack && !(flow_stack[flow_stack_pos] & VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT); //if there is a push bit, it means we are continuing a sequence
-
+ VisualScriptNodeInstance::StartMode start_mode;
+ {
+ if (p_resuming_yield)
+ start_mode=VisualScriptNodeInstance::START_MODE_RESUME_YIELD;
+ else if (flow_stack && !(flow_stack[flow_stack_pos] & VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT)) //if there is a push bit, it means we are continuing a sequence
+ start_mode=VisualScriptNodeInstance::START_MODE_BEGIN_SEQUENCE;
+ else
+ start_mode=VisualScriptNodeInstance::START_MODE_CONTINUE_SEQUENCE;
+ }
VSDEBUG("STEP - STARTSEQ: "+itos(start_sequence));
- int ret = node->step(input_args,output_args,start_sequence,working_mem,r_error,error_str);
+ int ret = node->step(input_args,output_args,start_mode,working_mem,r_error,error_str);
if (r_error.error!=Variant::CallError::CALL_OK) {
//use error from step
@@ -1531,6 +1475,52 @@ Variant VisualScriptInstance::call(const StringName& p_method,const Variant** p_
break;
}
+ if (ret&VisualScriptNodeInstance::STEP_YIELD_BIT) {
+ //yielded!
+ if (node->get_working_memory_size()==0) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ error_str=RTR("A node yielded without working memory, please read the docs on how to yield properly!");
+ error=true;
+ break;
+
+ } else {
+ Ref<VisualScriptFunctionState> state = *working_mem;
+ if (!state.is_valid()) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ error_str=RTR("Node yielded, but did not return a function state in the first working memory.");
+ error=true;
+ break;
+
+ }
+
+ //step 1, capture all state
+ state->instance_id=get_owner_ptr()->get_instance_ID();
+ state->script_id=get_script()->get_instance_ID();
+ state->instance=this;
+ state->function=p_method;
+ state->working_mem_index=node->working_mem_idx;
+ state->variant_stack_size=f->max_stack;
+ state->node=node;
+ state->flow_stack_pos=flow_stack_pos;
+ state->stack.resize(p_stack_size);
+ copymem(state->stack.ptr(),p_stack,p_stack_size);
+ //step 2, run away, return directly
+ r_error.error=Variant::CallError::CALL_OK;
+
+
+#ifdef DEBUG_ENABLED
+ //will re-enter later, so exiting
+ if (ScriptDebugger::get_singleton()) {
+ VisualScriptLanguage::singleton->exit_function();
+ }
+#endif
+
+ return state;
+
+ }
+ }
+
#ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton()) {
// line
@@ -1586,10 +1576,11 @@ Variant VisualScriptInstance::call(const StringName& p_method,const Variant** p_
}
next = node->sequence_outputs[output];
- if (next)
+ if (next) {
VSDEBUG("GOT NEXT NODE - "+itos(next->get_id()));
- else
+ } else {
VSDEBUG("GOT NEXT NODE - NULL");
+ }
}
if (flow_stack) {
@@ -1742,6 +1733,95 @@ Variant VisualScriptInstance::call(const StringName& p_method,const Variant** p_
return return_value;
}
+
+Variant VisualScriptInstance::call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error){
+
+ r_error.error=Variant::CallError::CALL_OK; //ok by default
+
+ Map<StringName,Function>::Element *F = functions.find(p_method);
+ if (!F) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+
+ VSDEBUG("CALLING: "+String(p_method));
+
+ Function *f=&F->get();
+
+ int total_stack_size=0;
+
+ total_stack_size+=f->max_stack*sizeof(Variant); //variants
+ total_stack_size+=f->node_count*sizeof(bool);
+ total_stack_size+=(max_input_args+max_output_args)*sizeof(Variant*); //arguments
+ total_stack_size+=f->flow_stack_size*sizeof(int); //flow
+
+ VSDEBUG("STACK SIZE: "+itos(total_stack_size));
+ VSDEBUG("STACK VARIANTS: : "+itos(f->max_stack));
+ VSDEBUG("SEQBITS: : "+itos(f->node_count));
+ VSDEBUG("MAX INPUT: "+itos(max_input_args));
+ VSDEBUG("MAX OUTPUT: "+itos(max_output_args));
+ VSDEBUG("FLOW STACK SIZE: "+itos(f->flow_stack_size));
+
+ void *stack = alloca(total_stack_size);
+
+ Variant *variant_stack=(Variant*)stack;
+ bool *sequence_bits = (bool*)(variant_stack + f->max_stack);
+ const Variant **input_args=(const Variant**)(sequence_bits+f->node_count);
+ Variant **output_args=(Variant**)(input_args + max_input_args);
+ int flow_max = f->flow_stack_size;
+ int* flow_stack = flow_max? (int*)(output_args + max_output_args) : (int*)NULL;
+
+ for(int i=0;i<f->node_count;i++) {
+ sequence_bits[i]=false; //all starts as false
+ }
+
+
+ Map<int,VisualScriptNodeInstance*>::Element *E = instances.find(f->node);
+ if (!E) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+
+ ERR_EXPLAIN("No VisualScriptFunction node in function!");
+ ERR_FAIL_V(Variant());
+ }
+
+ VisualScriptNodeInstance *node = E->get();
+
+
+ if (flow_stack) {
+ flow_stack[0]=node->get_id();
+ }
+
+ VSDEBUG("ARGUMENTS: "+itos(f->argument_count)=" RECEIVED: "+itos(p_argcount));
+
+ if (p_argcount<f->argument_count) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=node->get_input_port_count();
+
+ return Variant();
+ }
+
+ if (p_argcount>f->argument_count) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument=node->get_input_port_count();
+
+ return Variant();
+ }
+
+ //allocate variant stack
+ for(int i=0;i<f->max_stack;i++) {
+ memnew_placement(&variant_stack[i],Variant);
+ }
+
+ //allocate function arguments (must be copied for yield to work properly)
+ for(int i=0;i<p_argcount;i++) {
+ variant_stack[i]=*p_args[i];
+ }
+
+ return _call_internal(p_method,stack,total_stack_size,node,0,false,r_error);
+
+
+}
+
void VisualScriptInstance::notification(int p_notification){
//do nothing as this is called using virtual
@@ -1977,7 +2057,129 @@ VisualScriptInstance::~VisualScriptInstance() {
+/////////////////////////////////////////////
+
+/////////////////////
+
+
+Variant VisualScriptFunctionState::_signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+
+ ERR_FAIL_COND_V(function==StringName(),Variant());
+
+#ifdef DEBUG_ENABLED
+ if (instance_id && !ObjectDB::get_instance(instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ ERR_FAIL_V(Variant());
+ }
+
+ if (script_id && !ObjectDB::get_instance(script_id)) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
+ ERR_FAIL_V(Variant());
+ }
+#endif
+
+ r_error.error=Variant::CallError::CALL_OK;
+
+ Array args;
+
+ 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, reserved for me, me and only me.
+ } else {
+
+ for(int i=0;i<p_argcount-1;i++) {
+ args.push_back(*p_args[i]);
+ }
+ }
+
+ Ref<VisualScriptFunctionState> self = *p_args[p_argcount-1]; //hi, I'm myself, needed this to remain alive.
+
+ 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();
+ }
+
+ r_error.error=Variant::CallError::CALL_OK;
+
+ Variant *working_mem = ((Variant*)stack.ptr()) + working_mem_index;
+
+ *working_mem=args; //arguments go to working mem.
+
+ Variant ret = instance->_call_internal(function,stack.ptr(),stack.size(),node,flow_stack_pos,true,r_error);
+ function=StringName(); //invalidate
+ return ret;
+}
+
+void VisualScriptFunctionState::connect_to_signal(Object* p_obj, const String& p_signal, Array p_binds) {
+
+ Vector<Variant> binds;
+ for(int i=0;i<p_binds.size();i++) {
+ binds.push_back(p_binds[i]);
+ }
+ binds.push_back(Ref<VisualScriptFunctionState>(this)); //add myself on the back to avoid dying from unreferencing
+ p_obj->connect(p_signal,this,"_signal_callback",binds);
+}
+
+bool VisualScriptFunctionState::is_valid() const {
+
+ return function!=StringName();
+}
+
+Variant VisualScriptFunctionState::resume(Array p_args) {
+
+ ERR_FAIL_COND_V(function==StringName(),Variant());
+#ifdef DEBUG_ENABLED
+ if (instance_id && !ObjectDB::get_instance(instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ ERR_FAIL_V(Variant());
+ }
+
+ if (script_id && !ObjectDB::get_instance(script_id)) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
+ ERR_FAIL_V(Variant());
+ }
+#endif
+
+ Variant::CallError r_error;
+ r_error.error=Variant::CallError::CALL_OK;
+
+ Variant *working_mem = ((Variant*)stack.ptr()) + working_mem_index;
+
+ *working_mem=p_args; //arguments go to working mem.
+
+ Variant ret= instance->_call_internal(function,stack.ptr(),stack.size(),node,flow_stack_pos,true,r_error);
+ function=StringName(); //invalidate
+ return ret;
+}
+
+
+void VisualScriptFunctionState::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("connect_to_signal","obj","signals","args"),&VisualScriptFunctionState::connect_to_signal);
+ ObjectTypeDB::bind_method(_MD("resume:Array","args"),&VisualScriptFunctionState::resume,DEFVAL(Variant()));
+ ObjectTypeDB::bind_method(_MD("is_valid"),&VisualScriptFunctionState::is_valid);
+ ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&VisualScriptFunctionState::_signal_callback,MethodInfo("_signal_callback"));
+}
+
+VisualScriptFunctionState::VisualScriptFunctionState() {
+
+}
+
+VisualScriptFunctionState::~VisualScriptFunctionState() {
+
+ if (function!=StringName()) {
+ Variant *s = ((Variant*)stack.ptr());
+ for(int i=0;i<variant_stack_size;i++) {
+ s[i].~Variant();
+ }
+ }
+}