From 3db36684b183adbec93cce9fe91182186e389e06 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Sun, 14 Aug 2016 18:49:50 -0300 Subject: [PATCH] Added high level networked multiplayer to Godot. It's complete, but absolutely and completely untested, undocumented and NSFW. Have fun :-) --- core/io/networked_multiplayer_peer.h | 2 + modules/enet/networked_multiplayer_enet.cpp | 12 +- modules/enet/networked_multiplayer_enet.h | 4 +- scene/main/node.cpp | 261 +++++++++++++++- scene/main/node.h | 43 ++- scene/main/scene_main_loop.cpp | 328 ++++++++++++++++++++ scene/main/scene_main_loop.h | 53 ++++ 7 files changed, 698 insertions(+), 5 deletions(-) diff --git a/core/io/networked_multiplayer_peer.h b/core/io/networked_multiplayer_peer.h index d8143b02c089..7071a52d7b89 100644 --- a/core/io/networked_multiplayer_peer.h +++ b/core/io/networked_multiplayer_peer.h @@ -32,6 +32,8 @@ public: virtual StringName get_packet_peer() const=0; virtual int get_packet_channel() const=0; + virtual bool is_server() const=0; + virtual void poll()=0; virtual ConnectionStatus get_connection_status() const=0; diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 64f08a90ef2d..aebdfe03a564 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -81,6 +81,8 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip,int p_port, //technically safe to ignore the peer or anything else. connection_status=CONNECTION_CONNECTING; + active=true; + server=false; return OK; } @@ -144,7 +146,13 @@ void NetworkedMultiplayerENet::poll(){ } } -void NetworkedMultiplayerENet::disconnect() { +bool NetworkedMultiplayerENet::is_server() const { + ERR_FAIL_COND_V(!active,false); + + return server; +} + +void NetworkedMultiplayerENet::close_connection() { ERR_FAIL_COND(!active); @@ -258,6 +266,6 @@ NetworkedMultiplayerENet::NetworkedMultiplayerENet(){ NetworkedMultiplayerENet::~NetworkedMultiplayerENet(){ if (active) { - disconnect(); + close_connection(); } } diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h index 20eb53990dee..ec6b084d6602 100644 --- a/modules/enet/networked_multiplayer_enet.h +++ b/modules/enet/networked_multiplayer_enet.h @@ -52,10 +52,12 @@ public: Error create_server(int p_port, int p_max_clients=32, int p_max_channels=1, int p_in_bandwidth=0, int p_out_bandwidth=0); Error create_client(const IP_Address& p_ip,int p_port, int p_max_channels=1, int p_in_bandwidth=0, int p_out_bandwidth=0); - void disconnect(); + void close_connection(); virtual void poll(); + virtual bool is_server() const; + virtual int get_available_packet_count() const; virtual Error get_packet(const uint8_t **r_buffer,int &r_buffer_size) const; ///< buffer is GONE after next get_packet virtual Error put_packet(const uint8_t *p_buffer,int p_buffer_size); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index a53c19d2e76a..0c7d5693342d 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -36,6 +36,7 @@ #include "instance_placeholder.h" VARIANT_ENUM_CAST(Node::PauseMode); +VARIANT_ENUM_CAST(Node::NetworkMode); @@ -77,6 +78,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 +108,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 +437,200 @@ 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::allow_remote_call(const StringName& p_method) { + + data.allowed_remote_calls.insert(p_method); +} + +void Node::disallow_remote_call(const StringName& p_method){ + + data.allowed_remote_calls.erase(p_method); + +} + +void Node::allow_remote_set(const StringName& p_property){ + + data.allowed_remote_set.insert(p_property); + +} +void Node::disallow_remote_set(const StringName& p_property){ + + data.allowed_remote_set.erase(p_property); +} + + +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_propagate_network_owner(p_owner); + } +} + +void Node::remote_call_reliable(const StringName& p_method,VARIANT_ARG_DECLARE) { + + + VARIANT_ARGPTRS; + + int argc=0; + for(int i=0;iget_type()==Variant::NIL) + break; + argc++; + } + + remote_call_reliablep(p_method,argptr,argc); +} + +void Node::remote_call_reliablep(const StringName& p_method,const Variant** p_arg,int p_argcount){ + + ERR_FAIL_COND(!is_inside_tree()); + get_tree()->_remote_call(this,true,false,p_method,p_arg,p_argcount); +} + +void Node::remote_call_unreliable(const StringName& p_method,VARIANT_ARG_DECLARE){ + + VARIANT_ARGPTRS; + + int argc=0; + for(int i=0;iget_type()==Variant::NIL) + break; + argc++; + } + + remote_call_unreliablep(p_method,argptr,argc); + +} + +void Node::remote_call_unreliablep(const StringName& p_method,const Variant** p_arg,int p_argcount){ + + ERR_FAIL_COND(!is_inside_tree()); + + get_tree()->_remote_call(this,false,false,p_method,p_arg,p_argcount); +} + +void Node::remote_set_reliable(const StringName& p_property,const Variant& p_value) { + + + ERR_FAIL_COND(!is_inside_tree()); + const Variant *ptr=&p_value; + + get_tree()->_remote_call(this,true,true,p_property,&ptr,1); +} + +void Node::remote_set_unreliable(const StringName& p_property,const Variant& p_value){ + + ERR_FAIL_COND(!is_inside_tree()); + const Variant *ptr=&p_value; + + get_tree()->_remote_call(this,false,true,p_property,&ptr,1); +} + +Variant Node::_remote_call_reliable_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]; + + remote_call_reliablep(method,&p_args[1],p_argcount-1); + +} + + +Variant Node::_remote_call_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]; + + remote_call_unreliablep(method,&p_args[1],p_argcount-1); + +} + + bool Node::can_process() const { ERR_FAIL_COND_V( !is_inside_tree(), false ); @@ -567,6 +786,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 +1431,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 path; @@ -1221,7 +1446,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,8 +2439,16 @@ 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("allow_remote_call","method"),&Node::allow_remote_call); + ObjectTypeDB::bind_method(_MD("disallow_remote_call","method"),&Node::disallow_remote_call); + + ObjectTypeDB::bind_method(_MD("allow_remote_set","method"),&Node::allow_remote_set); + ObjectTypeDB::bind_method(_MD("disallow_remote_set","method"),&Node::disallow_remote_set); #ifdef TOOLS_ENABLED ObjectTypeDB::bind_method(_MD("_set_import_path","import_path"),&Node::set_import_path); @@ -2222,6 +2457,23 @@ void Node::_bind_methods() { #endif + { + MethodInfo mi; + mi.name="remote_call_reliable"; + mi.arguments.push_back( PropertyInfo( Variant::STRING, "method")); + + + ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"remote_call_reliable",&Node::_remote_call_reliable_bind,mi); + + mi.name="remote_call_unreliable"; + ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"remote_call_unreliable",&Node::_remote_call_unreliable_bind,mi); + + } + + ObjectTypeDB::bind_method(_MD("remote_set_reliable","property","value:Variant"),&Node::remote_set_reliable); + ObjectTypeDB::bind_method(_MD("remote_set_unreliable","property","value:Variant"),&Node::remote_set_unreliable); + + BIND_CONSTANT( NOTIFICATION_ENTER_TREE ); BIND_CONSTANT( NOTIFICATION_EXIT_TREE ); BIND_CONSTANT( NOTIFICATION_MOVED_IN_PARENT ); @@ -2236,6 +2488,8 @@ void Node::_bind_methods() { BIND_CONSTANT( NOTIFICATION_INSTANCED ); BIND_CONSTANT( NOTIFICATION_DRAG_BEGIN ); BIND_CONSTANT( NOTIFICATION_DRAG_END ); + BIND_CONSTANT( NOTIFICATION_PATH_CHANGED); + @@ -2252,6 +2506,7 @@ void Node::_bind_methods() { //ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/input" ), _SCS("set_process_input"),_SCS("is_processing_input" ) ); //ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), _SCS("set_process_unhandled_input"),_SCS("is_processing_unhandled_input" ) ); ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "process/pause_mode",PROPERTY_HINT_ENUM,"Inherit,Stop,Process" ), _SCS("set_pause_mode"),_SCS("get_pause_mode" ) ); + ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "process/network_mode",PROPERTY_HINT_ENUM,"Inherit,Master,Slave" ), _SCS("set_network_mode"),_SCS("get_network_mode" ) ); ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "editor/display_folded",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR ), _SCS("set_display_folded"),_SCS("is_displayed_folded" ) ); BIND_VMETHOD( MethodInfo("_process",PropertyInfo(Variant::REAL,"delta")) ); @@ -2286,11 +2541,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 18e403cd6157..761725286e2e 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -53,6 +53,12 @@ public: PAUSE_MODE_PROCESS }; + enum NetworkMode { + + NETWORK_MODE_INHERIT, + NETWORK_MODE_MASTER, + NETWORK_MODE_SLAVE + }; struct Comparator { @@ -68,6 +74,7 @@ private: GroupData() { persistent=false; } }; + struct Data { String filename; @@ -98,6 +105,13 @@ private: PauseMode pause_mode; Node *pause_owner; + + NetworkMode network_mode; + Node *network_owner; + Set allowed_remote_calls; + Set allowed_remote_set; + + // variables used to properly sort the node when processing, ignored otherwise //should move all the stuff below to bits bool fixed_process; @@ -113,6 +127,8 @@ private: bool display_folded; + mutable NodePath *path_cache; + } data; @@ -134,6 +150,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 +160,9 @@ private: Array _get_children() const; Array _get_groups() const; + Variant _remote_call_reliable_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error); + Variant _remote_call_unreliable_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error); + friend class SceneTree; void _set_tree(SceneTree *p_tree); @@ -186,6 +206,7 @@ public: NOTIFICATION_INSTANCED=20, NOTIFICATION_DRAG_BEGIN=21, NOTIFICATION_DRAG_END=22, + NOTIFICATION_PATH_CHANGED=23, }; /* NODE/TREE */ @@ -331,7 +352,27 @@ 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 allow_remote_call(const StringName& p_method); + void disallow_remote_call(const StringName& p_method); + + void allow_remote_set(const StringName& p_property); + void disallow_remote_set(const StringName& p_property); + + void remote_call_reliable(const StringName& p_method,VARIANT_ARG_DECLARE); + void remote_call_reliablep(const StringName& p_method,const Variant** p_arg,int p_argcount); + + void remote_call_unreliable(const StringName& p_method,VARIANT_ARG_DECLARE); + void remote_call_unreliablep(const StringName& p_method,const Variant** p_arg,int p_argcount); + + void remote_set_reliable(const StringName& p_property,const Variant& p_value); + void remote_set_unreliable(const StringName& p_property,const Variant& p_value); + Node(); ~Node(); diff --git a/scene/main/scene_main_loop.cpp b/scene/main/scene_main_loop.cpp index 77156203b81c..6af2b9b16957 100644 --- a/scene/main/scene_main_loop.cpp +++ b/scene/main/scene_main_loop.cpp @@ -545,6 +545,8 @@ bool SceneTree::idle(float p_time){ idle_process_time=p_time; + _network_poll(); + emit_signal("idle_frame"); _flush_transform_notifications(); @@ -1655,6 +1657,322 @@ Ref SceneTree::create_timer(float p_delay_sec) { return stt; } +void SceneTree::_network_peer_connected(const StringName& 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(const StringName& 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::set_network_peer(const Ref& 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"); + 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"); + } +} + +bool SceneTree::is_network_server() const { + + ERR_FAIL_COND_V(!network_peer.is_valid(),false); + return network_peer->is_server(); + +} + +void SceneTree::_remote_call(Node* p_from, bool p_reliable, 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(); + } + + NodePath from_path = p_from->get_path(); + ERR_FAIL_COND(from_path.is_empty()); + + //create base packet + Array message; + + message.resize(3+p_argcount); //alloc size for args + + //set message type + if (p_set) { + message[0]=NETWORK_COMMAND_REMOTE_SET; + } else { + message[0]=NETWORK_COMMAND_REMOTE_CALL; + } + + //set message name + message[2]=p_name; + + //set message args + for(int i=0;iid=last_send_cache_id++; + + } + + //see if all peers have cached path (is so, call can be fast) + bool has_all_peers=true; + + List peers_to_add; //if one is missing, take note to add it + + for (Set::Element *E=connected_peers.front();E;E=E->next()) { + + Map::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; + break; + } + } + + //those that need to be added, send a message for this + + for (List::Element *E=peers_to_add.front();E;E=E->next()) { + + Array add_path_message; + add_path_message.resize(3); + add_path_message[0]=NETWORK_COMMAND_SIMPLIFY_PATH; + add_path_message[1]=from_path; + add_path_message[2]=psc->id; + + network_peer->set_target_peer(E->get()); //to all of you + network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_ORDERED); + network_peer->put_var(add_path_message); //a message with love + + 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_reliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_ORDERED : NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE); + + if (has_all_peers) { + + //they all have verified paths, so send fast + message[1]=psc->id; + + network_peer->set_target_peer(StringName()); //to all of you + network_peer->put_var(message); //a message with love + } else { + //not all verified path, so send one by one + for (Set::Element *E=connected_peers.front();E;E=E->next()) { + + Map::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 + message[1]=psc->id; + } else { + //this one did not confirm path yet, so use entire path (sorry!) + message[1]=from_path; + } + + network_peer->put_var(message); + } + } +} + + +void SceneTree::_network_process_packet(const StringName& p_from,const Array& p_packet) { + + ERR_FAIL_COND(p_packet.empty()); + + int packet_type = p_packet[0]; + + switch(packet_type) { + + case NETWORK_COMMAND_REMOTE_CALL: + case NETWORK_COMMAND_REMOTE_SET: { + + ERR_FAIL_COND(p_packet.size()<3); + Variant target = p_packet[1]; + Node* node=NULL; + + if (target.get_type()==Variant::NODE_PATH) { + NodePath np = target; + 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 if (target.get_type()==Variant::INT) { + + int id = target; + + Map::Element *E=path_get_cache.find(p_from); + ERR_FAIL_COND(!E); + + Map::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); + } + + + } else { + ERR_FAIL(); + } + + StringName name = p_packet[2]; + + if (packet_type==NETWORK_COMMAND_REMOTE_CALL) { + + int argc = p_packet.size()-3; + Vector args; + Vector argp; + args.resize(argc); + argp.resize(argc); + + for(int i=0;icall(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); + ERR_PRINTS(error); + } + + } else { + + + ERR_FAIL_COND(p_packet.size()!=4); + Variant value = p_packet[3]; + 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.size()!=3); + NodePath path = p_packet[1]; + int id = p_packet[2]; + + 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; + + network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_ORDERED); + network_peer->set_target_peer(p_from); + + Array message; + message.resize(2); + message[0]=NETWORK_COMMAND_CONFIRM_PATH; + message[1]=path; + + network_peer->put_var(message); + } break; + case NETWORK_COMMAND_CONFIRM_PATH: { + ERR_FAIL_COND(p_packet.size()!=2); + NodePath path = p_packet[1]; + + PathSentCache *psc = path_send_cache.getptr(path); + ERR_FAIL_COND(!psc); + + Map::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()) + return; + + network_peer->poll(); + + while(network_peer->get_available_packet_count()) { + + StringName sender = network_peer->get_packet_peer(); + Variant packet; + Error err = network_peer->get_var(packet); + if (err!=OK) { + ERR_PRINT("Error getting packet!"); + } + if (packet.get_type()!=Variant::ARRAY) { + + ERR_PRINT("Error getting packet! (not an array)"); + } + + _network_process_packet(sender,packet); + } + + +} + + void SceneTree::_bind_methods() { @@ -1722,6 +2040,12 @@ 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","is_network_server"),&SceneTree::is_network_server); + ObjectTypeDB::bind_method(_MD("_network_peer_connected"),&SceneTree::_network_peer_connected); + ObjectTypeDB::bind_method(_MD("_network_peer_disconnected"),&SceneTree::_network_peer_disconnected); + ADD_SIGNAL( MethodInfo("tree_changed") ); ADD_SIGNAL( MethodInfo("node_removed",PropertyInfo( Variant::OBJECT, "node") ) ); ADD_SIGNAL( MethodInfo("screen_resized") ); @@ -1731,6 +2055,8 @@ 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::STRING,"id"))); + ADD_SIGNAL( MethodInfo("network_peer_disconnected",PropertyInfo(Variant::STRING,"id"))); BIND_CONSTANT( GROUP_CALL_DEFAULT ); BIND_CONSTANT( GROUP_CALL_REVERSE ); @@ -1831,6 +2157,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 6129a124467d..f67dae01befa 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 */ @@ -173,9 +176,53 @@ private: List > timers; + + ///network/// + + enum NetworkCommands { + NETWORK_COMMAND_REMOTE_CALL, + NETWORK_COMMAND_REMOTE_SET, + NETWORK_COMMAND_SIMPLIFY_PATH, + NETWORK_COMMAND_CONFIRM_PATH, + }; + + Ref network_peer; + + Set connected_peers; + void _network_peer_connected(const StringName& p_id); + void _network_peer_disconnected(const StringName& p_id); + + //path sent caches + struct PathSentCache { + Map confirmed_peers; + int id; + }; + + HashMap path_send_cache; + int last_send_cache_id; + + //path get caches + struct PathGetCache { + struct NodeInfo { + NodePath path; + ObjectID instance; + }; + + Map nodes; + }; + + Map path_get_cache; + + void _network_process_packet(const StringName &p_from, const Array& p_packet); + void _network_poll(); + static SceneTree *singleton; friend class Node; + + + void _remote_call(Node* p_from,bool p_reliable,bool p_set,const StringName& p_name,const Variant** p_arg,int p_argcount); + void tree_changed(); void node_removed(Node *p_node); @@ -251,6 +298,7 @@ friend class Viewport; #endif protected: + void _notification(int p_notification); static void _bind_methods(); @@ -366,6 +414,11 @@ public: void drop_files(const Vector& p_files,int p_from_screen=0); + //network API + + void set_network_peer(const Ref& p_network_peer); + bool is_network_server() const; + SceneTree(); ~SceneTree();