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 | |