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:
parent
df391cc5be
commit
5081ced57f
|
@ -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")));
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue