diff options
Diffstat (limited to 'core/io')
-rw-r--r-- | core/io/file_access_buffered.h | 2 | ||||
-rw-r--r-- | core/io/file_access_buffered_fa.h | 2 | ||||
-rw-r--r-- | core/io/file_access_pack.h | 1 | ||||
-rw-r--r-- | core/io/file_access_zip.cpp | 6 | ||||
-rw-r--r-- | core/io/http_client.cpp | 98 | ||||
-rw-r--r-- | core/io/http_client.h | 1 | ||||
-rw-r--r-- | core/io/logger.h | 12 | ||||
-rw-r--r-- | core/io/marshalls.cpp | 6 | ||||
-rw-r--r-- | core/io/packet_peer.cpp | 2 | ||||
-rw-r--r-- | core/io/resource_format_binary.cpp | 53 | ||||
-rw-r--r-- | core/io/resource_format_binary.h | 9 | ||||
-rw-r--r-- | core/io/resource_importer.cpp (renamed from core/io/resource_import.cpp) | 61 | ||||
-rw-r--r-- | core/io/resource_importer.h (renamed from core/io/resource_import.h) | 28 | ||||
-rw-r--r-- | core/io/resource_loader.cpp | 187 | ||||
-rw-r--r-- | core/io/resource_loader.h | 32 | ||||
-rw-r--r-- | core/io/resource_saver.cpp | 8 |
16 files changed, 403 insertions, 105 deletions
diff --git a/core/io/file_access_buffered.h b/core/io/file_access_buffered.h index 756045a674..4065d77c58 100644 --- a/core/io/file_access_buffered.h +++ b/core/io/file_access_buffered.h @@ -31,8 +31,8 @@ #ifndef FILE_ACCESS_BUFFERED_H #define FILE_ACCESS_BUFFERED_H -#include "core/dvector.h" #include "core/os/file_access.h" +#include "core/pool_vector.h" #include "core/ustring.h" class FileAccessBuffered : public FileAccess { diff --git a/core/io/file_access_buffered_fa.h b/core/io/file_access_buffered_fa.h index 5180f2344f..be960fbc25 100644 --- a/core/io/file_access_buffered_fa.h +++ b/core/io/file_access_buffered_fa.h @@ -54,7 +54,7 @@ class FileAccessBufferedFA : public FileAccessBuffered { cache.offset = p_offset; cache.buffer.resize(p_size); - // on dvector + // on PoolVector //PoolVector<uint8_t>::Write write = cache.buffer.write(); //f.get_buffer(write.ptrw(), p_size); diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index ae5e83d405..a90672ce26 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -223,7 +223,6 @@ public: virtual String get_filesystem_type() const; - DirAccessPack(); ~DirAccessPack(); }; diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index 66b911e83c..be28c9a877 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -168,10 +168,10 @@ bool ZipArchive::try_open_pack(const String &p_path) { zlib_filefunc_def io; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) + FileAccess *fa = FileAccess::open(p_path, FileAccess::READ); + if (!fa) return false; - io.opaque = f; + io.opaque = fa; io.zopen_file = godot_open; io.zread_file = godot_read; io.zwrite_file = godot_write; diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index b3fd814870..e5c6d2a4f2 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -278,6 +278,7 @@ void HTTPClient::close() { body_size = -1; body_left = 0; chunk_left = 0; + chunk_trailer_part = 0; read_until_eof = false; response_num = 0; handshaking = false; @@ -421,16 +422,16 @@ Error HTTPClient::poll() { chunked = false; body_left = 0; chunk_left = 0; + chunk_trailer_part = false; read_until_eof = false; response_str.clear(); response_headers.clear(); response_num = RESPONSE_OK; - // Per the HTTP 1.1 spec, keep-alive is the default, but in practice - // it's safe to assume it only if the explicit header is found, allowing - // to handle body-up-to-EOF responses on naive servers; that's what Curl - // and browsers do - bool keep_alive = false; + // Per the HTTP 1.1 spec, keep-alive is the default. + // Not following that specification breaks standard implemetations. + // Broken web servers should be fixed. + bool keep_alive = true; for (int i = 0; i < responses.size(); i++) { @@ -447,8 +448,8 @@ Error HTTPClient::poll() { if (encoding == "chunked") { chunked = true; } - } else if (s.begins_with("connection: keep-alive")) { - keep_alive = true; + } else if (s.begins_with("connection: close")) { + keep_alive = false; } if (i == 0 && responses[i].begins_with("HTTP")) { @@ -505,13 +506,37 @@ PoolByteArray HTTPClient::read_response_body_chunk() { ERR_FAIL_COND_V(status != STATUS_BODY, PoolByteArray()); + PoolByteArray ret; Error err = OK; if (chunked) { while (true) { - if (chunk_left == 0) { + if (chunk_trailer_part) { + // We need to consume the trailer part too or keep-alive will break + uint8_t b; + int rec = 0; + err = _get_http_data(&b, 1, rec); + + if (rec == 0) + break; + + chunk.push_back(b); + int cs = chunk.size(); + if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) { + if (cs == 2) { + // Finally over + chunk_trailer_part = false; + status = STATUS_CONNECTED; + chunk.clear(); + break; + } else { + // We do not process nor return the trailer data + chunk.clear(); + } + } + } else if (chunk_left == 0) { // Reading length uint8_t b; int rec = 0; @@ -525,7 +550,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() { if (chunk.size() > 32) { ERR_PRINT("HTTP Invalid chunk hex len"); status = STATUS_CONNECTION_ERROR; - return PoolByteArray(); + break; } if (chunk.size() > 2 && chunk[chunk.size() - 2] == '\r' && chunk[chunk.size() - 1] == '\n') { @@ -543,22 +568,22 @@ PoolByteArray HTTPClient::read_response_body_chunk() { else { ERR_PRINT("HTTP Chunk len not in hex!!"); status = STATUS_CONNECTION_ERROR; - return PoolByteArray(); + break; } len <<= 4; len |= v; if (len > (1 << 24)) { ERR_PRINT("HTTP Chunk too big!! >16mb"); status = STATUS_CONNECTION_ERROR; - return PoolByteArray(); + break; } } if (len == 0) { // End reached! - status = STATUS_CONNECTED; + chunk_trailer_part = true; chunk.clear(); - return PoolByteArray(); + break; } chunk_left = len + 2; @@ -578,18 +603,13 @@ PoolByteArray HTTPClient::read_response_body_chunk() { if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') { ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)"); status = STATUS_CONNECTION_ERROR; - return PoolByteArray(); + break; } - PoolByteArray ret; ret.resize(chunk.size() - 2); - { - PoolByteArray::Write w = ret.write(); - copymem(w.ptr(), chunk.ptr(), chunk.size() - 2); - } + PoolByteArray::Write w = ret.write(); + copymem(w.ptr(), chunk.ptr(), chunk.size() - 2); chunk.clear(); - - return ret; } break; @@ -599,45 +619,26 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } else { int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size; - PoolByteArray ret; ret.resize(to_read); int _offset = 0; - while (read_until_eof || to_read > 0) { + while (to_read > 0) { int rec = 0; { PoolByteArray::Write w = ret.write(); err = _get_http_data(w.ptr() + _offset, to_read, rec); } - if (rec < 0) { - if (to_read > 0) // Ended up reading less - ret.resize(_offset); + if (rec <= 0) { // Ended up reading less + ret.resize(_offset); break; } else { _offset += rec; + to_read -= rec; if (!read_until_eof) { body_left -= rec; - to_read -= rec; - } else { - if (rec < to_read) { - ret.resize(_offset); - err = ERR_FILE_EOF; - break; - } - ret.resize(_offset + to_read); } } - } - if (!read_until_eof) { - if (body_left == 0) { - status = STATUS_CONNECTED; - } - return ret; - } else { - if (err == ERR_FILE_EOF) { - err = OK; // EOF is expected here - close(); - return ret; - } + if (err != OK) + break; } } @@ -652,12 +653,12 @@ PoolByteArray HTTPClient::read_response_body_chunk() { status = STATUS_CONNECTION_ERROR; } - } else if (body_left == 0 && !chunked) { + } else if (body_left == 0 && !chunked && !read_until_eof) { status = STATUS_CONNECTED; } - return PoolByteArray(); + return ret; } HTTPClient::Status HTTPClient::get_status() const { @@ -719,6 +720,7 @@ HTTPClient::HTTPClient() { body_left = 0; read_until_eof = false; chunk_left = 0; + chunk_trailer_part = false; response_num = 0; ssl = false; blocking = false; diff --git a/core/io/http_client.h b/core/io/http_client.h index 1ad44d5f01..85ee1959a2 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -172,6 +172,7 @@ private: bool chunked; Vector<uint8_t> chunk; int chunk_left; + bool chunk_trailer_part; int body_size; int body_left; bool read_until_eof; diff --git a/core/io/logger.h b/core/io/logger.h index 0b871a13de..ff5b8ce489 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -49,11 +49,11 @@ public: ERR_SHADER }; - virtual void logv(const char *p_format, va_list p_list, bool p_err) = 0; + virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0 = 0; virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR); - void logf(const char *p_format, ...); - void logf_error(const char *p_format, ...); + void logf(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; + void logf_error(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; virtual ~Logger(); }; @@ -64,7 +64,7 @@ public: class StdLogger : public Logger { public: - virtual void logv(const char *p_format, va_list p_list, bool p_err); + virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0; virtual ~StdLogger(); }; @@ -88,7 +88,7 @@ class RotatedFileLogger : public Logger { public: RotatedFileLogger(const String &p_base_path, int p_max_files = 10); - virtual void logv(const char *p_format, va_list p_list, bool p_err); + virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0; virtual ~RotatedFileLogger(); }; @@ -99,7 +99,7 @@ class CompositeLogger : public Logger { public: CompositeLogger(Vector<Logger *> p_loggers); - virtual void logv(const char *p_format, va_list p_list, bool p_err); + virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0; virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR); void add_logger(Logger *p_logger); diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index eec1c55744..5087a63b68 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -889,11 +889,11 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo if (buf) { encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); //for compatibility with the old format encode_uint32(np.get_subname_count(), buf + 4); - uint32_t flags = 0; + uint32_t np_flags = 0; if (np.is_absolute()) - flags |= 1; + np_flags |= 1; - encode_uint32(flags, buf + 8); + encode_uint32(np_flags, buf + 8); buf += 12; } diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 3aa1fcfd8f..d7bfdbbb37 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -224,7 +224,7 @@ Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size) uint32_t len = decode_uint32(lbuf); ERR_FAIL_COND_V(remaining < (int)len, ERR_UNAVAILABLE); - ERR_FAIL_COND_V(input_buffer.size() < len, ERR_UNAVAILABLE); + ERR_FAIL_COND_V(input_buffer.size() < (int)len, ERR_UNAVAILABLE); ring_buffer.read(lbuf, 4); //get rid of first 4 bytes ring_buffer.read(input_buffer.ptrw(), len); // read packet diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index e27dea37d6..42070cd132 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -106,7 +106,7 @@ StringName ResourceInteractiveLoaderBinary::_get_string() { uint32_t id = f->get_32(); if (id & 0x80000000) { uint32_t len = id & 0x7FFFFFFF; - if (len > str_buf.size()) { + if ((int)len > str_buf.size()) { str_buf.resize(len); } if (len == 0) @@ -296,9 +296,9 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_OBJECT: { - uint32_t type = f->get_32(); + uint32_t objtype = f->get_32(); - switch (type) { + switch (objtype) { case OBJECT_EMPTY: { //do none @@ -317,7 +317,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { case OBJECT_EXTERNAL_RESOURCE: { //old file format, still around for compatibility - String type = get_unicode_string(); + String exttype = get_unicode_string(); String path = get_unicode_string(); if (path.find("://") == -1 && path.is_rel_path()) { @@ -329,7 +329,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { path = remaps[path]; } - RES res = ResourceLoader::load(path, type); + RES res = ResourceLoader::load(path, exttype); if (res.is_null()) { WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data()); @@ -342,11 +342,11 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { int erindex = f->get_32(); if (erindex < 0 || erindex >= external_resources.size()) { - WARN_PRINT("Broken external resource! (index out of size"); + WARN_PRINT("Broken external resource! (index out of size)"); r_v = Variant(); } else { - String type = external_resources[erindex].type; + String exttype = external_resources[erindex].type; String path = external_resources[erindex].path; if (path.find("://") == -1 && path.is_rel_path()) { @@ -354,7 +354,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path)); } - RES res = ResourceLoader::load(path, type); + RES res = ResourceLoader::load(path, exttype); if (res.is_null()) { WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data()); @@ -1314,8 +1314,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } else { f->store_32(VARIANT_INT); - int val = p_property; - f->store_32(int32_t(val)); + f->store_32(int32_t(p_property)); } } break; @@ -1502,7 +1501,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia if (!resource_set.has(res)) { f->store_32(OBJECT_EMPTY); - ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_EXPLAIN("Resource was not pre cached for the resource section, most likely due to circular refedence."); ERR_FAIL(); } @@ -1651,6 +1650,10 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant return; if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) { + if (res->get_path() == path) { + ERR_PRINTS("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded."); + return; + } int idx = external_resources.size(); external_resources[res] = idx; return; @@ -1667,7 +1670,20 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant if (E->get().usage & PROPERTY_USAGE_STORAGE) { - _find_resources(res->get(E->get().name)); + Variant value = res->get(E->get().name); + if (E->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { + RES sres = value; + if (sres.is_valid()) { + NonPersistentKey npk; + npk.base = res; + npk.property = E->get().name; + non_persistent_map[npk] = sres; + resource_set.insert(sres); + saved_resources.push_back(sres); + } + } else { + _find_resources(value); + } } } @@ -1762,6 +1778,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p takeover_paths = false; local_path = p_path.get_base_dir(); + path = ProjectSettings::get_singleton()->localize_path(p_path); _find_resources(p_resource, true); @@ -1811,7 +1828,17 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p if ((F->get().usage & PROPERTY_USAGE_STORAGE)) { Property p; p.name_idx = get_string_index(F->get().name); - p.value = E->get()->get(F->get().name); + + if (F->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { + NonPersistentKey npk; + npk.base = E->get(); + npk.property = F->get().name; + if (non_persistent_map.has(npk)) { + p.value = non_persistent_map[npk]; + } + } else { + p.value = E->get()->get(F->get().name); + } Variant default_value = ClassDB::class_get_default_property_value(E->get()->get_class(), F->get().name); diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 8f2c48e745..a4894e4033 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -114,6 +114,7 @@ public: class ResourceFormatSaverBinaryInstance { String local_path; + String path; bool relative_paths; bool bundle_resources; @@ -123,6 +124,14 @@ class ResourceFormatSaverBinaryInstance { FileAccess *f; String magic; Set<RES> resource_set; + + struct NonPersistentKey { //for resource properties generated on the fly + RES base; + StringName property; + bool operator<(const NonPersistentKey &p_key) const { return base == p_key.base ? property < p_key.property : base < p_key.base; } + }; + + Map<NonPersistentKey, RES> non_persistent_map; Map<StringName, int> string_map; Vector<StringName> strings; diff --git a/core/io/resource_import.cpp b/core/io/resource_importer.cpp index 63dc0b6a26..b5fa412576 100644 --- a/core/io/resource_import.cpp +++ b/core/io/resource_importer.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* resource_import.cpp */ +/* resource_importer.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,11 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "resource_import.h" +#include "resource_importer.h" #include "core/os/os.h" #include "core/variant_parser.h" +bool ResourceFormatImporter::SortImporterByName::operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const { + return p_a->get_importer_name() < p_b->get_importer_name(); +} + Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid) const { Error err; @@ -90,6 +94,8 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy r_path_and_type.type = value; } else if (assign == "importer") { r_path_and_type.importer = value; + } else if (assign == "metadata") { + r_path_and_type.metadata = value; } else if (assign == "valid") { if (r_valid) { *r_valid = value; @@ -177,6 +183,11 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_ } } +bool ResourceFormatImporter::exists(const String &p_path) const { + + return FileAccess::exists(p_path + ".import"); +} + bool ResourceFormatImporter::recognize_path(const String &p_path, const String &p_for_type) const { return FileAccess::exists(p_path + ".import"); @@ -304,6 +315,18 @@ String ResourceFormatImporter::get_resource_type(const String &p_path) const { return pat.type; } +Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const { + PathAndType pat; + Error err = _get_path_and_type(p_path, pat); + + if (err != OK) { + + return Variant(); + } + + return pat.metadata; +} + void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { PathAndType pat; @@ -366,6 +389,40 @@ String ResourceFormatImporter::get_import_base_path(const String &p_for_file) co return "res://.import/" + p_for_file.get_file() + "-" + p_for_file.md5_text(); } +bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) const { + + bool valid = true; + PathAndType pat; + _get_path_and_type(p_path, pat, &valid); + + if (!valid) { + return false; + } + + for (int i = 0; i < importers.size(); i++) { + if (importers[i]->get_importer_name() == pat.importer) { + if (!importers[i]->are_import_settings_valid(p_path)) { //importer thinks this is not valid + return false; + } + } + } + + return true; +} + +String ResourceFormatImporter::get_import_settings_hash() const { + + Vector<Ref<ResourceImporter> > sorted_importers = importers; + + sorted_importers.sort_custom<SortImporterByName>(); + + String hash; + for (int i = 0; i < sorted_importers.size(); i++) { + hash += ":" + sorted_importers[i]->get_importer_name() + ":" + sorted_importers[i]->get_import_settings_string(); + } + return hash.md5_text(); +} + ResourceFormatImporter *ResourceFormatImporter::singleton = NULL; ResourceFormatImporter::ResourceFormatImporter() { diff --git a/core/io/resource_import.h b/core/io/resource_importer.h index 96dd7983e6..1c146c33d7 100644 --- a/core/io/resource_import.h +++ b/core/io/resource_importer.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* resource_import.h */ +/* resource_importer.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RESOURCE_IMPORT_H -#define RESOURCE_IMPORT_H +#ifndef RESOURCE_IMPORTER_H +#define RESOURCE_IMPORTER_H #include "core/io/resource_loader.h" @@ -43,12 +43,18 @@ class ResourceFormatImporter : public ResourceFormatLoader { String path; String type; String importer; + Variant metadata; }; Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = NULL) const; static ResourceFormatImporter *singleton; + //need them to stay in order to compute the settings hash + struct SortImporterByName { + bool operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const; + }; + Vector<Ref<ResourceImporter> > importers; public: @@ -59,8 +65,11 @@ public: virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; + virtual Variant get_resource_metadata(const String &p_path) const; virtual bool is_import_valid(const String &p_path) const; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); + virtual bool is_imported(const String &p_path) const { return recognize_path(p_path); } + virtual bool exists(const String &p_path) const; virtual bool can_be_imported(const String &p_path) const; virtual int get_import_order(const String &p_path) const; @@ -68,12 +77,17 @@ public: String get_internal_resource_path(const String &p_path) const; void get_internal_resource_path_list(const String &p_path, List<String> *r_paths); - void add_importer(const Ref<ResourceImporter> &p_importer) { importers.push_back(p_importer); } + void add_importer(const Ref<ResourceImporter> &p_importer) { + importers.push_back(p_importer); + } void remove_importer(const Ref<ResourceImporter> &p_importer) { importers.erase(p_importer); } Ref<ResourceImporter> get_importer_by_name(const String &p_name) const; Ref<ResourceImporter> get_importer_by_extension(const String &p_extension) const; void get_importers_for_extension(const String &p_extension, List<Ref<ResourceImporter> > *r_importers); + bool are_import_settings_valid(const String &p_path) const; + String get_import_settings_hash() const; + String get_import_base_path(const String &p_for_file) const; ResourceFormatImporter(); }; @@ -107,7 +121,9 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const = 0; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const = 0; - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL) = 0; + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL) = 0; + virtual bool are_import_settings_valid(const String &p_path) const { return true; } + virtual String get_import_settings_string() const { return String(); } }; -#endif // RESOURCE_IMPORT_H +#endif // RESOURCE_IMPORTER_H diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index b8bc24c1de..e4b694b64f 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -30,7 +30,7 @@ #include "resource_loader.h" -#include "core/io/resource_import.h" +#include "core/io/resource_importer.h" #include "core/os/file_access.h" #include "core/os/os.h" #include "core/path_remap.h" @@ -53,6 +53,12 @@ Error ResourceInteractiveLoader::wait() { return err; } +ResourceInteractiveLoader::~ResourceInteractiveLoader() { + if (path_loading != String()) { + ResourceLoader::_remove_from_loading_map_and_thread(path_loading, path_loading_thread); + } +} + bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_for_type) const { String extension = p_path.get_extension(); @@ -280,6 +286,63 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c return RES(); } +bool ResourceLoader::_add_to_loading_map(const String &p_path) { + + bool success; + if (loading_map_mutex) { + loading_map_mutex->lock(); + } + + LoadingMapKey key; + key.path = p_path; + key.thread = Thread::get_caller_id(); + + if (loading_map.has(key)) { + success = false; + } else { + loading_map[key] = true; + success = true; + } + + if (loading_map_mutex) { + loading_map_mutex->unlock(); + } + + return success; +} + +void ResourceLoader::_remove_from_loading_map(const String &p_path) { + if (loading_map_mutex) { + loading_map_mutex->lock(); + } + + LoadingMapKey key; + key.path = p_path; + key.thread = Thread::get_caller_id(); + + loading_map.erase(key); + + if (loading_map_mutex) { + loading_map_mutex->unlock(); + } +} + +void ResourceLoader::_remove_from_loading_map_and_thread(const String &p_path, Thread::ID p_thread) { + if (loading_map_mutex) { + loading_map_mutex->lock(); + } + + LoadingMapKey key; + key.path = p_path; + key.thread = p_thread; + + loading_map.erase(key); + + if (loading_map_mutex) { + loading_map_mutex->unlock(); + } +} + RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) { if (r_error) @@ -292,6 +355,15 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p local_path = ProjectSettings::get_singleton()->localize_path(p_path); if (!p_no_cache) { + + { + bool success = _add_to_loading_map(local_path); + if (!success) { + ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); + ERR_FAIL_V(RES()); + } + } + //lock first if possible if (ResourceCache::lock) { ResourceCache::lock->read_lock(); @@ -310,7 +382,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p if (ResourceCache::lock) { ResourceCache::lock->read_unlock(); } - print_verbose("Loading resource: " + local_path + " (cached)"); + _remove_from_loading_map(local_path); return res; } } @@ -322,12 +394,21 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p bool xl_remapped = false; String path = _path_remap(local_path, &xl_remapped); - ERR_FAIL_COND_V(path == "", RES()); + if (path == "") { + if (!p_no_cache) { + _remove_from_loading_map(local_path); + } + ERR_EXPLAIN("Remapping '" + local_path + "'failed."); + ERR_FAIL_V(RES()); + } print_verbose("Loading resource: " + path); RES res = _load(path, local_path, p_type_hint, p_no_cache, r_error); if (res.is_null()) { + if (!p_no_cache) { + _remove_from_loading_map(local_path); + } return RES(); } if (!p_no_cache) @@ -346,6 +427,10 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p } #endif + if (!p_no_cache) { + _remove_from_loading_map(local_path); + } + if (_loaded_callback) { _loaded_callback(res, p_path); } @@ -394,19 +479,37 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_ else local_path = ProjectSettings::get_singleton()->localize_path(p_path); - if (!p_no_cache && ResourceCache::has(local_path)) { + if (!p_no_cache) { - print_verbose("Loading resource: " + local_path + " (cached)"); - Ref<Resource> res_cached = ResourceCache::get(local_path); - Ref<ResourceInteractiveLoaderDefault> ril = Ref<ResourceInteractiveLoaderDefault>(memnew(ResourceInteractiveLoaderDefault)); + bool success = _add_to_loading_map(local_path); + if (!success) { + ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); + ERR_FAIL_V(RES()); + } - ril->resource = res_cached; - return ril; + if (ResourceCache::has(local_path)) { + + print_verbose("Loading resource: " + local_path + " (cached)"); + Ref<Resource> res_cached = ResourceCache::get(local_path); + Ref<ResourceInteractiveLoaderDefault> ril = Ref<ResourceInteractiveLoaderDefault>(memnew(ResourceInteractiveLoaderDefault)); + + ril->resource = res_cached; + ril->path_loading = local_path; + ril->path_loading_thread = Thread::get_caller_id(); + return ril; + } } bool xl_remapped = false; String path = _path_remap(local_path, &xl_remapped); - ERR_FAIL_COND_V(path == "", Ref<ResourceInteractiveLoader>()); + if (path == "") { + if (!p_no_cache) { + _remove_from_loading_map(local_path); + } + ERR_EXPLAIN("Remapping '" + local_path + "'failed."); + ERR_FAIL_V(RES()); + } + print_verbose("Loading resource: " + path); bool found = false; @@ -418,14 +521,22 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_ Ref<ResourceInteractiveLoader> ril = loader[i]->load_interactive(path, local_path, r_error); if (ril.is_null()) continue; - if (!p_no_cache) + if (!p_no_cache) { ril->set_local_path(local_path); + ril->path_loading = local_path; + ril->path_loading_thread = Thread::get_caller_id(); + } + if (xl_remapped) ril->set_translation_remapped(true); return ril; } + if (!p_no_cache) { + _remove_from_loading_map(local_path); + } + if (found) { ERR_EXPLAIN("Failed loading resource: " + path); } else { @@ -522,6 +633,31 @@ bool ResourceLoader::is_import_valid(const String &p_path) { return false; //not found } +bool ResourceLoader::is_imported(const String &p_path) { + + String path = _path_remap(p_path); + + String local_path; + if (path.is_rel_path()) + local_path = "res://" + path; + else + local_path = ProjectSettings::get_singleton()->localize_path(path); + + for (int i = 0; i < loader_count; i++) { + + if (!loader[i]->recognize_path(local_path)) + continue; + /* + if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) + continue; + */ + + return loader[i]->is_imported(p_path); + } + + return false; //not found +} + void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { String path = _path_remap(p_path); @@ -794,9 +930,9 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) { void ResourceLoader::remove_custom_resource_format_loader(String script_path) { - Ref<ResourceFormatLoader> loader = _find_custom_resource_format_loader(script_path); - if (loader.is_valid()) - remove_resource_format_loader(loader); + Ref<ResourceFormatLoader> custom_loader = _find_custom_resource_format_loader(script_path); + if (custom_loader.is_valid()) + remove_resource_format_loader(custom_loader); } void ResourceLoader::add_custom_loaders() { @@ -810,7 +946,7 @@ void ResourceLoader::add_custom_loaders() { for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) { StringName class_name = E->get(); - StringName base_class = ScriptServer::get_global_class_base(class_name); + StringName base_class = ScriptServer::get_global_class_native_base(class_name); if (base_class == custom_loader_base_class) { String path = ScriptServer::get_global_class_path(class_name); @@ -833,6 +969,27 @@ void ResourceLoader::remove_custom_loaders() { } } +Mutex *ResourceLoader::loading_map_mutex = NULL; +HashMap<ResourceLoader::LoadingMapKey, int, ResourceLoader::LoadingMapKeyHasher> ResourceLoader::loading_map; + +void ResourceLoader::initialize() { +#ifndef NO_THREADS + loading_map_mutex = Mutex::create(); +#endif +} + +void ResourceLoader::finalize() { +#ifndef NO_THREADS + const LoadingMapKey *K = NULL; + while ((K = loading_map.next(K))) { + ERR_PRINTS("Exited while resource is being loaded: " + K->path); + } + loading_map.clear(); + memdelete(loading_map_mutex); + loading_map_mutex = NULL; +#endif +} + ResourceLoadErrorNotify ResourceLoader::err_notify = NULL; void *ResourceLoader::err_notify_ud = NULL; diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 6c0e9d5554..ca7610a0d2 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -31,8 +31,8 @@ #ifndef RESOURCE_LOADER_H #define RESOURCE_LOADER_H +#include "core/os/thread.h" #include "core/resource.h" - /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -40,6 +40,9 @@ class ResourceInteractiveLoader : public Reference { GDCLASS(ResourceInteractiveLoader, Reference); + friend class ResourceLoader; + String path_loading; + Thread::ID path_loading_thread; protected: static void _bind_methods(); @@ -54,6 +57,7 @@ public: virtual Error wait(); ResourceInteractiveLoader() {} + ~ResourceInteractiveLoader(); }; class ResourceFormatLoader : public Reference { @@ -75,6 +79,7 @@ public: virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map); virtual bool is_import_valid(const String &p_path) const { return true; } + virtual bool is_imported(const String &p_path) const { return false; } virtual int get_import_order(const String &p_path) const { return 0; } virtual ~ResourceFormatLoader() {} @@ -110,12 +115,33 @@ class ResourceLoader { static SelfList<Resource>::List remapped_list; friend class ResourceFormatImporter; + friend class ResourceInteractiveLoader; //internal load function static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error); static ResourceLoadedCallback _loaded_callback; static Ref<ResourceFormatLoader> _find_custom_resource_format_loader(String path); + static Mutex *loading_map_mutex; + + //used to track paths being loaded in a thread, avoids cyclic recursion + struct LoadingMapKey { + String path; + Thread::ID thread; + bool operator==(const LoadingMapKey &p_key) const { + return (thread == p_key.thread && path == p_key.path); + } + }; + struct LoadingMapKeyHasher { + + static _FORCE_INLINE_ uint32_t hash(const LoadingMapKey &p_key) { return p_key.path.hash() + HashMapHasherDefault::hash(p_key.thread); } + }; + + static HashMap<LoadingMapKey, int, LoadingMapKeyHasher> loading_map; + + static bool _add_to_loading_map(const String &p_path); + static void _remove_from_loading_map(const String &p_path); + static void _remove_from_loading_map_and_thread(const String &p_path, Thread::ID p_thread); public: static Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); @@ -129,6 +155,7 @@ public: static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); static Error rename_dependencies(const String &p_path, const Map<String, String> &p_map); static bool is_import_valid(const String &p_path); + static bool is_imported(const String &p_path); static int get_import_order(const String &p_path); static void set_timestamp_on_load(bool p_timestamp) { timestamp_on_load = p_timestamp; } @@ -170,6 +197,9 @@ public: static void remove_custom_resource_format_loader(String script_path); static void add_custom_loaders(); static void remove_custom_loaders(); + + static void initialize(); + static void finalize(); }; #endif diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 279788796c..0cecca904d 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -233,9 +233,9 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) { void ResourceSaver::remove_custom_resource_format_saver(String script_path) { - Ref<ResourceFormatSaver> saver = _find_custom_resource_format_saver(script_path); - if (saver.is_valid()) - remove_resource_format_saver(saver); + Ref<ResourceFormatSaver> custom_saver = _find_custom_resource_format_saver(script_path); + if (custom_saver.is_valid()) + remove_resource_format_saver(custom_saver); } void ResourceSaver::add_custom_savers() { @@ -249,7 +249,7 @@ void ResourceSaver::add_custom_savers() { for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) { StringName class_name = E->get(); - StringName base_class = ScriptServer::get_global_class_base(class_name); + StringName base_class = ScriptServer::get_global_class_native_base(class_name); if (base_class == custom_saver_base_class) { String path = ScriptServer::get_global_class_path(class_name); |