Added high level networked multiplayer to Godot.
It's complete, but absolutely and completely untested, undocumented and NSFW. Have fun :-)
This commit is contained in:
parent
01bdfe1ff6
commit
3db36684b1
7 changed files with 698 additions and 5 deletions
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<data.children.size();i++) {
|
||||
|
||||
data.children[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;i<VARIANT_ARG_MAX;i++) {
|
||||
if (argptr[i]->get_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;i<VARIANT_ARG_MAX;i++) {
|
||||
if (argptr[i]->get_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<StringName> 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() {
|
||||
|
|
|
@ -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<StringName> allowed_remote_calls;
|
||||
Set<StringName> 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();
|
||||
|
|
|
@ -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<SceneTreeTimer> 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<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");
|
||||
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;i<p_argcount;i++) {
|
||||
message[3+i]=*p_arg[i];
|
||||
}
|
||||
|
||||
//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++;
|
||||
|
||||
}
|
||||
|
||||
//see if all peers have cached path (is so, call can be fast)
|
||||
bool has_all_peers=true;
|
||||
|
||||
List<StringName> peers_to_add; //if one is missing, take note to add it
|
||||
|
||||
for (Set<StringName>::Element *E=connected_peers.front();E;E=E->next()) {
|
||||
|
||||
Map<StringName,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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//those that need to be added, send a message for this
|
||||
|
||||
for (List<StringName>::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<StringName>::Element *E=connected_peers.front();E;E=E->next()) {
|
||||
|
||||
Map<StringName,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
|
||||
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<StringName,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);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
StringName name = p_packet[2];
|
||||
|
||||
if (packet_type==NETWORK_COMMAND_REMOTE_CALL) {
|
||||
|
||||
int argc = p_packet.size()-3;
|
||||
Vector<Variant> args;
|
||||
Vector<const Variant*> argp;
|
||||
args.resize(argc);
|
||||
argp.resize(argc);
|
||||
|
||||
for(int i=0;i<argc;i++) {
|
||||
args[i]=p_packet[3+i];
|
||||
argp[i]=&args[i];
|
||||
}
|
||||
|
||||
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);
|
||||
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<StringName,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())
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
||||
*/
|
||||
|
@ -173,9 +176,53 @@ private:
|
|||
|
||||
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<StringName> connected_peers;
|
||||
void _network_peer_connected(const StringName& p_id);
|
||||
void _network_peer_disconnected(const StringName& p_id);
|
||||
|
||||
//path sent caches
|
||||
struct PathSentCache {
|
||||
Map<StringName,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<StringName,PathGetCache> 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<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;
|
||||
|
||||
SceneTree();
|
||||
~SceneTree();
|
||||
|
||||
|
|
Loading…
Reference in a new issue