summaryrefslogtreecommitdiff
path: root/scene/main
diff options
context:
space:
mode:
Diffstat (limited to 'scene/main')
-rw-r--r--scene/main/http_request.cpp12
-rw-r--r--scene/main/http_request.h4
-rw-r--r--scene/main/node.cpp717
-rw-r--r--scene/main/node.h60
-rw-r--r--scene/main/scene_main_loop.cpp576
-rw-r--r--scene/main/scene_main_loop.h84
-rw-r--r--scene/main/viewport.cpp1
7 files changed, 1441 insertions, 13 deletions
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 9b79af32bf..c713b5e4dc 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -96,7 +96,7 @@ Error HTTPRequest::_parse_url(const String& p_url) {
return OK;
}
-Error HTTPRequest::request(const String& p_url, const Vector<String>& p_custom_headers, bool p_ssl_validate_domain) {
+Error HTTPRequest::request(const String& p_url, const Vector<String>& p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const String& p_request_data) {
ERR_FAIL_COND_V(!is_inside_tree(),ERR_UNCONFIGURED);
if ( requesting ) {
@@ -104,6 +104,8 @@ Error HTTPRequest::request(const String& p_url, const Vector<String>& p_custom_h
ERR_FAIL_V(ERR_BUSY);
}
+ method=p_method;
+
Error err = _parse_url(p_url);
if (err)
return err;
@@ -114,6 +116,8 @@ Error HTTPRequest::request(const String& p_url, const Vector<String>& p_custom_h
bool has_accept=false;
headers=p_custom_headers;
+ request_data = p_request_data;
+
for(int i=0;i<headers.size();i++) {
if (headers[i].findn("user-agent:")==0)
@@ -281,7 +285,7 @@ bool HTTPRequest::_update_connection() {
switch( client->get_status() ) {
case HTTPClient::STATUS_DISCONNECTED: {
call_deferred("_request_done",RESULT_CANT_CONNECT,0,StringArray(),ByteArray());
- return true; //end it, since it's doing something
+ return true; //end it, since it's doing something
} break;
case HTTPClient::STATUS_RESOLVING: {
client->poll();
@@ -334,7 +338,7 @@ bool HTTPRequest::_update_connection() {
} else {
//did not request yet, do request
- Error err = client->request(HTTPClient::METHOD_GET,request_string,headers);
+ Error err = client->request(method,request_string,headers,request_data);
if (err!=OK) {
call_deferred("_request_done",RESULT_CONNECTION_ERROR,0,StringArray(),ByteArray());
return true;
@@ -531,7 +535,7 @@ int HTTPRequest::get_body_size() const{
void HTTPRequest::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("request","url","custom_headers","ssl_validate_domain"),&HTTPRequest::request,DEFVAL(StringArray()),DEFVAL(true));
+ ObjectTypeDB::bind_method(_MD("request","url","custom_headers","ssl_validate_domain","method","request_data"),&HTTPRequest::request,DEFVAL(StringArray()),DEFVAL(true),DEFVAL(HTTPClient::METHOD_GET),DEFVAL(String()));
ObjectTypeDB::bind_method(_MD("cancel_request"),&HTTPRequest::cancel_request);
ObjectTypeDB::bind_method(_MD("get_http_client_status"),&HTTPRequest::get_http_client_status);
diff --git a/scene/main/http_request.h b/scene/main/http_request.h
index 8dd433982b..705799f044 100644
--- a/scene/main/http_request.h
+++ b/scene/main/http_request.h
@@ -66,6 +66,8 @@ private:
Vector<String> headers;
bool validate_ssl;
bool use_ssl;
+ HTTPClient::Method method;
+ String request_data;
bool request_sent;
Ref<HTTPClient> client;
@@ -114,7 +116,7 @@ protected:
static void _bind_methods();
public:
- Error request(const String& p_url,const Vector<String>& p_custom_headers=Vector<String>(),bool p_ssl_validate_domain=true); //connects to a full url and perform request
+ Error request(const String& p_url, const Vector<String>& p_custom_headers=Vector<String>(), bool p_ssl_validate_domain=true, HTTPClient::Method p_method=HTTPClient::METHOD_GET, const String& p_request_data=""); //connects to a full url and perform request
void cancel_request();
HTTPClient::Status get_http_client_status() const;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index a53c19d2e7..1892240426 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -36,6 +36,8 @@
#include "instance_placeholder.h"
VARIANT_ENUM_CAST(Node::PauseMode);
+VARIANT_ENUM_CAST(Node::NetworkMode);
+VARIANT_ENUM_CAST(Node::RPCMode);
@@ -77,6 +79,16 @@ void Node::_notification(int p_notification) {
data.pause_owner=this;
}
+ if (data.network_mode==NETWORK_MODE_INHERIT) {
+
+ if (data.parent)
+ data.network_owner=data.parent->data.network_owner;
+ else
+ data.network_owner=NULL;
+ } else {
+ data.network_owner=this;
+ }
+
if (data.input)
add_to_group("_vp_input"+itos(get_viewport()->get_instance_ID()));
if (data.unhandled_input)
@@ -97,6 +109,20 @@ void Node::_notification(int p_notification) {
if (data.unhandled_key_input)
remove_from_group("_vp_unhandled_key_input"+itos(get_viewport()->get_instance_ID()));
+
+ data.pause_owner=NULL;
+ data.network_owner=NULL;
+ if (data.path_cache) {
+ memdelete(data.path_cache);
+ data.path_cache=NULL;
+ }
+ } break;
+ case NOTIFICATION_PATH_CHANGED: {
+
+ if (data.path_cache) {
+ memdelete(data.path_cache);
+ data.path_cache=NULL;
+ }
} break;
case NOTIFICATION_READY: {
@@ -412,6 +438,640 @@ void Node::_propagate_pause_owner(Node*p_owner) {
}
}
+void Node::set_network_mode(NetworkMode p_mode) {
+
+ if (data.network_mode==p_mode)
+ return;
+
+ bool prev_inherits=data.network_mode==NETWORK_MODE_INHERIT;
+ data.network_mode=p_mode;
+ if (!is_inside_tree())
+ return; //pointless
+ if ((data.network_mode==NETWORK_MODE_INHERIT) == prev_inherits)
+ return; ///nothing changed
+
+ Node *owner=NULL;
+
+ if (data.network_mode==NETWORK_MODE_INHERIT) {
+
+ if (data.parent)
+ owner=data.parent->data.network_owner;
+ } else {
+ owner=this;
+ }
+
+ _propagate_network_owner(owner);
+
+
+
+}
+
+Node::NetworkMode Node::get_network_mode() const {
+
+ return data.network_mode;
+}
+
+bool Node::is_network_master() const {
+
+ ERR_FAIL_COND_V(!is_inside_tree(),false);
+
+ switch(data.network_mode) {
+ case NETWORK_MODE_INHERIT: {
+
+ if (data.network_owner)
+ return data.network_owner->is_network_master();
+ else
+ return get_tree()->is_network_server();
+ } break;
+ case NETWORK_MODE_MASTER: {
+
+ return true;
+ } break;
+ case NETWORK_MODE_SLAVE: {
+ return false;
+ } break;
+ }
+
+ return false;
+}
+
+
+
+void Node::_propagate_network_owner(Node*p_owner) {
+
+ if (data.network_mode!=NETWORK_MODE_INHERIT)
+ return;
+ data.network_owner=p_owner;
+ for(int i=0;i<data.children.size();i++) {
+
+ data.children[i]->_propagate_network_owner(p_owner);
+ }
+}
+
+/***** RPC CONFIG ********/
+
+void Node::rpc_config(const StringName& p_method,RPCMode p_mode) {
+
+ if (p_mode==RPC_MODE_DISABLED) {
+ data.rpc_methods.erase(p_method);
+ } else {
+ data.rpc_methods[p_method]=p_mode;
+ };
+}
+
+void Node::rset_config(const StringName& p_property,RPCMode p_mode) {
+
+ if (p_mode==RPC_MODE_DISABLED) {
+ data.rpc_properties.erase(p_property);
+ } else {
+ data.rpc_properties[p_property]=p_mode;
+ };
+}
+
+/***** RPC FUNCTIONS ********/
+
+void Node::rpc(const StringName& p_method,VARIANT_ARG_DECLARE) {
+
+ VARIANT_ARGPTRS;
+
+ int argc=0;
+ for(int i=0;i<VARIANT_ARG_MAX;i++) {
+ if (argptr[i]->get_type()==Variant::NIL)
+ break;
+ argc++;
+ }
+
+ rpcp(0,false,p_method,argptr,argc);
+}
+
+
+void Node::rpc_id(int p_peer_id,const StringName& p_method,VARIANT_ARG_DECLARE) {
+
+ VARIANT_ARGPTRS;
+
+ int argc=0;
+ for(int i=0;i<VARIANT_ARG_MAX;i++) {
+ if (argptr[i]->get_type()==Variant::NIL)
+ break;
+ argc++;
+ }
+
+ rpcp(p_peer_id,false,p_method,argptr,argc);
+}
+
+
+void Node::rpc_unreliable(const StringName& p_method,VARIANT_ARG_DECLARE) {
+
+ VARIANT_ARGPTRS;
+
+ int argc=0;
+ for(int i=0;i<VARIANT_ARG_MAX;i++) {
+ if (argptr[i]->get_type()==Variant::NIL)
+ break;
+ argc++;
+ }
+
+ rpcp(0,true,p_method,argptr,argc);
+}
+
+
+void Node::rpc_unreliable_id(int p_peer_id,const StringName& p_method,VARIANT_ARG_DECLARE) {
+
+ VARIANT_ARGPTRS;
+
+ int argc=0;
+ for(int i=0;i<VARIANT_ARG_MAX;i++) {
+ if (argptr[i]->get_type()==Variant::NIL)
+ break;
+ argc++;
+ }
+
+ rpcp(p_peer_id,true,p_method,argptr,argc);
+}
+
+
+Variant Node::_rpc_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+
+ if (p_argcount<1) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=1;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::STRING;
+ return Variant();
+ }
+
+ StringName method = *p_args[0];
+
+ rpcp(0,false,method,&p_args[1],p_argcount-1);
+
+ r_error.error=Variant::CallError::CALL_OK;
+ return Variant();
+}
+
+
+Variant Node::_rpc_id_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+
+ if (p_argcount<2) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=2;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type()!=Variant::INT) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::INT;
+ return Variant();
+ }
+
+ if (p_args[1]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=1;
+ r_error.expected=Variant::STRING;
+ return Variant();
+ }
+
+ int peer_id = *p_args[0];
+ StringName method = *p_args[1];
+
+ rpcp(peer_id,false,method,&p_args[2],p_argcount-2);
+
+ r_error.error=Variant::CallError::CALL_OK;
+ return Variant();
+}
+
+
+Variant Node::_rpc_unreliable_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+
+ if (p_argcount<1) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=1;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::STRING;
+ return Variant();
+ }
+
+ StringName method = *p_args[0];
+
+ rpcp(0,true,method,&p_args[1],p_argcount-1);
+
+ r_error.error=Variant::CallError::CALL_OK;
+ return Variant();
+}
+
+
+Variant Node::_rpc_unreliable_id_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+
+ if (p_argcount<2) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=2;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type()!=Variant::INT) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::INT;
+ return Variant();
+ }
+
+ if (p_args[1]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=1;
+ r_error.expected=Variant::STRING;
+ return Variant();
+ }
+
+ int peer_id = *p_args[0];
+ StringName method = *p_args[1];
+
+ rpcp(peer_id,true,method,&p_args[2],p_argcount-2);
+
+ r_error.error=Variant::CallError::CALL_OK;
+ return Variant();
+}
+
+#if 0
+Variant Node::_rpc_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+
+ if (p_argcount<1) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=1;
+ return Variant();
+ }
+
+ if (p_args[0]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::STRING;
+ return Variant();
+ }
+
+ StringName method = *p_args[0];
+
+ rpcp(method,&p_args[1],p_argcount-1);
+
+ r_error.error=Variant::CallError::CALL_OK;
+ return Variant();
+}
+
+#endif
+void Node::rpcp(int p_peer_id,bool p_unreliable,const StringName& p_method,const Variant** p_arg,int p_argcount) {
+
+ ERR_FAIL_COND(!is_inside_tree());
+
+ bool skip_rpc=false;
+
+ if (p_peer_id==0 || p_peer_id==get_tree()->get_network_unique_id() || (p_peer_id<0 && p_peer_id!=-get_tree()->get_network_unique_id())) {
+ //check that send mode can use local call
+
+
+ bool call_local=false;
+
+
+
+ Map<StringName,RPCMode>::Element *E = data.rpc_methods.find(p_method);
+ if (E) {
+
+ switch(E->get()) {
+
+ case RPC_MODE_DISABLED: {
+ //do nothing
+ } break;
+ case RPC_MODE_REMOTE: {
+ //do nothing also, no need to call local
+ } break;
+ case RPC_MODE_SYNC: {
+ //call it, sync always results in call
+ call_local=true;
+ } break;
+ case RPC_MODE_MASTER: {
+ call_local=is_network_master();
+ if (call_local) {
+ skip_rpc=true; //no other master so..
+ }
+ } break;
+ case RPC_MODE_SLAVE: {
+ call_local=!is_network_master();
+ } break;
+
+ }
+ }
+
+
+ if (call_local) {
+ Variant::CallError ce;
+ call(p_method,p_arg,p_argcount,ce);
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(this,p_method,p_arg,p_argcount,ce);
+ error="rpc() aborted in local call: - "+error;
+ ERR_PRINTS(error);
+ return;
+ }
+ } else if (get_script_instance()){
+ //attempt with script
+ ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rpc_mode(p_method);
+
+ switch(rpc_mode) {
+
+ case ScriptInstance::RPC_MODE_DISABLED: {
+ //do nothing
+ } break;
+ case ScriptInstance::RPC_MODE_REMOTE: {
+ //do nothing also, no need to call local
+ } break;
+ case ScriptInstance::RPC_MODE_SYNC: {
+ //call it, sync always results in call
+ call_local=true;
+ } break;
+ case ScriptInstance::RPC_MODE_MASTER: {
+ call_local=is_network_master();
+ if (call_local) {
+ skip_rpc=true; //no other master so..
+ }
+ } break;
+ case ScriptInstance::RPC_MODE_SLAVE: {
+ call_local=!is_network_master();
+ } break;
+ }
+
+ if (call_local) {
+ Variant::CallError ce;
+ ce.error=Variant::CallError::CALL_OK;
+ get_script_instance()->call(p_method,p_arg,p_argcount,ce);
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(this,p_method,p_arg,p_argcount,ce);
+ error="rpc() aborted in script local call: - "+error;
+ ERR_PRINTS(error);
+ return;
+ }
+ }
+ }
+ }
+
+ if (skip_rpc)
+ return;
+
+
+ get_tree()->_rpc(this,p_peer_id,p_unreliable,false,p_method,p_arg,p_argcount);
+
+}
+
+
+/******** RSET *********/
+
+
+void Node::rsetp(int p_peer_id,bool p_unreliable,const StringName& p_property,const Variant& p_value) {
+
+ ERR_FAIL_COND(!is_inside_tree());
+
+ bool skip_rset=false;
+
+ if (p_peer_id==0 || p_peer_id==get_tree()->get_network_unique_id() || (p_peer_id<0 && p_peer_id!=-get_tree()->get_network_unique_id())) {
+ //check that send mode can use local call
+
+
+ bool set_local=false;
+
+ Map<StringName,RPCMode>::Element *E = data.rpc_properties.find(p_property);
+ if (E) {
+
+ switch(E->get()) {
+
+ case RPC_MODE_DISABLED: {
+ //do nothing
+ } break;
+ case RPC_MODE_REMOTE: {
+ //do nothing also, no need to call local
+ } break;
+ case RPC_MODE_SYNC: {
+ //call it, sync always results in call
+ set_local=true;
+ } break;
+ case RPC_MODE_MASTER: {
+ set_local=is_network_master();
+ if (set_local) {
+ skip_rset=true;
+ }
+
+ } break;
+ case RPC_MODE_SLAVE: {
+ set_local=!is_network_master();
+ } break;
+
+ }
+ }
+
+
+ if (set_local) {
+ bool valid;
+ set(p_property,p_value,&valid);
+
+ if (!valid) {
+ String error="rset() aborted in local set, property not found: - "+String(p_property);
+ ERR_PRINTS(error);
+ return;
+ }
+ } else if (get_script_instance()){
+ //attempt with script
+ ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rset_mode(p_property);
+
+ switch(rpc_mode) {
+
+ case ScriptInstance::RPC_MODE_DISABLED: {
+ //do nothing
+ } break;
+ case ScriptInstance::RPC_MODE_REMOTE: {
+ //do nothing also, no need to call local
+ } break;
+ case ScriptInstance::RPC_MODE_SYNC: {
+ //call it, sync always results in call
+ set_local=true;
+ } break;
+ case ScriptInstance::RPC_MODE_MASTER: {
+ set_local=is_network_master();
+ if (set_local) {
+ skip_rset=true;
+ }
+ } break;
+ case ScriptInstance::RPC_MODE_SLAVE: {
+ set_local=!is_network_master();
+ } break;
+ }
+
+ if (set_local) {
+
+ bool valid = get_script_instance()->set(p_property,p_value);
+
+ if (!valid) {
+ String error="rset() aborted in local script set, property not found: - "+String(p_property);
+ ERR_PRINTS(error);
+ return;
+ }
+ }
+
+ }
+ }
+
+ if (skip_rset)
+ return;
+
+ const Variant*vptr = &p_value;
+
+ get_tree()->_rpc(this,p_peer_id,p_unreliable,true,p_property,&vptr,1);
+
+}
+
+
+
+void Node::rset(const StringName& p_property,const Variant& p_value) {
+
+ rsetp(0,false,p_property,p_value);
+
+}
+
+void Node::rset_id(int p_peer_id,const StringName& p_property,const Variant& p_value) {
+
+ rsetp(p_peer_id,false,p_property,p_value);
+
+}
+
+void Node::rset_unreliable(const StringName& p_property,const Variant& p_value) {
+
+ rsetp(0,true,p_property,p_value);
+
+}
+
+void Node::rset_unreliable_id(int p_peer_id,const StringName& p_property,const Variant& p_value) {
+
+ rsetp(p_peer_id,true,p_property,p_value);
+
+}
+
+//////////// end of rpc
+
+bool Node::can_call_rpc(const StringName& p_method) const {
+
+ const Map<StringName,RPCMode>::Element *E = data.rpc_methods.find(p_method);
+ if (E) {
+
+ switch(E->get()) {
+
+ case RPC_MODE_DISABLED: {
+ return false;
+ } break;
+ case RPC_MODE_REMOTE: {
+ return true;
+ } break;
+ case RPC_MODE_SYNC: {
+ return true;
+ } break;
+ case RPC_MODE_MASTER: {
+ return is_network_master();
+ } break;
+ case RPC_MODE_SLAVE: {
+ return !is_network_master();
+ } break;
+ }
+ }
+
+
+ if (get_script_instance()){
+ //attempt with script
+ ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rpc_mode(p_method);
+
+ switch(rpc_mode) {
+
+ case ScriptInstance::RPC_MODE_DISABLED: {
+ return false;
+ } break;
+ case ScriptInstance::RPC_MODE_REMOTE: {
+ return true;
+ } break;
+ case ScriptInstance::RPC_MODE_SYNC: {
+ return true;
+ } break;
+ case ScriptInstance::RPC_MODE_MASTER: {
+ return is_network_master();
+ } break;
+ case ScriptInstance::RPC_MODE_SLAVE: {
+ return !is_network_master();
+ } break;
+ }
+
+ }
+
+ ERR_PRINTS("RPC on unauthorized method attempted: "+String(p_method)+" on base: "+String(Variant(this)));
+ return false;
+}
+
+bool Node::can_call_rset(const StringName& p_property) const {
+
+ const Map<StringName,RPCMode>::Element *E = data.rpc_properties.find(p_property);
+ if (E) {
+
+ switch(E->get()) {
+
+ case RPC_MODE_DISABLED: {
+ return false;
+ } break;
+ case RPC_MODE_REMOTE: {
+ return true;
+ } break;
+ case RPC_MODE_SYNC: {
+ return true;
+ } break;
+ case RPC_MODE_MASTER: {
+ return is_network_master();
+ } break;
+ case RPC_MODE_SLAVE: {
+ return !is_network_master();
+ } break;
+ }
+ }
+
+
+ if (get_script_instance()){
+ //attempt with script
+ ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rset_mode(p_property);
+
+ switch(rpc_mode) {
+
+ case ScriptInstance::RPC_MODE_DISABLED: {
+ return false;
+ } break;
+ case ScriptInstance::RPC_MODE_REMOTE: {
+ return true;
+ } break;
+ case ScriptInstance::RPC_MODE_SYNC: {
+ return true;
+ } break;
+ case ScriptInstance::RPC_MODE_MASTER: {
+ return is_network_master();
+ } break;
+ case ScriptInstance::RPC_MODE_SLAVE: {
+ return !is_network_master();
+ } break;
+ }
+
+ }
+
+ ERR_PRINTS("RSET on unauthorized property attempted: "+String(p_property)+" on base: "+String(Variant(this)));
+
+ return false;
+}
+
+
bool Node::can_process() const {
ERR_FAIL_COND_V( !is_inside_tree(), false );
@@ -567,6 +1227,8 @@ void Node::set_name(const String& p_name) {
data.parent->_validate_child_name(this);
}
+ propagate_notification(NOTIFICATION_PATH_CHANGED);
+
if (is_inside_tree()) {
emit_signal("renamed");
@@ -1210,6 +1872,10 @@ NodePath Node::get_path_to(const Node *p_node) const {
NodePath Node::get_path() const {
ERR_FAIL_COND_V(!is_inside_tree(),NodePath());
+
+ if (data.path_cache)
+ return *data.path_cache;
+
const Node *n = this;
Vector<StringName> path;
@@ -1221,7 +1887,9 @@ NodePath Node::get_path() const {
path.invert();
- return NodePath( path, true );
+ data.path_cache = memnew( NodePath( path, true ) );
+
+ return *data.path_cache;
}
bool Node::is_in_group(const StringName& p_identifier) const {
@@ -2212,7 +2880,13 @@ void Node::_bind_methods() {
ObjectTypeDB::bind_method(_MD("queue_free"),&Node::queue_delete);
+ ObjectTypeDB::bind_method(_MD("set_network_mode","mode"),&Node::set_network_mode);
+ ObjectTypeDB::bind_method(_MD("get_network_mode"),&Node::get_network_mode);
+
+ ObjectTypeDB::bind_method(_MD("is_network_master"),&Node::is_network_master);
+ ObjectTypeDB::bind_method(_MD("rpc_config","method","mode"),&Node::rpc_config);
+ ObjectTypeDB::bind_method(_MD("rset_config","property","mode"),&Node::rset_config);
#ifdef TOOLS_ENABLED
@@ -2222,6 +2896,33 @@ void Node::_bind_methods() {
#endif
+ {
+ MethodInfo mi;
+
+ mi.arguments.push_back( PropertyInfo( Variant::STRING, "method"));
+
+
+ mi.name="rpc";
+ ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"rpc",&Node::_rpc_bind,mi);
+ mi.name="rpc_unreliable";
+ ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"rpc_unreliable",&Node::_rpc_unreliable_bind,mi);
+
+ mi.arguments.push_front( PropertyInfo( Variant::INT, "peer_id") );
+
+ mi.name="rpc_id";
+ ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"rpc_id",&Node::_rpc_id_bind,mi);
+ mi.name="rpc_unreliable_id";
+ ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"rpc_unreliable_id",&Node::_rpc_unreliable_id_bind,mi);
+
+
+ }
+
+ ObjectTypeDB::bind_method(_MD("rset","property","value:Variant"),&Node::rset);
+ ObjectTypeDB::bind_method(_MD("rset_id","peer_id","property","value:Variant"),&Node::rset_id);
+ ObjectTypeDB::bind_method(_MD("rset_unreliable","property","value:Variant"),&Node::rset_unreliable);
+ ObjectTypeDB::bind_method(_MD("rset_unreliable_id","peer_id","property","value:Variant"),&Node::rset_unreliable_id);
+
+
BIND_CONSTANT( NOTIFICATION_ENTER_TREE );
BIND_CONSTANT( NOTIFICATION_EXIT_TREE );
BIND_CONSTANT( NOTIFICATION_MOVED_IN_PARENT );
@@ -2236,8 +2937,18 @@ void Node::_bind_methods() {
BIND_CONSTANT( NOTIFICATION_INSTANCED );
BIND_CONSTANT( NOTIFICATION_DRAG_BEGIN );
BIND_CONSTANT( NOTIFICATION_DRAG_END );
+ BIND_CONSTANT( NOTIFICATION_PATH_CHANGED);
+ BIND_CONSTANT( NETWORK_MODE_INHERIT );
+ BIND_CONSTANT( NETWORK_MODE_MASTER );
+ BIND_CONSTANT( NETWORK_MODE_SLAVE );
+
+ BIND_CONSTANT( RPC_MODE_DISABLED );
+ BIND_CONSTANT( RPC_MODE_REMOTE );
+ BIND_CONSTANT( RPC_MODE_SYNC );
+ BIND_CONSTANT( RPC_MODE_MASTER );
+ BIND_CONSTANT( RPC_MODE_SLAVE );
BIND_CONSTANT( PAUSE_MODE_INHERIT );
BIND_CONSTANT( PAUSE_MODE_STOP );
@@ -2286,11 +2997,15 @@ Node::Node() {
data.unhandled_key_input=false;
data.pause_mode=PAUSE_MODE_INHERIT;
data.pause_owner=NULL;
+ data.network_mode=NETWORK_MODE_INHERIT;
+ data.network_owner=NULL;
+ data.path_cache=NULL;
data.parent_owned=false;
data.in_constructor=true;
data.viewport=NULL;
data.use_placeholder=false;
data.display_folded=false;
+
}
Node::~Node() {
diff --git a/scene/main/node.h b/scene/main/node.h
index 18e403cd61..3568da2ab0 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -53,6 +53,21 @@ public:
PAUSE_MODE_PROCESS
};
+ enum NetworkMode {
+
+ NETWORK_MODE_INHERIT,
+ NETWORK_MODE_MASTER,
+ NETWORK_MODE_SLAVE
+ };
+
+ enum RPCMode {
+
+ RPC_MODE_DISABLED, //no rpc for this method, calls to this will be blocked (default)
+ RPC_MODE_REMOTE, // using rpc() on it will call method / set property in all other peers
+ RPC_MODE_SYNC, // using rpc() on it will call method / set property in all other peers and locally
+ RPC_MODE_MASTER, // usinc rpc() on it will call method on wherever the master is, be it local or remote
+ RPC_MODE_SLAVE, // usinc rpc() on it will call method for all slaves, be it local or remote
+ };
struct Comparator {
@@ -68,6 +83,8 @@ private:
GroupData() { persistent=false; }
};
+
+
struct Data {
String filename;
@@ -98,6 +115,13 @@ private:
PauseMode pause_mode;
Node *pause_owner;
+
+ NetworkMode network_mode;
+ Node *network_owner;
+ Map<StringName,RPCMode> rpc_methods;
+ Map<StringName,RPCMode> rpc_properties;
+
+
// variables used to properly sort the node when processing, ignored otherwise
//should move all the stuff below to bits
bool fixed_process;
@@ -113,6 +137,8 @@ private:
bool display_folded;
+ mutable NodePath *path_cache;
+
} data;
@@ -134,6 +160,7 @@ private:
void _propagate_validate_owner();
void _print_stray_nodes();
void _propagate_pause_owner(Node*p_owner);
+ void _propagate_network_owner(Node*p_owner);
Array _get_node_and_resource(const NodePath& p_path);
void _duplicate_signals(const Node* p_original,Node* p_copy) const;
@@ -143,6 +170,11 @@ private:
Array _get_children() const;
Array _get_groups() const;
+ Variant _rpc_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error);
+ Variant _rpc_unreliable_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error);
+ Variant _rpc_id_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error);
+ Variant _rpc_unreliable_id_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error);
+
friend class SceneTree;
void _set_tree(SceneTree *p_tree);
@@ -186,6 +218,7 @@ public:
NOTIFICATION_INSTANCED=20,
NOTIFICATION_DRAG_BEGIN=21,
NOTIFICATION_DRAG_END=22,
+ NOTIFICATION_PATH_CHANGED=23,
};
/* NODE/TREE */
@@ -331,7 +364,32 @@ public:
void set_display_folded(bool p_folded);
bool is_displayed_folded() const;
- /* CANVAS */
+ /* NETWORK */
+
+ void set_network_mode(NetworkMode p_mode);
+ NetworkMode get_network_mode() const;
+ bool is_network_master() const;
+
+ void rpc_config(const StringName& p_method,RPCMode p_mode); // config a local method for RPC
+ void rset_config(const StringName& p_property,RPCMode p_mode); // config a local property for RPC
+
+ void rpc(const StringName& p_method,VARIANT_ARG_LIST); //rpc call, honors RPCMode
+ void rpc_unreliable(const StringName& p_method,VARIANT_ARG_LIST); //rpc call, honors RPCMode
+ void rpc_id(int p_peer_id,const StringName& p_method,VARIANT_ARG_LIST); //rpc call, honors RPCMode
+ void rpc_unreliable_id(int p_peer_id,const StringName& p_method,VARIANT_ARG_LIST); //rpc call, honors RPCMode
+
+ void rpcp(int p_peer_id,bool p_unreliable,const StringName& p_method,const Variant** p_arg,int p_argcount);
+
+ void rset(const StringName& p_property, const Variant& p_value); //remote set call, honors RPCMode
+ void rset_unreliable(const StringName& p_property,const Variant& p_value); //remote set call, honors RPCMode
+ void rset_id(int p_peer_id,const StringName& p_property,const Variant& p_value); //remote set call, honors RPCMode
+ void rset_unreliable_id(int p_peer_id,const StringName& p_property,const Variant& p_value); //remote set call, honors RPCMode
+
+ void rsetp(int p_peer_id,bool p_unreliable,const StringName& p_property,const Variant& p_value);
+
+ bool can_call_rpc(const StringName& p_method) const;
+ bool can_call_rset(const StringName& p_property) const;
+
Node();
~Node();
diff --git a/scene/main/scene_main_loop.cpp b/scene/main/scene_main_loop.cpp
index a7ef7ca7c1..e3472c074a 100644
--- a/scene/main/scene_main_loop.cpp
+++ b/scene/main/scene_main_loop.cpp
@@ -44,6 +44,30 @@
#include "scene/resources/packed_scene.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
+#include "io/marshalls.h"
+
+void SceneTreeTimer::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_time_left","time"),&SceneTreeTimer::set_time_left);
+ ObjectTypeDB::bind_method(_MD("get_time_left"),&SceneTreeTimer::get_time_left);
+
+ ADD_SIGNAL(MethodInfo("timeout"));
+}
+
+
+void SceneTreeTimer::set_time_left(float p_time) {
+ time_left=p_time;
+}
+
+float SceneTreeTimer::get_time_left() const {
+ return time_left;
+}
+
+
+SceneTreeTimer::SceneTreeTimer() {
+ time_left=0;
+}
+
void SceneTree::tree_changed() {
@@ -521,6 +545,8 @@ bool SceneTree::idle(float p_time){
idle_process_time=p_time;
+ _network_poll();
+
emit_signal("idle_frame");
_flush_transform_notifications();
@@ -547,6 +573,23 @@ bool SceneTree::idle(float p_time){
_flush_delete_queue();
+ //go through timers
+
+ for (List<Ref<SceneTreeTimer> >::Element *E=timers.front();E;) {
+
+ List<Ref<SceneTreeTimer> >::Element *N = E->next();
+
+ float time_left = E->get()->get_time_left();
+ time_left-=p_time;
+ E->get()->set_time_left(time_left);
+
+ if (time_left<0) {
+ E->get()->emit_signal("timeout");
+ timers.erase(E);
+ }
+ E=N;
+ }
+
return _quit;
}
@@ -1604,6 +1647,510 @@ void SceneTree::drop_files(const Vector<String>& p_files,int p_from_screen) {
MainLoop::drop_files(p_files,p_from_screen);
}
+
+Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec) {
+
+ Ref<SceneTreeTimer> stt;
+ stt.instance();
+ stt->set_time_left(p_delay_sec);
+ timers.push_back(stt);
+ return stt;
+}
+
+void SceneTree::_network_peer_connected(int p_id) {
+
+
+ connected_peers.insert(p_id);
+ path_get_cache.insert(p_id,PathGetCache());
+
+
+ emit_signal("network_peer_connected",p_id);
+}
+
+void SceneTree::_network_peer_disconnected(int p_id) {
+
+ connected_peers.erase(p_id);
+ path_get_cache.erase(p_id); //I no longer need your cache, sorry
+ emit_signal("network_peer_disconnected",p_id);
+}
+
+void SceneTree::_connected_to_server() {
+
+ emit_signal("connected_to_server");
+}
+
+void SceneTree::_connection_failed() {
+
+ emit_signal("connection_failed");
+}
+
+void SceneTree::_server_disconnected() {
+
+ emit_signal("server_disconnected");
+}
+
+void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer>& p_network_peer) {
+ if (network_peer.is_valid()) {
+ network_peer->disconnect("peer_connected",this,"_network_peer_connected");
+ network_peer->disconnect("peer_disconnected",this,"_network_peer_disconnected");
+ network_peer->disconnect("connection_succeeded",this,"_connected_to_server");
+ network_peer->disconnect("connection_failed",this,"_connection_failed");
+ network_peer->disconnect("server_disconnected",this,"_server_disconnected");
+ connected_peers.clear();
+ path_get_cache.clear();
+ path_send_cache.clear();
+ last_send_cache_id=1;
+ }
+
+ ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected.");
+ ERR_FAIL_COND(p_network_peer.is_valid() && p_network_peer->get_connection_status()==NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED);
+
+ network_peer=p_network_peer;
+
+ if (network_peer.is_valid()) {
+ network_peer->connect("peer_connected",this,"_network_peer_connected");
+ network_peer->connect("peer_disconnected",this,"_network_peer_disconnected");
+ network_peer->connect("connection_succeeded",this,"_connected_to_server");
+ network_peer->connect("connection_failed",this,"_connection_failed");
+ network_peer->connect("server_disconnected",this,"_server_disconnected");
+ }
+}
+
+bool SceneTree::is_network_server() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(),false);
+ return network_peer->is_server();
+
+}
+
+int SceneTree::get_network_unique_id() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(),0);
+ return network_peer->get_unique_id();
+}
+
+void SceneTree::set_refuse_new_network_connections(bool p_refuse) {
+ ERR_FAIL_COND(!network_peer.is_valid());
+ network_peer->set_refuse_new_connections(p_refuse);
+}
+
+bool SceneTree::is_refusing_new_network_connections() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(),false);
+
+ return network_peer->is_refusing_new_connections();
+
+}
+
+
+void SceneTree::_rpc(Node* p_from,int p_to,bool p_unreliable,bool p_set,const StringName& p_name,const Variant** p_arg,int p_argcount) {
+
+ if (network_peer.is_null()) {
+ ERR_EXPLAIN("Attempt to remote call/set when networking is not active in SceneTree.");
+ ERR_FAIL();
+ }
+
+ if (network_peer->get_connection_status()==NetworkedMultiplayerPeer::CONNECTION_CONNECTING) {
+ ERR_EXPLAIN("Attempt to remote call/set when networking is not connected yet in SceneTree.");
+ ERR_FAIL();
+ }
+
+ if (network_peer->get_connection_status()==NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) {
+ ERR_EXPLAIN("Attempt to remote call/set when networking is disconnected.");
+ ERR_FAIL();
+ }
+
+ if (p_argcount>255) {
+ ERR_EXPLAIN("Too many arguments >255.");
+ ERR_FAIL();
+ }
+
+ if (p_to!=0 && !connected_peers.has(ABS(p_to))) {
+ if (p_to==get_network_unique_id()) {
+ ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: "+itos(get_network_unique_id()));
+ } else {
+ ERR_EXPLAIN("Attempt to remote call unexisting ID: "+itos(p_to));
+
+ }
+
+ ERR_FAIL();
+ }
+
+ NodePath from_path = p_from->get_path();
+ ERR_FAIL_COND(from_path.is_empty());
+
+
+
+ //see if the path is cached
+ PathSentCache *psc = path_send_cache.getptr(from_path);
+ if (!psc) {
+ //path is not cached, create
+ path_send_cache[from_path]=PathSentCache();
+ psc = path_send_cache.getptr(from_path);
+ psc->id=last_send_cache_id++;
+
+ }
+
+
+ //create base packet, lots of harcode because it must be tight
+
+ int ofs=0;
+
+#define MAKE_ROOM(m_amount) if (packet_cache.size() < m_amount) packet_cache.resize(m_amount);
+
+ //encode type
+ MAKE_ROOM(1);
+ packet_cache[0]=p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
+ ofs+=1;
+
+ //encode ID
+ MAKE_ROOM(ofs+4);
+ encode_uint32(psc->id,&packet_cache[ofs]);
+ ofs+=4;
+
+ //encode function name
+ CharString name = String(p_name).utf8();
+ int len = encode_cstring(name.get_data(),NULL);
+ MAKE_ROOM(ofs+len);
+ encode_cstring(name.get_data(),&packet_cache[ofs]);
+ ofs+=len;
+
+ if (p_set) {
+ //set argument
+ Error err = encode_variant(*p_arg[0],NULL,len);
+ ERR_FAIL_COND(err!=OK);
+ MAKE_ROOM(ofs+len);
+ encode_variant(*p_arg[0],&packet_cache[ofs],len);
+ ofs+=len;
+
+ } else {
+ //call arguments
+ MAKE_ROOM(ofs+1);
+ packet_cache[ofs]=p_argcount;
+ ofs+=1;
+ for(int i=0;i<p_argcount;i++) {
+ Error err = encode_variant(*p_arg[i],NULL,len);
+ ERR_FAIL_COND(err!=OK);
+ MAKE_ROOM(ofs+len);
+ encode_variant(*p_arg[i],&packet_cache[ofs],len);
+ ofs+=len;
+ }
+
+ }
+
+ //see if all peers have cached path (is so, call can be fast)
+ bool has_all_peers=true;
+
+ List<int> peers_to_add; //if one is missing, take note to add it
+
+ for (Set<int>::Element *E=connected_peers.front();E;E=E->next()) {
+
+ if (p_to<0 && E->get()==-p_to)
+ continue; //continue, excluded
+
+ if (p_to>0 && E->get()!=p_to)
+ continue; //continue, not for this peer
+
+ Map<int,bool>::Element *F = psc->confirmed_peers.find(E->get());
+
+ if (!F || F->get()==false) {
+ //path was not cached, or was cached but is unconfirmed
+ if (!F) {
+ //not cached at all, take note
+ peers_to_add.push_back(E->get());
+ }
+
+ has_all_peers=false;
+ }
+ }
+
+ //those that need to be added, send a message for this
+
+ for (List<int>::Element *E=peers_to_add.front();E;E=E->next()) {
+
+ //encode function name
+ CharString pname = String(from_path).utf8();
+ int len = encode_cstring(pname.get_data(),NULL);
+
+ Vector<uint8_t> packet;
+
+ packet.resize(1+4+len);
+ packet[0]=NETWORK_COMMAND_SIMPLIFY_PATH;
+ encode_uint32(psc->id,&packet[1]);
+ encode_cstring(pname.get_data(),&packet[5]);
+
+ network_peer->set_target_peer(E->get()); //to all of you
+ network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ network_peer->put_packet(packet.ptr(),packet.size());
+
+ psc->confirmed_peers.insert(E->get(),false); //insert into confirmed, but as false since it was not confirmed
+ }
+
+ //take chance and set transfer mode, since all send methods will use it
+ network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+
+ if (has_all_peers) {
+
+ //they all have verified paths, so send fast
+ network_peer->set_target_peer(p_to); //to all of you
+ network_peer->put_packet(packet_cache.ptr(),ofs); //a message with love
+ } else {
+ //not all verified path, so send one by one
+
+ //apend path at the end, since we will need it for some packets
+ CharString pname = String(from_path).utf8();
+ int path_len = encode_cstring(pname.get_data(),NULL);
+ MAKE_ROOM(ofs+path_len);
+ encode_cstring(pname.get_data(),&packet_cache[ofs]);
+
+
+ for (Set<int>::Element *E=connected_peers.front();E;E=E->next()) {
+
+ if (p_to<0 && E->get()==-p_to)
+ continue; //continue, excluded
+
+ if (p_to>0 && E->get()!=p_to)
+ continue; //continue, not for this peer
+
+ Map<int,bool>::Element *F = psc->confirmed_peers.find(E->get());
+ ERR_CONTINUE(!F);//should never happen
+
+ network_peer->set_target_peer(E->get()); //to this one specifically
+
+ if (F->get()==true) {
+ //this one confirmed path, so use id
+ encode_uint32(psc->id,&packet_cache[1]);
+ network_peer->put_packet(packet_cache.ptr(),ofs);
+ } else {
+ //this one did not confirm path yet, so use entire path (sorry!)
+ encode_uint32(0x80000000|ofs,&packet_cache[1]); //offset to path and flag
+ network_peer->put_packet(packet_cache.ptr(),ofs+path_len);
+ }
+
+ }
+ }
+}
+
+
+void SceneTree::_network_process_packet(int p_from, const uint8_t* p_packet, int p_packet_len) {
+
+ ERR_FAIL_COND(p_packet_len<5);
+
+ uint8_t packet_type = p_packet[0];
+
+ switch(packet_type) {
+
+ case NETWORK_COMMAND_REMOTE_CALL:
+ case NETWORK_COMMAND_REMOTE_SET: {
+
+ ERR_FAIL_COND(p_packet_len<5);
+ uint32_t target = decode_uint32(&p_packet[1]);
+
+
+ Node *node=NULL;
+
+ if (target&0x80000000) {
+
+ int ofs = target&0x7FFFFFFF;
+ ERR_FAIL_COND(ofs>=p_packet_len);
+
+ String paths;
+ paths.parse_utf8((const char*)&p_packet[ofs],p_packet_len-ofs);
+
+ NodePath np = paths;
+
+ node = get_root()->get_node(np);
+ if (node==NULL) {
+ ERR_EXPLAIN("Failed to get path from RPC: "+String(np));
+ ERR_FAIL_COND(node==NULL);
+ }
+ } else {
+
+ int id = target;
+
+ Map<int,PathGetCache>::Element *E=path_get_cache.find(p_from);
+ ERR_FAIL_COND(!E);
+
+ Map<int,PathGetCache::NodeInfo>::Element *F=E->get().nodes.find(id);
+ ERR_FAIL_COND(!F);
+
+ PathGetCache::NodeInfo *ni = &F->get();
+ //do proper caching later
+
+ node = get_root()->get_node(ni->path);
+ if (node==NULL) {
+ ERR_EXPLAIN("Failed to get cached path from RPC: "+String(ni->path));
+ ERR_FAIL_COND(node==NULL);
+ }
+
+
+ }
+
+ ERR_FAIL_COND(p_packet_len<6);
+
+ //detect cstring end
+ int len_end=5;
+ for(;len_end<p_packet_len;len_end++) {
+ if (p_packet[len_end]==0) {
+ break;
+ }
+ }
+
+ ERR_FAIL_COND(len_end>=p_packet_len);
+
+ StringName name = String::utf8((const char*)&p_packet[5]);
+
+
+
+
+ if (packet_type==NETWORK_COMMAND_REMOTE_CALL) {
+
+ if (!node->can_call_rpc(name))
+ return;
+
+ int ofs = len_end+1;
+
+ ERR_FAIL_COND(ofs>=p_packet_len);
+
+ int argc = p_packet[ofs];
+ Vector<Variant> args;
+ Vector<const Variant*> argp;
+ args.resize(argc);
+ argp.resize(argc);
+
+ ofs++;
+
+ for(int i=0;i<argc;i++) {
+
+ ERR_FAIL_COND(ofs>=p_packet_len);
+ int vlen;
+ Error err = decode_variant(args[i],&p_packet[ofs],p_packet_len-ofs,&vlen);
+ ERR_FAIL_COND(err!=OK);
+ //args[i]=p_packet[3+i];
+ argp[i]=&args[i];
+ ofs+=vlen;
+ }
+
+ Variant::CallError ce;
+
+ node->call(name,argp.ptr(),argc,ce);
+ if (ce.error!=Variant::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(node,name,argp.ptr(),argc,ce);
+ error="RPC - "+error;
+ ERR_PRINTS(error);
+ }
+
+ } else {
+
+ if (!node->can_call_rset(name))
+ return;
+
+ int ofs = len_end+1;
+
+ ERR_FAIL_COND(ofs>=p_packet_len);
+
+ Variant value;
+ decode_variant(value,&p_packet[ofs],p_packet_len-ofs);
+
+ bool valid;
+
+ node->set(name,value,&valid);
+ if (!valid) {
+ String error = "Error setting remote property '"+String(name)+"', not found in object of type "+node->get_type();
+ ERR_PRINTS(error);
+ }
+ }
+
+ } break;
+ case NETWORK_COMMAND_SIMPLIFY_PATH: {
+
+ ERR_FAIL_COND(p_packet_len<5);
+ int id = decode_uint32(&p_packet[1]);
+
+ String paths;
+ paths.parse_utf8((const char*)&p_packet[5],p_packet_len-5);
+
+ NodePath path = paths;
+
+ if (!path_get_cache.has(p_from)) {
+ path_get_cache[p_from]=PathGetCache();
+ }
+
+ PathGetCache::NodeInfo ni;
+ ni.path=path;
+ ni.instance=0;
+
+ path_get_cache[p_from].nodes[id]=ni;
+
+
+ {
+ //send ack
+
+ //encode path
+ CharString pname = String(path).utf8();
+ int len = encode_cstring(pname.get_data(),NULL);
+
+ Vector<uint8_t> packet;
+
+ packet.resize(1+len);
+ packet[0]=NETWORK_COMMAND_CONFIRM_PATH;
+ encode_cstring(pname.get_data(),&packet[1]);
+
+ network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ network_peer->set_target_peer(p_from);
+ network_peer->put_packet(packet.ptr(),packet.size());
+ }
+ } break;
+ case NETWORK_COMMAND_CONFIRM_PATH: {
+
+ String paths;
+ paths.parse_utf8((const char*)&p_packet[1],p_packet_len-1);
+
+ NodePath path = paths;
+
+ PathSentCache *psc = path_send_cache.getptr(path);
+ ERR_FAIL_COND(!psc);
+
+ Map<int,bool>::Element *E=psc->confirmed_peers.find(p_from);
+ ERR_FAIL_COND(!E);
+ E->get()=true;
+ } break;
+ }
+
+}
+
+void SceneTree::_network_poll() {
+
+ if (!network_peer.is_valid() || network_peer->get_connection_status()==NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED)
+ return;
+
+ network_peer->poll();
+
+ if (!network_peer.is_valid()) //it's possible that polling might have resulted in a disconnection, so check here
+ return;
+
+ while(network_peer->get_available_packet_count()) {
+
+ int sender = network_peer->get_packet_peer();
+ const uint8_t *packet;
+ int len;
+
+ Error err = network_peer->get_packet(&packet,len);
+ if (err!=OK) {
+ ERR_PRINT("Error getting packet!");
+ }
+
+ _network_process_packet(sender,packet,len);
+
+ if (!network_peer.is_valid()) {
+ break; //it's also possible that a packet or RPC caused a disconnection, so also check here
+ }
+ }
+
+
+}
+
+
void SceneTree::_bind_methods() {
@@ -1634,6 +2181,8 @@ void SceneTree::_bind_methods() {
ObjectTypeDB::bind_method(_MD("is_paused"),&SceneTree::is_paused);
ObjectTypeDB::bind_method(_MD("set_input_as_handled"),&SceneTree::set_input_as_handled);
+ ObjectTypeDB::bind_method(_MD("create_timer:SceneTreeTimer","time_sec"),&SceneTree::create_timer);
+
ObjectTypeDB::bind_method(_MD("get_node_count"),&SceneTree::get_node_count);
ObjectTypeDB::bind_method(_MD("get_frame"),&SceneTree::get_frame);
@@ -1651,13 +2200,9 @@ void SceneTree::_bind_methods() {
mi.arguments.push_back( PropertyInfo( Variant::INT, "flags"));
mi.arguments.push_back( PropertyInfo( Variant::STRING, "group"));
mi.arguments.push_back( PropertyInfo( Variant::STRING, "method"));
- Vector<Variant> defargs;
- for(int i=0;i<VARIANT_ARG_MAX;i++) {
- mi.arguments.push_back( PropertyInfo( Variant::NIL, "arg"+itos(i)));
- defargs.push_back(Variant());
- }
- ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"call_group",&SceneTree::_call_group,mi,defargs);
+
+ ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"call_group",&SceneTree::_call_group,mi);
ObjectTypeDB::bind_method(_MD("set_current_scene","child_node:Node"),&SceneTree::set_current_scene);
ObjectTypeDB::bind_method(_MD("get_current_scene:Node"),&SceneTree::get_current_scene);
@@ -1669,6 +2214,18 @@ void SceneTree::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_change_scene"),&SceneTree::_change_scene);
+
+ ObjectTypeDB::bind_method(_MD("set_network_peer","peer:NetworkedMultiplayerPeer"),&SceneTree::set_network_peer);
+ ObjectTypeDB::bind_method(_MD("is_network_server"),&SceneTree::is_network_server);
+ ObjectTypeDB::bind_method(_MD("get_network_unique_id"),&SceneTree::get_network_unique_id);
+ ObjectTypeDB::bind_method(_MD("set_refuse_new_network_connections","refuse"),&SceneTree::set_refuse_new_network_connections);
+ ObjectTypeDB::bind_method(_MD("is_refusing_new_network_connections"),&SceneTree::is_refusing_new_network_connections);
+ ObjectTypeDB::bind_method(_MD("_network_peer_connected"),&SceneTree::_network_peer_connected);
+ ObjectTypeDB::bind_method(_MD("_network_peer_disconnected"),&SceneTree::_network_peer_disconnected);
+ ObjectTypeDB::bind_method(_MD("_connected_to_server"),&SceneTree::_connected_to_server);
+ ObjectTypeDB::bind_method(_MD("_connection_failed"),&SceneTree::_connection_failed);
+ ObjectTypeDB::bind_method(_MD("_server_disconnected"),&SceneTree::_server_disconnected);
+
ADD_SIGNAL( MethodInfo("tree_changed") );
ADD_SIGNAL( MethodInfo("node_removed",PropertyInfo( Variant::OBJECT, "node") ) );
ADD_SIGNAL( MethodInfo("screen_resized") );
@@ -1678,6 +2235,11 @@ void SceneTree::_bind_methods() {
ADD_SIGNAL( MethodInfo("fixed_frame"));
ADD_SIGNAL( MethodInfo("files_dropped",PropertyInfo(Variant::STRING_ARRAY,"files"),PropertyInfo(Variant::INT,"screen")) );
+ ADD_SIGNAL( MethodInfo("network_peer_connected",PropertyInfo(Variant::INT,"id")));
+ ADD_SIGNAL( MethodInfo("network_peer_disconnected",PropertyInfo(Variant::INT,"id")));
+ ADD_SIGNAL( MethodInfo("connected_to_server"));
+ ADD_SIGNAL( MethodInfo("connection_failed"));
+ ADD_SIGNAL( MethodInfo("server_disconnected"));
BIND_CONSTANT( GROUP_CALL_DEFAULT );
BIND_CONSTANT( GROUP_CALL_REVERSE );
@@ -1778,6 +2340,8 @@ SceneTree::SceneTree() {
live_edit_root=NodePath("/root");
+ last_send_cache_id=1;
+
#endif
diff --git a/scene/main/scene_main_loop.h b/scene/main/scene_main_loop.h
index 38d13c0447..1c0f88862c 100644
--- a/scene/main/scene_main_loop.h
+++ b/scene/main/scene_main_loop.h
@@ -35,6 +35,9 @@
#include "scene/resources/world_2d.h"
#include "os/thread_safe.h"
#include "self_list.h"
+#include "io/networked_multiplayer_peer.h"
+
+
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -47,6 +50,22 @@ class Viewport;
class Material;
class Mesh;
+
+
+class SceneTreeTimer : public Reference {
+ OBJ_TYPE(SceneTreeTimer,Reference);
+
+ float time_left;
+protected:
+ static void _bind_methods();
+public:
+
+ void set_time_left(float p_time);
+ float get_time_left() const;
+
+ SceneTreeTimer();
+};
+
class SceneTree : public MainLoop {
_THREAD_SAFE_CLASS_
@@ -155,9 +174,62 @@ private:
void _change_scene(Node* p_to);
//void _call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,const Variant& p_arg1,const Variant& p_arg2);
+ List<Ref<SceneTreeTimer> > timers;
+
+
+ ///network///
+
+ enum NetworkCommands {
+ NETWORK_COMMAND_REMOTE_CALL,
+ NETWORK_COMMAND_REMOTE_SET,
+ NETWORK_COMMAND_SIMPLIFY_PATH,
+ NETWORK_COMMAND_CONFIRM_PATH,
+ };
+
+ Ref<NetworkedMultiplayerPeer> network_peer;
+
+ Set<int> connected_peers;
+ void _network_peer_connected(int p_id);
+ void _network_peer_disconnected(int p_id);
+
+ void _connected_to_server();
+ void _connection_failed();
+ void _server_disconnected();
+
+ //path sent caches
+ struct PathSentCache {
+ Map<int,bool> confirmed_peers;
+ int id;
+ };
+
+ HashMap<NodePath,PathSentCache> path_send_cache;
+ int last_send_cache_id;
+
+ //path get caches
+ struct PathGetCache {
+ struct NodeInfo {
+ NodePath path;
+ ObjectID instance;
+ };
+
+ Map<int,NodeInfo> nodes;
+ };
+
+ Map<int,PathGetCache> path_get_cache;
+
+ Vector<uint8_t> packet_cache;
+
+ void _network_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void _network_poll();
+
static SceneTree *singleton;
friend class Node;
+
+
+
+ void _rpc(Node* p_from,int p_to,bool p_unreliable,bool p_set,const StringName& p_name,const Variant** p_arg,int p_argcount);
+
void tree_changed();
void node_removed(Node *p_node);
@@ -233,6 +305,7 @@ friend class Viewport;
#endif
protected:
+
void _notification(int p_notification);
static void _bind_methods();
@@ -339,6 +412,8 @@ public:
Error change_scene_to(const Ref<PackedScene>& p_scene);
Error reload_current_scene();
+ Ref<SceneTreeTimer> create_timer(float p_delay_sec);
+
//used by Main::start, don't use otherwise
void add_current_scene(Node * p_current);
@@ -346,6 +421,15 @@ public:
void drop_files(const Vector<String>& p_files,int p_from_screen=0);
+ //network API
+
+ void set_network_peer(const Ref<NetworkedMultiplayerPeer>& p_network_peer);
+ bool is_network_server() const;
+ int get_network_unique_id() const;
+
+ void set_refuse_new_network_connections(bool p_refuse);
+ bool is_refusing_new_network_connections() const;
+
SceneTree();
~SceneTree();
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index f182f2c96c..7970229c06 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2095,6 +2095,7 @@ void Viewport::_gui_input_event(InputEvent p_event) {
} break;
case InputEvent::ACTION:
case InputEvent::JOYSTICK_BUTTON:
+ case InputEvent::JOYSTICK_MOTION:
case InputEvent::KEY: {