diff options
Diffstat (limited to 'core/io')
-rw-r--r-- | core/io/file_access_network.cpp | 4 | ||||
-rw-r--r-- | core/io/file_access_pack.cpp | 6 | ||||
-rw-r--r-- | core/io/http_client.cpp | 24 | ||||
-rw-r--r-- | core/io/image_loader.cpp | 2 | ||||
-rw-r--r-- | core/io/ip_address.cpp | 2 | ||||
-rw-r--r-- | core/io/marshalls.cpp | 6 | ||||
-rw-r--r-- | core/io/multiplayer_api.cpp | 722 | ||||
-rw-r--r-- | core/io/multiplayer_api.h | 87 | ||||
-rw-r--r-- | core/io/pck_packer.cpp | 8 | ||||
-rw-r--r-- | core/io/resource_format_binary.cpp | 10 | ||||
-rw-r--r-- | core/io/resource_saver.cpp | 2 | ||||
-rw-r--r-- | core/io/stream_peer.cpp | 2 | ||||
-rw-r--r-- | core/io/stream_peer.h | 2 | ||||
-rw-r--r-- | core/io/stream_peer_ssl.cpp | 32 | ||||
-rw-r--r-- | core/io/stream_peer_ssl.h | 2 | ||||
-rw-r--r-- | core/io/stream_peer_tcp.cpp | 1 | ||||
-rw-r--r-- | core/io/stream_peer_tcp.h | 2 |
17 files changed, 892 insertions, 22 deletions
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 7b2bccdfec..21e3a4172b 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -83,7 +83,7 @@ int64_t FileAccessNetworkClient::get_64() { void FileAccessNetworkClient::_thread_func() { - client->set_nodelay(true); + client->set_no_delay(true); while (!quit) { DEBUG_PRINT("SEM WAIT - " + itos(sem->get())); @@ -418,8 +418,6 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { if (page != last_page) { buffer_mutex->lock(); if (pages[page].buffer.empty()) { - //fuck - waiting_on_page = page; for (int j = 0; j < read_ahead; j++) { diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 1a16d0f61c..efb4c7a073 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -88,7 +88,11 @@ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t o } } } - cd->files.insert(path.get_file()); + String filename = path.get_file(); + // Don't add as a file if the path points to a directoryy + if (!filename.empty()) { + cd->files.insert(filename); + } } } diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index a9eb9466b7..9e301ccac5 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -298,7 +298,7 @@ Error HTTPClient::poll() { case StreamPeerTCP::STATUS_CONNECTED: { if (ssl) { Ref<StreamPeerSSL> ssl = StreamPeerSSL::create(); - Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, ssl_verify_host ? conn_host : String()); + Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); if (err != OK) { close(); status = STATUS_SSL_HANDSHAKE_ERROR; @@ -618,7 +618,27 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) { String query = ""; Array keys = p_dict.keys(); for (int i = 0; i < keys.size(); ++i) { - query += "&" + String(keys[i]).http_escape() + "=" + String(p_dict[keys[i]]).http_escape(); + String encoded_key = String(keys[i]).http_escape(); + Variant value = p_dict[keys[i]]; + switch (value.get_type()) { + case Variant::ARRAY: { + // Repeat the key with every values + Array values = value; + for (int j = 0; j < values.size(); ++j) { + query += "&" + encoded_key + "=" + String(values[j]).http_escape(); + } + break; + } + case Variant::NIL: { + // Add the key with no value + query += "&" + encoded_key; + break; + } + default: { + // Add the key-value pair + query += "&" + encoded_key + "=" + String(value).http_escape(); + } + } } query.erase(0, 1); return query; diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 999c9a8ca2..8ebd9d6cd9 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -37,7 +37,7 @@ bool ImageFormatLoader::recognize(const String &p_extension) const { get_recognized_extensions(&extensions); for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - if (E->get().nocasecmp_to(p_extension.get_extension()) == 0) + if (E->get().nocasecmp_to(p_extension) == 0) return true; } diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index 7261363ad6..6d979d10eb 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -211,7 +211,7 @@ IP_Address::IP_Address(const String &p_string) { clear(); if (p_string == "*") { - // Wildcard (not a vaild IP) + // Wildcard (not a valid IP) wildcard = true; } else if (p_string.find(":") >= 0) { diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 9e21287780..0a3a6c1ba1 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -813,7 +813,7 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) { while (r_len % 4) { r_len++; //pad if (buf) { - buf++; + *(buf++) = 0; } } } @@ -1211,7 +1211,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += len; if (buf) buf += len; - encode_variant(d[E->get()], buf, len, p_object_as_id); + Variant *v = d.getptr(E->get()); + ERR_FAIL_COND_V(!v, ERR_BUG); + encode_variant(*v, buf, len, p_object_as_id); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; if (buf) diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp new file mode 100644 index 0000000000..cbe7f87d92 --- /dev/null +++ b/core/io/multiplayer_api.cpp @@ -0,0 +1,722 @@ +#include "core/io/multiplayer_api.h" +#include "core/io/marshalls.h" +#include "scene/main/node.h" + +void MultiplayerAPI::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; + _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 + } + } +} + +void MultiplayerAPI::clear() { + connected_peers.clear(); + path_get_cache.clear(); + path_send_cache.clear(); + last_send_cache_id = 1; +} + +void MultiplayerAPI::set_root_node(Node *p_node) { + root_node = p_node; +} + +void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) { + + if (network_peer.is_valid()) { + network_peer->disconnect("peer_connected", this, "add_peer"); + network_peer->disconnect("peer_disconnected", this, "del_peer"); + 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"); + clear(); + } + + network_peer = p_peer; + + ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected."); + ERR_FAIL_COND(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED); + + if (network_peer.is_valid()) { + network_peer->connect("peer_connected", this, "add_peer"); + network_peer->connect("peer_disconnected", this, "del_peer"); + 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"); + } +} + +Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const { + return network_peer; +} + +void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { + + ERR_FAIL_COND(root_node == NULL); + ERR_FAIL_COND(p_packet_len < 5); + + uint8_t packet_type = p_packet[0]; + + switch (packet_type) { + + case NETWORK_COMMAND_SIMPLIFY_PATH: { + + _process_simplify_path(p_from, p_packet, p_packet_len); + } break; + + case NETWORK_COMMAND_CONFIRM_PATH: { + + _process_confirm_path(p_from, p_packet, p_packet_len); + } break; + + case NETWORK_COMMAND_REMOTE_CALL: + case NETWORK_COMMAND_REMOTE_SET: { + + ERR_FAIL_COND(p_packet_len < 6); + + Node *node = _process_get_node(p_from, p_packet, p_packet_len); + + ERR_FAIL_COND(node == NULL); + + //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) { + + _process_rpc(node, name, p_from, p_packet, p_packet_len, len_end + 1); + + } else { + + _process_rset(node, name, p_from, p_packet, p_packet_len, len_end + 1); + } + + } break; + } +} + +Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len) { + + 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_V(ofs >= p_packet_len, NULL); + + String paths; + paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); + + NodePath np = paths; + + node = root_node->get_node(np); + + if (!node) + ERR_PRINTS("Failed to get path from RPC: " + String(np)); + } else { + //use cached path + int id = target; + + Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); + ERR_FAIL_COND_V(!E, NULL); + + Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id); + ERR_FAIL_COND_V(!F, NULL); + + PathGetCache::NodeInfo *ni = &F->get(); + //do proper caching later + + node = root_node->get_node(ni->path); + if (!node) + ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path)); + } + return node; +} + +void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { + if (!p_node->can_call_rpc(p_name, p_from)) + return; + + ERR_FAIL_COND(p_offset >= p_packet_len); + + int argc = p_packet[p_offset]; + Vector<Variant> args; + Vector<const Variant *> argp; + args.resize(argc); + argp.resize(argc); + + p_offset++; + + for (int i = 0; i < argc; i++) { + + ERR_FAIL_COND(p_offset >= p_packet_len); + int vlen; + Error err = decode_variant(args[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + ERR_FAIL_COND(err != OK); + //args[i]=p_packet[3+i]; + argp[i] = &args[i]; + p_offset += vlen; + } + + Variant::CallError ce; + + p_node->call(p_name, (const Variant **)argp.ptr(), argc, ce); + if (ce.error != Variant::CallError::CALL_OK) { + String error = Variant::get_call_error_text(p_node, p_name, (const Variant **)argp.ptr(), argc, ce); + error = "RPC - " + error; + ERR_PRINTS(error); + } +} + +void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { + + if (!p_node->can_call_rset(p_name, p_from)) + return; + + ERR_FAIL_COND(p_offset >= p_packet_len); + + Variant value; + decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset); + + bool valid; + + p_node->set(p_name, value, &valid); + if (!valid) { + String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class(); + ERR_PRINTS(error); + } +} + +void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + + 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()); +} + +void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + + 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; +} + +bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target) { + 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_target < 0 && E->get() == -p_target) + continue; //continue, excluded + + if (p_target > 0 && E->get() != p_target) + 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(p_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 + } + + return has_all_peers; +} + +void MultiplayerAPI::_send_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 == network_peer->get_unique_id()) { + ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id())); + } else { + ERR_EXPLAIN("Attempt to remote call unexisting ID: " + itos(p_to)); + } + + ERR_FAIL(); + } + + NodePath from_path = (root_node->get_path()).rel_path_to(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 = _send_confirm_path(from_path, psc, p_to); + + //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 MultiplayerAPI::add_peer(int p_id) { + connected_peers.insert(p_id); + path_get_cache.insert(p_id, PathGetCache()); + emit_signal("network_peer_connected", p_id); +} + +void MultiplayerAPI::del_peer(int p_id) { + connected_peers.erase(p_id); + path_get_cache.erase(p_id); //I no longer need your cache, sorry + emit_signal("network_peer_disconnected", p_id); +} + +void MultiplayerAPI::connected_to_server() { + + emit_signal("connected_to_server"); +} + +void MultiplayerAPI::connection_failed() { + + emit_signal("connection_failed"); +} + +void MultiplayerAPI::server_disconnected() { + + emit_signal("server_disconnected"); +} + +bool _should_call_native(Node::RPCMode mode, bool is_master, bool &r_skip_rpc) { + + switch (mode) { + + case Node::RPC_MODE_DISABLED: { + //do nothing + } break; + case Node::RPC_MODE_REMOTE: { + //do nothing also, no need to call local + } break; + case Node::RPC_MODE_SYNC: { + //call it, sync always results in call + return true; + } break; + case Node::RPC_MODE_MASTER: { + if (is_master) + r_skip_rpc = true; //no other master so.. + return is_master; + } break; + case Node::RPC_MODE_SLAVE: { + return !is_master; + } break; + } + return false; +} + +bool _should_call_script(ScriptInstance::RPCMode mode, bool is_master, bool &r_skip_rpc) { + switch (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 + return true; + } break; + case ScriptInstance::RPC_MODE_MASTER: { + if (is_master) + r_skip_rpc = true; //no other master so.. + return is_master; + } break; + case ScriptInstance::RPC_MODE_SLAVE: { + return !is_master; + } break; + } + return false; +} + +void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { + + ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_FAIL_COND(!network_peer.is_valid()); + + int node_id = network_peer->get_unique_id(); + bool skip_rpc = false; + bool call_local_native = false; + bool call_local_script = false; + bool is_master = p_node->is_network_master(); + + if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { + //check that send mode can use local call + + const Map<StringName, Node::RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method); + if (E) { + call_local_native = _should_call_native(E->get(), is_master, skip_rpc); + } + + if (call_local_native) { + // done below + } else if (p_node->get_script_instance()) { + //attempt with script + ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); + call_local_script = _should_call_script(rpc_mode, is_master, skip_rpc); + } + } + + if (!skip_rpc) { + _send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount); + } + + if (call_local_native) { + Variant::CallError ce; + p_node->call(p_method, p_arg, p_argcount, ce); + if (ce.error != Variant::CallError::CALL_OK) { + String error = Variant::get_call_error_text(p_node, 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; + p_node->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(p_node, p_method, p_arg, p_argcount, ce); + error = "rpc() aborted in script local call: - " + error; + ERR_PRINTS(error); + return; + } + } +} + +void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { + + ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_FAIL_COND(!network_peer.is_valid()); + + int node_id = network_peer->get_unique_id(); + bool is_master = p_node->is_network_master(); + bool skip_rset = false; + + if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { + //check that send mode can use local call + + bool set_local = false; + + const Map<StringName, Node::RPCMode>::Element *E = p_node->get_node_rset_mode(p_property); + if (E) { + + set_local = _should_call_native(E->get(), is_master, skip_rset); + } + + if (set_local) { + bool valid; + p_node->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 (p_node->get_script_instance()) { + //attempt with script + ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); + + set_local = _should_call_script(rpc_mode, is_master, skip_rset); + + if (set_local) { + + bool valid = p_node->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; + + _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1); +} + +int MultiplayerAPI::get_network_unique_id() const { + + ERR_FAIL_COND_V(!network_peer.is_valid(), 0); + return network_peer->get_unique_id(); +} + +bool MultiplayerAPI::is_network_server() const { + + ERR_FAIL_COND_V(!network_peer.is_valid(), false); + return network_peer->is_server(); +} + +void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { + + ERR_FAIL_COND(!network_peer.is_valid()); + network_peer->set_refuse_new_connections(p_refuse); +} + +bool MultiplayerAPI::is_refusing_new_network_connections() const { + + ERR_FAIL_COND_V(!network_peer.is_valid(), false); + return network_peer->is_refusing_new_connections(); +} + +Vector<int> MultiplayerAPI::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; +} + +void MultiplayerAPI::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); + ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); + ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); + ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); + ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server); + ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id); + ClassDB::bind_method(D_METHOD("add_peer", "id"), &MultiplayerAPI::add_peer); + ClassDB::bind_method(D_METHOD("del_peer", "id"), &MultiplayerAPI::del_peer); + ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer); + ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll); + ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear); + + ClassDB::bind_method(D_METHOD("connected_to_server"), &MultiplayerAPI::connected_to_server); + ClassDB::bind_method(D_METHOD("connection_failed"), &MultiplayerAPI::connection_failed); + ClassDB::bind_method(D_METHOD("server_disconnected"), &MultiplayerAPI::server_disconnected); + ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers); + ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections); + ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer"); + + ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("connected_to_server")); + ADD_SIGNAL(MethodInfo("connection_failed")); + ADD_SIGNAL(MethodInfo("server_disconnected")); +} + +MultiplayerAPI::MultiplayerAPI() { + clear(); +} + +MultiplayerAPI::~MultiplayerAPI() { + clear(); +} diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h new file mode 100644 index 0000000000..e7c6ffbea6 --- /dev/null +++ b/core/io/multiplayer_api.h @@ -0,0 +1,87 @@ +#ifndef MULTIPLAYER_PROTOCOL_H +#define MULTIPLAYER_PROTOCOL_H + +#include "core/io/networked_multiplayer_peer.h" +#include "core/reference.h" + +class MultiplayerAPI : public Reference { + + GDCLASS(MultiplayerAPI, Reference); + +private: + //path sent caches + struct PathSentCache { + Map<int, bool> confirmed_peers; + int id; + }; + + //path get caches + struct PathGetCache { + struct NodeInfo { + NodePath path; + ObjectID instance; + }; + + Map<int, NodeInfo> nodes; + }; + + Ref<NetworkedMultiplayerPeer> network_peer; + int rpc_sender_id; + Set<int> connected_peers; + HashMap<NodePath, PathSentCache> path_send_cache; + Map<int, PathGetCache> path_get_cache; + int last_send_cache_id; + Vector<uint8_t> packet_cache; + Node *root_node; + +protected: + static void _bind_methods(); + + void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len); + void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len); + void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len); + Node *_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len); + void _process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); + void _process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); + + void _send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount); + bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_from); + +public: + enum NetworkCommands { + NETWORK_COMMAND_REMOTE_CALL, + NETWORK_COMMAND_REMOTE_SET, + NETWORK_COMMAND_SIMPLIFY_PATH, + NETWORK_COMMAND_CONFIRM_PATH, + }; + + void poll(); + void clear(); + void set_root_node(Node *p_node); + void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer); + Ref<NetworkedMultiplayerPeer> get_network_peer() const; + + // Called by Node.rpc + void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); + // Called by Node.rset + void rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value); + + void add_peer(int p_id); + void del_peer(int p_id); + void connected_to_server(); + void connection_failed(); + void server_disconnected(); + + bool has_network_peer() const { return network_peer.is_valid(); } + Vector<int> get_network_connected_peers() const; + int get_rpc_sender_id() const { return rpc_sender_id; } + int get_network_unique_id() const; + bool is_network_server() const; + void set_refuse_new_network_connections(bool p_refuse); + bool is_refusing_new_network_connections() const; + + MultiplayerAPI(); + ~MultiplayerAPI(); +}; + +#endif // MULTIPLAYER_PROTOCOL_H diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 596060221e..b6377662de 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "pck_packer.h" - #include "core/os/file_access.h" +#include "version.h" static uint64_t _align(uint64_t p_n, int p_alignment) { @@ -70,9 +70,9 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment) { alignment = p_alignment; file->store_32(0x43504447); // MAGIC - file->store_32(0); // # version - file->store_32(0); // # major - file->store_32(0); // # minor + file->store_32(1); // # version + file->store_32(VERSION_MAJOR); // # major + file->store_32(VERSION_MINOR); // # minor file->store_32(0); // # revision for (int i = 0; i < 16; i++) { diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 15c4835dc6..0c626c197b 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -1124,7 +1124,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); da->remove(p_path + ".depren"); memdelete(da); - //fuck it, use the old approach; + //use the old approach WARN_PRINT(("This file is old, so it can't refactor dependencies, opening and resaving: " + p_path).utf8().get_data()); @@ -1162,9 +1162,11 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); } - fw->store_32(VERSION_MAJOR); //current version - fw->store_32(VERSION_MINOR); - fw->store_32(FORMAT_VERSION); + // Since we're not actually converting the file contents, leave the version + // numbers in the file untouched. + fw->store_32(ver_major); + fw->store_32(ver_minor); + fw->store_32(ver_format); save_ustring(fw, get_ustring(f)); //type diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 609dd7e93c..3dcd94880a 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -56,7 +56,7 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - if (E->get().nocasecmp_to(extension.get_extension()) == 0) + if (E->get().nocasecmp_to(extension) == 0) recognized = true; } diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index f6c4948fc3..927b9f6366 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -310,7 +310,7 @@ float StreamPeer::get_float() { return decode_float(buf); } -float StreamPeer::get_double() { +double StreamPeer::get_double() { uint8_t buf[8]; get_data(buf, 8); diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index ff9ae788ec..605b0a7980 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -83,7 +83,7 @@ public: uint64_t get_u64(); int64_t get_64(); float get_float(); - float get_double(); + double get_double(); String get_string(int p_bytes); String get_utf8_string(int p_bytes); Variant get_var(); diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index 633b353102..012ba78c6d 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "stream_peer_ssl.h" +#include "os/file_access.h" +#include "project_settings.h" StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL; @@ -50,8 +52,38 @@ bool StreamPeerSSL::is_available() { return available; } +PoolByteArray StreamPeerSSL::get_project_cert_array() { + + PoolByteArray out; + String certs_path = GLOBAL_DEF("network/ssl/certificates", ""); + ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); + + if (certs_path != "") { + + FileAccess *f = FileAccess::open(certs_path, FileAccess::READ); + if (f) { + int flen = f->get_len(); + out.resize(flen + 1); + { + PoolByteArray::Write w = out.write(); + f->get_buffer(w.ptr(), flen); + w[flen] = 0; //end f string + } + + memdelete(f); + +#ifdef DEBUG_ENABLED + print_line("Loaded certs from '" + certs_path); +#endif + } + } + + return out; +} + void StreamPeerSSL::_bind_methods() { + ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); ClassDB::bind_method(D_METHOD("accept_stream", "stream"), &StreamPeerSSL::accept_stream); ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status); diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index e4d14ebdfd..77301a7c87 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -57,6 +57,7 @@ public: STATUS_ERROR_HOSTNAME_MISMATCH }; + virtual void poll() = 0; virtual Error accept_stream(Ref<StreamPeer> p_base) = 0; virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String()) = 0; virtual Status get_status() const = 0; @@ -65,6 +66,7 @@ public: static StreamPeerSSL *create(); + static PoolByteArray get_project_cert_array(); static void load_certs_from_memory(const PoolByteArray &p_memory); static bool is_available(); diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index 9cfa810034..5d008904ff 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -55,6 +55,7 @@ void StreamPeerTCP::_bind_methods() { ClassDB::bind_method(D_METHOD("get_connected_host"), &StreamPeerTCP::get_connected_host); ClassDB::bind_method(D_METHOD("get_connected_port"), &StreamPeerTCP::get_connected_port); ClassDB::bind_method(D_METHOD("disconnect_from_host"), &StreamPeerTCP::disconnect_from_host); + ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &StreamPeerTCP::set_no_delay); BIND_ENUM_CONSTANT(STATUS_NONE); BIND_ENUM_CONSTANT(STATUS_CONNECTING); diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h index fc84525c5f..8a16d820f2 100644 --- a/core/io/stream_peer_tcp.h +++ b/core/io/stream_peer_tcp.h @@ -65,7 +65,7 @@ public: virtual void disconnect_from_host() = 0; virtual IP_Address get_connected_host() const = 0; virtual uint16_t get_connected_port() const = 0; - virtual void set_nodelay(bool p_enabled) = 0; + virtual void set_no_delay(bool p_enabled) = 0; static Ref<StreamPeerTCP> create_ref(); static StreamPeerTCP *create(); |