1 | /**************************************************************************/ |
2 | /* resource.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 "resource.h" |
32 | |
33 | #include "core/core_string_names.h" |
34 | #include "core/io/file_access.h" |
35 | #include "core/io/resource_loader.h" |
36 | #include "core/math/math_funcs.h" |
37 | #include "core/object/script_language.h" |
38 | #include "core/os/os.h" |
39 | #include "scene/main/node.h" //only so casting works |
40 | |
41 | #include <stdio.h> |
42 | |
43 | void Resource::emit_changed() { |
44 | emit_signal(CoreStringNames::get_singleton()->changed); |
45 | } |
46 | |
47 | void Resource::_resource_path_changed() { |
48 | } |
49 | |
50 | void Resource::set_path(const String &p_path, bool p_take_over) { |
51 | if (path_cache == p_path) { |
52 | return; |
53 | } |
54 | |
55 | if (p_path.is_empty()) { |
56 | p_take_over = false; // Can't take over an empty path |
57 | } |
58 | |
59 | ResourceCache::lock.lock(); |
60 | |
61 | if (!path_cache.is_empty()) { |
62 | ResourceCache::resources.erase(path_cache); |
63 | } |
64 | |
65 | path_cache = "" ; |
66 | |
67 | Ref<Resource> existing = ResourceCache::get_ref(p_path); |
68 | |
69 | if (existing.is_valid()) { |
70 | if (p_take_over) { |
71 | existing->path_cache = String(); |
72 | ResourceCache::resources.erase(p_path); |
73 | } else { |
74 | ResourceCache::lock.unlock(); |
75 | ERR_FAIL_MSG("Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion)." ); |
76 | } |
77 | } |
78 | |
79 | path_cache = p_path; |
80 | |
81 | if (!path_cache.is_empty()) { |
82 | ResourceCache::resources[path_cache] = this; |
83 | } |
84 | ResourceCache::lock.unlock(); |
85 | |
86 | _resource_path_changed(); |
87 | } |
88 | |
89 | String Resource::get_path() const { |
90 | return path_cache; |
91 | } |
92 | |
93 | String Resource::generate_scene_unique_id() { |
94 | // Generate a unique enough hash, but still user-readable. |
95 | // If it's not unique it does not matter because the saver will try again. |
96 | OS::DateTime dt = OS::get_singleton()->get_datetime(); |
97 | uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec()); |
98 | hash = hash_murmur3_one_32(dt.year, hash); |
99 | hash = hash_murmur3_one_32(dt.month, hash); |
100 | hash = hash_murmur3_one_32(dt.day, hash); |
101 | hash = hash_murmur3_one_32(dt.hour, hash); |
102 | hash = hash_murmur3_one_32(dt.minute, hash); |
103 | hash = hash_murmur3_one_32(dt.second, hash); |
104 | hash = hash_murmur3_one_32(Math::rand(), hash); |
105 | |
106 | static constexpr uint32_t characters = 5; |
107 | static constexpr uint32_t char_count = ('z' - 'a'); |
108 | static constexpr uint32_t base = char_count + ('9' - '0'); |
109 | String id; |
110 | for (uint32_t i = 0; i < characters; i++) { |
111 | uint32_t c = hash % base; |
112 | if (c < char_count) { |
113 | id += String::chr('a' + c); |
114 | } else { |
115 | id += String::chr('0' + (c - char_count)); |
116 | } |
117 | hash /= base; |
118 | } |
119 | |
120 | return id; |
121 | } |
122 | |
123 | void Resource::set_scene_unique_id(const String &p_id) { |
124 | scene_unique_id = p_id; |
125 | } |
126 | |
127 | String Resource::get_scene_unique_id() const { |
128 | return scene_unique_id; |
129 | } |
130 | |
131 | void Resource::set_name(const String &p_name) { |
132 | name = p_name; |
133 | emit_changed(); |
134 | } |
135 | |
136 | String Resource::get_name() const { |
137 | return name; |
138 | } |
139 | |
140 | void Resource::update_configuration_warning() { |
141 | if (_update_configuration_warning) { |
142 | _update_configuration_warning(); |
143 | } |
144 | } |
145 | |
146 | bool Resource::editor_can_reload_from_file() { |
147 | return true; //by default yes |
148 | } |
149 | |
150 | void Resource::connect_changed(const Callable &p_callable, uint32_t p_flags) { |
151 | if (!is_connected(CoreStringNames::get_singleton()->changed, p_callable) || p_flags & CONNECT_REFERENCE_COUNTED) { |
152 | connect(CoreStringNames::get_singleton()->changed, p_callable, p_flags); |
153 | } |
154 | } |
155 | |
156 | void Resource::disconnect_changed(const Callable &p_callable) { |
157 | if (is_connected(CoreStringNames::get_singleton()->changed, p_callable)) { |
158 | disconnect(CoreStringNames::get_singleton()->changed, p_callable); |
159 | } |
160 | } |
161 | |
162 | void Resource::reset_state() { |
163 | } |
164 | |
165 | Error Resource::copy_from(const Ref<Resource> &p_resource) { |
166 | ERR_FAIL_COND_V(p_resource.is_null(), ERR_INVALID_PARAMETER); |
167 | if (get_class() != p_resource->get_class()) { |
168 | return ERR_INVALID_PARAMETER; |
169 | } |
170 | |
171 | reset_state(); // May want to reset state. |
172 | |
173 | List<PropertyInfo> pi; |
174 | p_resource->get_property_list(&pi); |
175 | |
176 | for (const PropertyInfo &E : pi) { |
177 | if (!(E.usage & PROPERTY_USAGE_STORAGE)) { |
178 | continue; |
179 | } |
180 | if (E.name == "resource_path" ) { |
181 | continue; //do not change path |
182 | } |
183 | |
184 | set(E.name, p_resource->get(E.name)); |
185 | } |
186 | return OK; |
187 | } |
188 | void Resource::reload_from_file() { |
189 | String path = get_path(); |
190 | if (!path.is_resource_file()) { |
191 | return; |
192 | } |
193 | |
194 | Ref<Resource> s = ResourceLoader::load(ResourceLoader::path_remap(path), get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); |
195 | |
196 | if (!s.is_valid()) { |
197 | return; |
198 | } |
199 | |
200 | copy_from(s); |
201 | } |
202 | |
203 | Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache) { |
204 | List<PropertyInfo> plist; |
205 | get_property_list(&plist); |
206 | |
207 | Ref<Resource> r = Object::cast_to<Resource>(ClassDB::instantiate(get_class())); |
208 | ERR_FAIL_COND_V(r.is_null(), Ref<Resource>()); |
209 | |
210 | r->local_scene = p_for_scene; |
211 | |
212 | for (const PropertyInfo &E : plist) { |
213 | if (!(E.usage & PROPERTY_USAGE_STORAGE)) { |
214 | continue; |
215 | } |
216 | Variant p = get(E.name); |
217 | if (p.get_type() == Variant::OBJECT) { |
218 | Ref<Resource> sr = p; |
219 | if (sr.is_valid()) { |
220 | if (sr->is_local_to_scene()) { |
221 | if (remap_cache.has(sr)) { |
222 | p = remap_cache[sr]; |
223 | } else { |
224 | Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, remap_cache); |
225 | p = dupe; |
226 | remap_cache[sr] = dupe; |
227 | } |
228 | } |
229 | } |
230 | } |
231 | |
232 | r->set(E.name, p); |
233 | } |
234 | |
235 | return r; |
236 | } |
237 | |
238 | void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache) { |
239 | List<PropertyInfo> plist; |
240 | get_property_list(&plist); |
241 | |
242 | reset_local_to_scene(); |
243 | local_scene = p_for_scene; |
244 | |
245 | for (const PropertyInfo &E : plist) { |
246 | if (!(E.usage & PROPERTY_USAGE_STORAGE)) { |
247 | continue; |
248 | } |
249 | Variant p = get(E.name); |
250 | if (p.get_type() == Variant::OBJECT) { |
251 | Ref<Resource> sr = p; |
252 | if (sr.is_valid()) { |
253 | if (sr->is_local_to_scene()) { |
254 | if (!remap_cache.has(sr)) { |
255 | sr->configure_for_local_scene(p_for_scene, remap_cache); |
256 | remap_cache[sr] = sr; |
257 | } |
258 | } |
259 | } |
260 | } |
261 | } |
262 | } |
263 | |
264 | Ref<Resource> Resource::duplicate(bool p_subresources) const { |
265 | List<PropertyInfo> plist; |
266 | get_property_list(&plist); |
267 | |
268 | Ref<Resource> r = static_cast<Resource *>(ClassDB::instantiate(get_class())); |
269 | ERR_FAIL_COND_V(r.is_null(), Ref<Resource>()); |
270 | |
271 | for (const PropertyInfo &E : plist) { |
272 | if (!(E.usage & PROPERTY_USAGE_STORAGE)) { |
273 | continue; |
274 | } |
275 | Variant p = get(E.name); |
276 | |
277 | switch (p.get_type()) { |
278 | case Variant::Type::DICTIONARY: |
279 | case Variant::Type::ARRAY: |
280 | case Variant::Type::PACKED_BYTE_ARRAY: |
281 | case Variant::Type::PACKED_COLOR_ARRAY: |
282 | case Variant::Type::PACKED_INT32_ARRAY: |
283 | case Variant::Type::PACKED_INT64_ARRAY: |
284 | case Variant::Type::PACKED_FLOAT32_ARRAY: |
285 | case Variant::Type::PACKED_FLOAT64_ARRAY: |
286 | case Variant::Type::PACKED_STRING_ARRAY: |
287 | case Variant::Type::PACKED_VECTOR2_ARRAY: |
288 | case Variant::Type::PACKED_VECTOR3_ARRAY: { |
289 | r->set(E.name, p.duplicate(p_subresources)); |
290 | } break; |
291 | |
292 | case Variant::Type::OBJECT: { |
293 | if (!(E.usage & PROPERTY_USAGE_NEVER_DUPLICATE) && (p_subresources || (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE))) { |
294 | Ref<Resource> sr = p; |
295 | if (sr.is_valid()) { |
296 | r->set(E.name, sr->duplicate(p_subresources)); |
297 | } |
298 | } else { |
299 | r->set(E.name, p); |
300 | } |
301 | } break; |
302 | |
303 | default: { |
304 | r->set(E.name, p); |
305 | } |
306 | } |
307 | } |
308 | |
309 | return r; |
310 | } |
311 | |
312 | void Resource::_set_path(const String &p_path) { |
313 | set_path(p_path, false); |
314 | } |
315 | |
316 | void Resource::_take_over_path(const String &p_path) { |
317 | set_path(p_path, true); |
318 | } |
319 | |
320 | RID Resource::get_rid() const { |
321 | if (get_script_instance()) { |
322 | Callable::CallError ce; |
323 | RID ret = get_script_instance()->callp(SNAME("_get_rid" ), nullptr, 0, ce); |
324 | if (ce.error == Callable::CallError::CALL_OK && ret.is_valid()) { |
325 | return ret; |
326 | } |
327 | } |
328 | if (_get_extension() && _get_extension()->get_rid) { |
329 | RID ret; |
330 | ret.from_uint64(_get_extension()->get_rid(_get_extension_instance())); |
331 | if (ret.is_valid()) { |
332 | return ret; |
333 | } |
334 | } |
335 | |
336 | return RID(); |
337 | } |
338 | |
339 | #ifdef TOOLS_ENABLED |
340 | |
341 | uint32_t Resource::hash_edited_version() const { |
342 | uint32_t hash = hash_murmur3_one_32(get_edited_version()); |
343 | |
344 | List<PropertyInfo> plist; |
345 | get_property_list(&plist); |
346 | |
347 | for (const PropertyInfo &E : plist) { |
348 | if (E.usage & PROPERTY_USAGE_STORAGE && E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) { |
349 | Ref<Resource> res = get(E.name); |
350 | if (res.is_valid()) { |
351 | hash = hash_murmur3_one_32(res->hash_edited_version(), hash); |
352 | } |
353 | } |
354 | } |
355 | |
356 | return hash; |
357 | } |
358 | |
359 | #endif |
360 | |
361 | void Resource::set_local_to_scene(bool p_enable) { |
362 | local_to_scene = p_enable; |
363 | } |
364 | |
365 | bool Resource::is_local_to_scene() const { |
366 | return local_to_scene; |
367 | } |
368 | |
369 | Node *Resource::get_local_scene() const { |
370 | if (local_scene) { |
371 | return local_scene; |
372 | } |
373 | |
374 | if (_get_local_scene_func) { |
375 | return _get_local_scene_func(); |
376 | } |
377 | |
378 | return nullptr; |
379 | } |
380 | |
381 | void Resource::setup_local_to_scene() { |
382 | // Can't use GDVIRTUAL in Resource, so this will have to be done with a signal |
383 | emit_signal(SNAME("setup_local_to_scene_requested" )); |
384 | } |
385 | |
386 | void Resource::reset_local_to_scene() { |
387 | // Restores the state as if setup_local_to_scene() hadn't been called. |
388 | } |
389 | |
390 | Node *(*Resource::_get_local_scene_func)() = nullptr; |
391 | void (*Resource::_update_configuration_warning)() = nullptr; |
392 | |
393 | void Resource::set_as_translation_remapped(bool p_remapped) { |
394 | if (remapped_list.in_list() == p_remapped) { |
395 | return; |
396 | } |
397 | |
398 | ResourceCache::lock.lock(); |
399 | |
400 | if (p_remapped) { |
401 | ResourceLoader::remapped_list.add(&remapped_list); |
402 | } else { |
403 | ResourceLoader::remapped_list.remove(&remapped_list); |
404 | } |
405 | |
406 | ResourceCache::lock.unlock(); |
407 | } |
408 | |
409 | #ifdef TOOLS_ENABLED |
410 | //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored |
411 | void Resource::set_id_for_path(const String &p_path, const String &p_id) { |
412 | if (p_id.is_empty()) { |
413 | ResourceCache::path_cache_lock.write_lock(); |
414 | ResourceCache::resource_path_cache[p_path].erase(get_path()); |
415 | ResourceCache::path_cache_lock.write_unlock(); |
416 | } else { |
417 | ResourceCache::path_cache_lock.write_lock(); |
418 | ResourceCache::resource_path_cache[p_path][get_path()] = p_id; |
419 | ResourceCache::path_cache_lock.write_unlock(); |
420 | } |
421 | } |
422 | |
423 | String Resource::get_id_for_path(const String &p_path) const { |
424 | ResourceCache::path_cache_lock.read_lock(); |
425 | if (ResourceCache::resource_path_cache[p_path].has(get_path())) { |
426 | String result = ResourceCache::resource_path_cache[p_path][get_path()]; |
427 | ResourceCache::path_cache_lock.read_unlock(); |
428 | return result; |
429 | } else { |
430 | ResourceCache::path_cache_lock.read_unlock(); |
431 | return "" ; |
432 | } |
433 | } |
434 | #endif |
435 | |
436 | void Resource::_bind_methods() { |
437 | ClassDB::bind_method(D_METHOD("set_path" , "path" ), &Resource::_set_path); |
438 | ClassDB::bind_method(D_METHOD("take_over_path" , "path" ), &Resource::_take_over_path); |
439 | ClassDB::bind_method(D_METHOD("get_path" ), &Resource::get_path); |
440 | ClassDB::bind_method(D_METHOD("set_name" , "name" ), &Resource::set_name); |
441 | ClassDB::bind_method(D_METHOD("get_name" ), &Resource::get_name); |
442 | ClassDB::bind_method(D_METHOD("get_rid" ), &Resource::get_rid); |
443 | ClassDB::bind_method(D_METHOD("set_local_to_scene" , "enable" ), &Resource::set_local_to_scene); |
444 | ClassDB::bind_method(D_METHOD("is_local_to_scene" ), &Resource::is_local_to_scene); |
445 | ClassDB::bind_method(D_METHOD("get_local_scene" ), &Resource::get_local_scene); |
446 | ClassDB::bind_method(D_METHOD("setup_local_to_scene" ), &Resource::setup_local_to_scene); |
447 | |
448 | ClassDB::bind_method(D_METHOD("emit_changed" ), &Resource::emit_changed); |
449 | |
450 | ClassDB::bind_method(D_METHOD("duplicate" , "subresources" ), &Resource::duplicate, DEFVAL(false)); |
451 | ADD_SIGNAL(MethodInfo("changed" )); |
452 | ADD_SIGNAL(MethodInfo("setup_local_to_scene_requested" )); |
453 | |
454 | ADD_GROUP("Resource" , "resource_" ); |
455 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resource_local_to_scene" ), "set_local_to_scene" , "is_local_to_scene" ); |
456 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_path" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_EDITOR), "set_path" , "get_path" ); |
457 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_name" ), "set_name" , "get_name" ); |
458 | |
459 | MethodInfo get_rid_bind("_get_rid" ); |
460 | get_rid_bind.return_val.type = Variant::RID; |
461 | |
462 | ::ClassDB::add_virtual_method(get_class_static(), get_rid_bind, true, Vector<String>(), true); |
463 | } |
464 | |
465 | Resource::Resource() : |
466 | remapped_list(this) {} |
467 | |
468 | Resource::~Resource() { |
469 | if (!path_cache.is_empty()) { |
470 | ResourceCache::lock.lock(); |
471 | ResourceCache::resources.erase(path_cache); |
472 | ResourceCache::lock.unlock(); |
473 | } |
474 | } |
475 | |
476 | HashMap<String, Resource *> ResourceCache::resources; |
477 | #ifdef TOOLS_ENABLED |
478 | HashMap<String, HashMap<String, String>> ResourceCache::resource_path_cache; |
479 | #endif |
480 | |
481 | Mutex ResourceCache::lock; |
482 | #ifdef TOOLS_ENABLED |
483 | RWLock ResourceCache::path_cache_lock; |
484 | #endif |
485 | |
486 | void ResourceCache::clear() { |
487 | if (resources.size()) { |
488 | ERR_PRINT("Resources still in use at exit (run with --verbose for details)." ); |
489 | if (OS::get_singleton()->is_stdout_verbose()) { |
490 | for (const KeyValue<String, Resource *> &E : resources) { |
491 | print_line(vformat("Resource still in use: %s (%s)" , E.key, E.value->get_class())); |
492 | } |
493 | } |
494 | } |
495 | |
496 | resources.clear(); |
497 | } |
498 | |
499 | bool ResourceCache::has(const String &p_path) { |
500 | lock.lock(); |
501 | |
502 | Resource **res = resources.getptr(p_path); |
503 | |
504 | if (res && (*res)->get_reference_count() == 0) { |
505 | // This resource is in the process of being deleted, ignore its existence. |
506 | (*res)->path_cache = String(); |
507 | resources.erase(p_path); |
508 | res = nullptr; |
509 | } |
510 | |
511 | lock.unlock(); |
512 | |
513 | if (!res) { |
514 | return false; |
515 | } |
516 | |
517 | return true; |
518 | } |
519 | |
520 | Ref<Resource> ResourceCache::get_ref(const String &p_path) { |
521 | Ref<Resource> ref; |
522 | lock.lock(); |
523 | |
524 | Resource **res = resources.getptr(p_path); |
525 | |
526 | if (res) { |
527 | ref = Ref<Resource>(*res); |
528 | } |
529 | |
530 | if (res && !ref.is_valid()) { |
531 | // This resource is in the process of being deleted, ignore its existence |
532 | (*res)->path_cache = String(); |
533 | resources.erase(p_path); |
534 | res = nullptr; |
535 | } |
536 | |
537 | lock.unlock(); |
538 | |
539 | return ref; |
540 | } |
541 | |
542 | void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) { |
543 | lock.lock(); |
544 | |
545 | LocalVector<String> to_remove; |
546 | |
547 | for (KeyValue<String, Resource *> &E : resources) { |
548 | Ref<Resource> ref = Ref<Resource>(E.value); |
549 | |
550 | if (!ref.is_valid()) { |
551 | // This resource is in the process of being deleted, ignore its existence |
552 | E.value->path_cache = String(); |
553 | to_remove.push_back(E.key); |
554 | continue; |
555 | } |
556 | |
557 | p_resources->push_back(ref); |
558 | } |
559 | |
560 | for (const String &E : to_remove) { |
561 | resources.erase(E); |
562 | } |
563 | |
564 | lock.unlock(); |
565 | } |
566 | |
567 | int ResourceCache::get_cached_resource_count() { |
568 | lock.lock(); |
569 | int rc = resources.size(); |
570 | lock.unlock(); |
571 | |
572 | return rc; |
573 | } |
574 | |