Use MultiplayerAPI class for high level networking

Remove networking related logic from Node and SceneTree.
SceneTree now simply relay all networking related stuff to
MultiplayerAPI for compatibility
This commit is contained in:
Fabio Alessandrelli 2018-03-03 18:30:11 +01:00
parent df391cc5be
commit 5081ced57f
4 changed files with 89 additions and 676 deletions

View file

@ -477,7 +477,7 @@ bool Node::is_network_master() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
return get_tree()->get_network_unique_id() == data.network_master;
return get_multiplayer_api()->get_network_unique_id() == data.network_master;
}
/***** RPC CONFIG ********/
@ -667,200 +667,16 @@ Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Va
}
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());
get_multiplayer_api()->rpcp(this, p_peer_id, p_unreliable, p_method, p_arg, p_argcount);
}
bool skip_rpc = false;
bool call_local_native = false;
bool call_local_script = 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
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_native = true;
} break;
case RPC_MODE_MASTER: {
call_local_native = is_network_master();
if (call_local_native) {
skip_rpc = true; //no other master so..
}
} break;
case RPC_MODE_SLAVE: {
call_local_native = !is_network_master();
} break;
}
}
if (call_local_native) {
// done below
} 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_script = true;
} break;
case ScriptInstance::RPC_MODE_MASTER: {
call_local_script = is_network_master();
if (call_local_script) {
skip_rpc = true; //no other master so..
}
} break;
case ScriptInstance::RPC_MODE_SLAVE: {
call_local_script = !is_network_master();
} break;
}
}
}
if (!skip_rpc) {
get_tree()->_rpc(this, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
}
if (call_local_native) {
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;
}
}
if (call_local_script) {
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;
}
}
void Node::rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) {
ERR_FAIL_COND(!is_inside_tree());
get_multiplayer_api()->rsetp(this, p_peer_id, p_unreliable, p_property, p_value);
}
/******** 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);
@ -882,6 +698,30 @@ void Node::rset_unreliable_id(int p_peer_id, const StringName &p_property, const
}
//////////// end of rpc
Ref<MultiplayerAPI> Node::get_multiplayer_api() const {
if (multiplayer_api.is_valid())
return multiplayer_api;
if (!is_inside_tree())
return Ref<MultiplayerAPI>();
return get_tree()->get_multiplayer_api();
}
Ref<MultiplayerAPI> Node::get_custom_multiplayer_api() const {
return multiplayer_api;
}
void Node::set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) {
multiplayer_api = p_multiplayer_api;
}
const Map<StringName, Node::RPCMode>::Element *Node::get_node_rpc_mode(const StringName &p_method) {
return data.rpc_methods.find(p_method);
}
const Map<StringName, Node::RPCMode>::Element *Node::get_node_rset_mode(const StringName &p_property) {
return data.rpc_properties.find(p_property);
}
bool Node::can_call_rpc(const StringName &p_method, int p_from) const {
@ -2886,6 +2726,9 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_network_master"), &Node::is_network_master);
ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &Node::get_multiplayer_api);
ClassDB::bind_method(D_METHOD("get_custom_multiplayer_api"), &Node::get_custom_multiplayer_api);
ClassDB::bind_method(D_METHOD("set_custom_multiplayer_api", "api"), &Node::set_custom_multiplayer_api);
ClassDB::bind_method(D_METHOD("rpc_config", "method", "mode"), &Node::rpc_config);
ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config);
@ -2967,6 +2810,8 @@ void Node::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name");
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer_api");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "custom_multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer_api", "get_custom_multiplayer_api");
BIND_VMETHOD(MethodInfo("_process", PropertyInfo(Variant::REAL, "delta")));
BIND_VMETHOD(MethodInfo("_physics_process", PropertyInfo(Variant::REAL, "delta")));

View file

@ -151,6 +151,8 @@ private:
NAME_CASING_SNAKE_CASE
};
Ref<MultiplayerAPI> multiplayer_api;
void _print_tree(const Node *p_node);
Node *_get_node(const NodePath &p_path) const;
@ -403,15 +405,20 @@ public:
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 rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
void rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
Ref<MultiplayerAPI> get_multiplayer_api() const;
Ref<MultiplayerAPI> get_custom_multiplayer_api() const;
void set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api);
const Map<StringName, RPCMode>::Element *get_node_rpc_mode(const StringName &p_method);
const Map<StringName, RPCMode>::Element *get_node_rset_mode(const StringName &p_property);
bool can_call_rpc(const StringName &p_method, int p_from) const;
bool can_call_rset(const StringName &p_property, int p_from) const;

View file

@ -484,7 +484,7 @@ bool SceneTree::idle(float p_time) {
idle_process_time = p_time;
_network_poll();
multiplayer_api->poll();
emit_signal("idle_frame");
@ -1631,16 +1631,11 @@ Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_pa
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);
}
@ -1659,471 +1654,70 @@ void SceneTree::_server_disconnected() {
emit_signal("server_disconnected");
}
Ref<MultiplayerAPI> SceneTree::get_multiplayer_api() const {
return multiplayer_api;
}
void SceneTree::set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) {
ERR_FAIL_COND(!p_multiplayer_api.is_valid());
if (multiplayer_api.is_valid()) {
multiplayer_api->disconnect("network_peer_connected", this, "_network_peer_connected");
multiplayer_api->disconnect("network_peer_disconnected", this, "_network_peer_disconnected");
multiplayer_api->disconnect("connected_to_server", this, "_connected_to_server");
multiplayer_api->disconnect("connection_failed", this, "_connection_failed");
multiplayer_api->disconnect("server_disconnected", this, "_server_disconnected");
}
multiplayer_api = p_multiplayer_api;
multiplayer_api->set_root_node(root);
multiplayer_api->connect("network_peer_connected", this, "_network_peer_connected");
multiplayer_api->connect("network_peer_disconnected", this, "_network_peer_disconnected");
multiplayer_api->connect("connected_to_server", this, "_connected_to_server");
multiplayer_api->connect("connection_failed", this, "_connection_failed");
multiplayer_api->connect("server_disconnected", this, "_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");
}
multiplayer_api->set_network_peer(p_network_peer);
}
Ref<NetworkedMultiplayerPeer> SceneTree::get_network_peer() const {
return network_peer;
return multiplayer_api->get_network_peer();
}
bool SceneTree::is_network_server() const {
ERR_FAIL_COND_V(!network_peer.is_valid(), false);
return network_peer->is_server();
return multiplayer_api->is_network_server();
}
bool SceneTree::has_network_peer() const {
return network_peer.is_valid();
return multiplayer_api->has_network_peer();
}
int SceneTree::get_network_unique_id() const {
ERR_FAIL_COND_V(!network_peer.is_valid(), 0);
return network_peer->get_unique_id();
return multiplayer_api->get_network_unique_id();
}
Vector<int> SceneTree::get_network_connected_peers() const {
ERR_FAIL_COND_V(!network_peer.is_valid(), Vector<int>());
Vector<int> ret;
for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
ret.push_back(E->get());
}
return ret;
return multiplayer_api->get_network_connected_peers();
}
int SceneTree::get_rpc_sender_id() const {
return rpc_sender_id;
return multiplayer_api->get_rpc_sender_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);
multiplayer_api->set_refuse_new_network_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 hardcode 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) {
//use full path (not cached yet)
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 {
//use cached path
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, p_from))
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, (const Variant **)argp.ptr(), argc, ce);
if (ce.error != Variant::CallError::CALL_OK) {
String error = Variant::get_call_error_text(node, name, (const Variant **)argp.ptr(), argc, ce);
error = "RPC - " + error;
ERR_PRINTS(error);
}
} else {
if (!node->can_call_rset(name, p_from))
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_class();
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!");
}
rpc_sender_id = sender;
_network_process_packet(sender, packet, len);
rpc_sender_id = 0;
if (!network_peer.is_valid()) {
break; //it's also possible that a packet or RPC caused a disconnection, so also check here
}
}
return multiplayer_api->is_refusing_new_network_connections();
}
void SceneTree::_bind_methods() {
@ -2194,6 +1788,8 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene);
ClassDB::bind_method(D_METHOD("set_multiplayer_api", "multiplayer_api"), &SceneTree::set_multiplayer_api);
ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &SceneTree::get_multiplayer_api);
ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &SceneTree::set_network_peer);
ClassDB::bind_method(D_METHOD("get_network_peer"), &SceneTree::get_network_peer);
ClassDB::bind_method(D_METHOD("is_network_server"), &SceneTree::is_network_server);
@ -2223,6 +1819,7 @@ void SceneTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_current_scene", "get_current_scene");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "", "get_root");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_multiplayer_api", "get_multiplayer_api");
ADD_SIGNAL(MethodInfo("tree_changed"));
ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node")));
@ -2318,7 +1915,6 @@ SceneTree::SceneTree() {
call_lock = 0;
root_lock = 0;
node_count = 0;
rpc_sender_id = 0;
//create with mainloop
@ -2327,6 +1923,9 @@ SceneTree::SceneTree() {
if (!root->get_world().is_valid())
root->set_world(Ref<World>(memnew(World)));
// Initialize network state
set_multiplayer_api(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
//root->set_world_2d( Ref<World2D>( memnew( World2D )));
root->set_as_audio_listener(true);
root->set_as_audio_listener_2d(true);
@ -2421,8 +2020,6 @@ SceneTree::SceneTree() {
live_edit_root = NodePath("/root");
last_send_cache_id = 1;
#endif
use_font_oversampling = false;

View file

@ -31,7 +31,7 @@
#ifndef SCENE_MAIN_LOOP_H
#define SCENE_MAIN_LOOP_H
#include "io/networked_multiplayer_peer.h"
#include "io/multiplayer_api.h"
#include "os/main_loop.h"
#include "os/thread_safe.h"
#include "scene/resources/mesh.h"
@ -185,16 +185,8 @@ private:
///network///
enum NetworkCommands {
NETWORK_COMMAND_REMOTE_CALL,
NETWORK_COMMAND_REMOTE_SET,
NETWORK_COMMAND_SIMPLIFY_PATH,
NETWORK_COMMAND_CONFIRM_PATH,
};
Ref<MultiplayerAPI> multiplayer_api;
Ref<NetworkedMultiplayerPeer> network_peer;
Set<int> connected_peers;
void _network_peer_connected(int p_id);
void _network_peer_disconnected(int p_id);
@ -202,39 +194,9 @@ private:
void _connection_failed();
void _server_disconnected();
int rpc_sender_id;
//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_added(Node *p_node);
void node_removed(Node *p_node);
@ -450,6 +412,8 @@ public:
//network API
Ref<MultiplayerAPI> get_multiplayer_api() const;
void set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api);
void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer);
Ref<NetworkedMultiplayerPeer> get_network_peer() const;
bool is_network_server() const;