| 1 | /**************************************************************************/ |
| 2 | /* scene_cache_interface.cpp */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #include "scene_cache_interface.h" |
| 32 | |
| 33 | #include "scene_multiplayer.h" |
| 34 | |
| 35 | #include "core/io/marshalls.h" |
| 36 | #include "scene/main/node.h" |
| 37 | #include "scene/main/window.h" |
| 38 | |
| 39 | void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) { |
| 40 | if (p_connected) { |
| 41 | path_get_cache.insert(p_id, PathGetCache()); |
| 42 | } else { |
| 43 | // Cleanup get cache. |
| 44 | path_get_cache.erase(p_id); |
| 45 | // Cleanup sent cache. |
| 46 | // Some refactoring is needed to make this faster and do paths GC. |
| 47 | for (const KeyValue<NodePath, PathSentCache> &E : path_send_cache) { |
| 48 | PathSentCache *psc = path_send_cache.getptr(E.key); |
| 49 | psc->confirmed_peers.erase(p_id); |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { |
| 55 | Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); |
| 56 | ERR_FAIL_COND(!root_node); |
| 57 | ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small." ); |
| 58 | int ofs = 1; |
| 59 | |
| 60 | String methods_md5; |
| 61 | methods_md5.parse_utf8((const char *)(p_packet + ofs), 32); |
| 62 | ofs += 33; |
| 63 | |
| 64 | int id = decode_uint32(&p_packet[ofs]); |
| 65 | ofs += 4; |
| 66 | |
| 67 | String paths; |
| 68 | paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs); |
| 69 | |
| 70 | NodePath path = paths; |
| 71 | |
| 72 | if (!path_get_cache.has(p_from)) { |
| 73 | path_get_cache[p_from] = PathGetCache(); |
| 74 | } |
| 75 | |
| 76 | Node *node = root_node->get_node(path); |
| 77 | ERR_FAIL_COND(node == nullptr); |
| 78 | const bool valid_rpc_checksum = multiplayer->get_rpc_md5(node) == methods_md5; |
| 79 | if (valid_rpc_checksum == false) { |
| 80 | ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); |
| 81 | } |
| 82 | |
| 83 | PathGetCache::NodeInfo ni; |
| 84 | ni.path = path; |
| 85 | |
| 86 | path_get_cache[p_from].nodes[id] = ni; |
| 87 | |
| 88 | // Encode path to send ack. |
| 89 | CharString pname = String(path).utf8(); |
| 90 | int len = encode_cstring(pname.get_data(), nullptr); |
| 91 | |
| 92 | Vector<uint8_t> packet; |
| 93 | |
| 94 | packet.resize(1 + 1 + len); |
| 95 | packet.write[0] = SceneMultiplayer::NETWORK_COMMAND_CONFIRM_PATH; |
| 96 | packet.write[1] = valid_rpc_checksum; |
| 97 | encode_cstring(pname.get_data(), &packet.write[2]); |
| 98 | |
| 99 | Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer(); |
| 100 | ERR_FAIL_COND(multiplayer_peer.is_null()); |
| 101 | |
| 102 | multiplayer_peer->set_transfer_channel(0); |
| 103 | multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); |
| 104 | multiplayer->send_command(p_from, packet.ptr(), packet.size()); |
| 105 | } |
| 106 | |
| 107 | void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { |
| 108 | ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small." ); |
| 109 | |
| 110 | const bool valid_rpc_checksum = p_packet[1]; |
| 111 | |
| 112 | String paths; |
| 113 | paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); |
| 114 | |
| 115 | NodePath path = paths; |
| 116 | |
| 117 | if (valid_rpc_checksum == false) { |
| 118 | ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); |
| 119 | } |
| 120 | |
| 121 | PathSentCache *psc = path_send_cache.getptr(path); |
| 122 | ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache." ); |
| 123 | |
| 124 | HashMap<int, bool>::Iterator E = psc->confirmed_peers.find(p_from); |
| 125 | ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path." ); |
| 126 | E->value = true; |
| 127 | } |
| 128 | |
| 129 | Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers) { |
| 130 | // Encode function name. |
| 131 | const CharString path = String(p_path).utf8(); |
| 132 | const int path_len = encode_cstring(path.get_data(), nullptr); |
| 133 | |
| 134 | // Extract MD5 from rpc methods list. |
| 135 | const String methods_md5 = multiplayer->get_rpc_md5(p_node); |
| 136 | const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. |
| 137 | |
| 138 | Vector<uint8_t> packet; |
| 139 | packet.resize(1 + 4 + path_len + methods_md5_len); |
| 140 | int ofs = 0; |
| 141 | |
| 142 | packet.write[ofs] = SceneMultiplayer::NETWORK_COMMAND_SIMPLIFY_PATH; |
| 143 | ofs += 1; |
| 144 | |
| 145 | ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); |
| 146 | |
| 147 | ofs += encode_uint32(psc->id, &packet.write[ofs]); |
| 148 | |
| 149 | ofs += encode_cstring(path.get_data(), &packet.write[ofs]); |
| 150 | |
| 151 | Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer(); |
| 152 | ERR_FAIL_COND_V(multiplayer_peer.is_null(), ERR_BUG); |
| 153 | |
| 154 | Error err = OK; |
| 155 | for (int peer_id : p_peers) { |
| 156 | multiplayer_peer->set_transfer_channel(0); |
| 157 | multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); |
| 158 | err = multiplayer->send_command(peer_id, packet.ptr(), packet.size()); |
| 159 | ERR_FAIL_COND_V(err != OK, err); |
| 160 | // Insert into confirmed, but as false since it was not confirmed. |
| 161 | psc->confirmed_peers.insert(peer_id, false); |
| 162 | } |
| 163 | return err; |
| 164 | } |
| 165 | |
| 166 | bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) { |
| 167 | const PathSentCache *psc = path_send_cache.getptr(p_path); |
| 168 | ERR_FAIL_COND_V(!psc, false); |
| 169 | HashMap<int, bool>::ConstIterator F = psc->confirmed_peers.find(p_peer); |
| 170 | ERR_FAIL_COND_V(!F, false); // Should never happen. |
| 171 | return F->value; |
| 172 | } |
| 173 | |
| 174 | int SceneCacheInterface::make_object_cache(Object *p_obj) { |
| 175 | Node *node = Object::cast_to<Node>(p_obj); |
| 176 | ERR_FAIL_COND_V(!node, -1); |
| 177 | NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path()); |
| 178 | // See if the path is cached. |
| 179 | PathSentCache *psc = path_send_cache.getptr(for_path); |
| 180 | if (!psc) { |
| 181 | // Path is not cached, create. |
| 182 | path_send_cache[for_path] = PathSentCache(); |
| 183 | psc = path_send_cache.getptr(for_path); |
| 184 | psc->id = last_send_cache_id++; |
| 185 | } |
| 186 | return psc->id; |
| 187 | } |
| 188 | |
| 189 | bool SceneCacheInterface::send_object_cache(Object *p_obj, int p_peer_id, int &r_id) { |
| 190 | Node *node = Object::cast_to<Node>(p_obj); |
| 191 | ERR_FAIL_COND_V(!node, false); |
| 192 | |
| 193 | r_id = make_object_cache(p_obj); |
| 194 | ERR_FAIL_COND_V(r_id < 0, false); |
| 195 | NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path()); |
| 196 | PathSentCache *psc = path_send_cache.getptr(for_path); |
| 197 | |
| 198 | bool has_all_peers = true; |
| 199 | List<int> peers_to_add; // If one is missing, take note to add it. |
| 200 | |
| 201 | if (p_peer_id > 0) { |
| 202 | // Fast single peer check. |
| 203 | HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(p_peer_id); |
| 204 | if (!F) { |
| 205 | peers_to_add.push_back(p_peer_id); // Need to also be notified. |
| 206 | has_all_peers = false; |
| 207 | } else if (!F->value) { |
| 208 | has_all_peers = false; |
| 209 | } |
| 210 | } else { |
| 211 | // Long and painful. |
| 212 | for (const int &E : multiplayer->get_connected_peers()) { |
| 213 | if (p_peer_id < 0 && E == -p_peer_id) { |
| 214 | continue; // Continue, excluded. |
| 215 | } |
| 216 | |
| 217 | HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(E); |
| 218 | if (!F) { |
| 219 | peers_to_add.push_back(E); // Need to also be notified. |
| 220 | has_all_peers = false; |
| 221 | } else if (!F->value) { |
| 222 | has_all_peers = false; |
| 223 | } |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | if (peers_to_add.size()) { |
| 228 | _send_confirm_path(node, for_path, psc, peers_to_add); |
| 229 | } |
| 230 | |
| 231 | return has_all_peers; |
| 232 | } |
| 233 | |
| 234 | Object *SceneCacheInterface::get_cached_object(int p_from, uint32_t p_cache_id) { |
| 235 | Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); |
| 236 | ERR_FAIL_COND_V(!root_node, nullptr); |
| 237 | HashMap<int, PathGetCache>::Iterator E = path_get_cache.find(p_from); |
| 238 | ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d." , p_from)); |
| 239 | |
| 240 | HashMap<int, PathGetCache::NodeInfo>::Iterator F = E->value.nodes.find(p_cache_id); |
| 241 | ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d." , p_cache_id, p_from)); |
| 242 | |
| 243 | PathGetCache::NodeInfo *ni = &F->value; |
| 244 | Node *node = root_node->get_node(ni->path); |
| 245 | if (!node) { |
| 246 | ERR_PRINT("Failed to get cached path: " + String(ni->path) + "." ); |
| 247 | } |
| 248 | return node; |
| 249 | } |
| 250 | |
| 251 | void SceneCacheInterface::clear() { |
| 252 | path_get_cache.clear(); |
| 253 | path_send_cache.clear(); |
| 254 | last_send_cache_id = 1; |
| 255 | } |
| 256 | |