summaryrefslogtreecommitdiff
path: root/scene/main/scene_main_loop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/main/scene_main_loop.cpp')
-rw-r--r--scene/main/scene_main_loop.cpp597
1 files changed, 590 insertions, 7 deletions
diff --git a/scene/main/scene_main_loop.cpp b/scene/main/scene_main_loop.cpp
index cc103b5115..4c7b7c1399 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() {
@@ -64,7 +88,7 @@ void SceneTree::node_removed(Node *p_node) {
}
-void SceneTree::add_to_group(const StringName& p_group, Node *p_node) {
+SceneTree::Group *SceneTree::add_to_group(const StringName& p_group, Node *p_node) {
Map<StringName,Group>::Element *E=group_map.find(p_group);
if (!E) {
@@ -73,10 +97,12 @@ void SceneTree::add_to_group(const StringName& p_group, Node *p_node) {
if (E->get().nodes.find(p_node)!=-1) {
ERR_EXPLAIN("Already in group: "+p_group);
- ERR_FAIL();
+ ERR_FAIL_V(&E->get());
}
E->get().nodes.push_back(p_node);
- E->get().last_tree_version=0;
+ //E->get().last_tree_version=0;
+ E->get().changed=true;
+ return &E->get();
}
void SceneTree::remove_from_group(const StringName& p_group, Node *p_node) {
@@ -125,7 +151,7 @@ void SceneTree::_flush_ugc() {
void SceneTree::_update_group_order(Group& g) {
- if (g.last_tree_version==tree_version)
+ if (!g.changed)
return;
if (g.nodes.empty())
return;
@@ -135,7 +161,8 @@ void SceneTree::_update_group_order(Group& g) {
SortArray<Node*,Node::Comparator> node_sort;
node_sort.sort(nodes,node_count);
- g.last_tree_version=tree_version;
+ g.changed=false;
+
}
@@ -148,8 +175,6 @@ void SceneTree::call_group(uint32_t p_call_flags,const StringName& p_group,const
if (g.nodes.empty())
return;
- _update_group_order(g);
-
if (p_call_flags&GROUP_CALL_UNIQUE && !(p_call_flags&GROUP_CALL_REALTIME)) {
@@ -175,6 +200,8 @@ void SceneTree::call_group(uint32_t p_call_flags,const StringName& p_group,const
return;
}
+ _update_group_order(g);
+
Vector<Node*> nodes_copy = g.nodes;
Node **nodes = &nodes_copy[0];
int node_count=nodes_copy.size();
@@ -518,6 +545,8 @@ bool SceneTree::idle(float p_time){
idle_process_time=p_time;
+ _network_poll();
+
emit_signal("idle_frame");
_flush_transform_notifications();
@@ -544,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;
}
@@ -1002,6 +1048,7 @@ static void _fill_array(Node *p_node, Array& array, int p_level) {
array.push_back(p_level);
array.push_back(p_node->get_name());
array.push_back(p_node->get_type());
+ array.push_back(p_node->get_instance_ID());
for(int i=0;i<p_node->get_child_count();i++) {
_fill_array(p_node->get_child(i),array,p_level+1);
@@ -1592,6 +1639,518 @@ void SceneTree::_live_edit_reparent_node_func(const NodePath& p_at,const NodePat
#endif
+
+
+void SceneTree::drop_files(const Vector<String>& p_files,int p_from_screen) {
+
+ emit_signal("files_dropped",p_files,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() {
@@ -1622,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);
@@ -1657,13 +2218,33 @@ 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") );
+ ADD_SIGNAL( MethodInfo("node_configuration_warning_changed",PropertyInfo( Variant::OBJECT, "node")) );
ADD_SIGNAL( MethodInfo("idle_frame"));
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 );
BIND_CONSTANT( GROUP_CALL_REALTIME );
@@ -1763,6 +2344,8 @@ SceneTree::SceneTree() {
live_edit_root=NodePath("/root");
+ last_send_cache_id=1;
+
#endif