summaryrefslogtreecommitdiff
path: root/core/io
diff options
context:
space:
mode:
Diffstat (limited to 'core/io')
-rw-r--r--core/io/config_file.cpp12
-rw-r--r--core/io/dir_access.cpp21
-rw-r--r--core/io/file_access.cpp37
-rw-r--r--core/io/file_access_compressed.cpp2
-rw-r--r--core/io/file_access_pack.cpp8
-rw-r--r--core/io/file_access_zip.cpp2
-rw-r--r--core/io/http_client.cpp7
-rw-r--r--core/io/http_client_tcp.cpp26
-rw-r--r--core/io/http_client_tcp.h1
-rw-r--r--core/io/image.cpp8
-rw-r--r--core/io/image_loader.cpp4
-rw-r--r--core/io/ip.cpp81
-rw-r--r--core/io/json.cpp11
-rw-r--r--core/io/logger.cpp10
-rw-r--r--core/io/logger.h4
-rw-r--r--core/io/marshalls.cpp39
-rw-r--r--core/io/marshalls.h2
-rw-r--r--core/io/multiplayer_api.cpp1110
-rw-r--r--core/io/multiplayer_api.h163
-rw-r--r--core/io/multiplayer_peer.cpp67
-rw-r--r--core/io/packed_data_container.cpp14
-rw-r--r--core/io/packet_peer.cpp46
-rw-r--r--core/io/packet_peer.h23
-rw-r--r--core/io/resource.cpp110
-rw-r--r--core/io/resource.h16
-rw-r--r--core/io/resource_format_binary.cpp253
-rw-r--r--core/io/resource_format_binary.h17
-rw-r--r--core/io/resource_importer.cpp44
-rw-r--r--core/io/resource_importer.h15
-rw-r--r--core/io/resource_loader.cpp222
-rw-r--r--core/io/resource_loader.h22
-rw-r--r--core/io/resource_saver.cpp54
-rw-r--r--core/io/resource_saver.h11
-rw-r--r--core/io/resource_uid.cpp266
-rw-r--r--core/io/resource_uid.h (renamed from core/io/multiplayer_peer.h)79
-rw-r--r--core/io/stream_peer.cpp57
-rw-r--r--core/io/stream_peer.h25
-rw-r--r--core/io/zip_io.cpp2
38 files changed, 1090 insertions, 1801 deletions
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index 10f68f3cef..49fa73dab2 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -40,8 +40,8 @@ PackedStringArray ConfigFile::_get_sections() const {
PackedStringArray arr;
arr.resize(s.size());
int idx = 0;
- for (const List<String>::Element *E = s.front(); E; E = E->next()) {
- arr.set(idx++, E->get());
+ for (const String &E : s) {
+ arr.set(idx++, E);
}
return arr;
@@ -53,8 +53,8 @@ PackedStringArray ConfigFile::_get_section_keys(const String &p_section) const {
PackedStringArray arr;
arr.resize(s.size());
int idx = 0;
- for (const List<String>::Element *E = s.front(); E; E = E->next()) {
- arr.set(idx++, E->get());
+ for (const String &E : s) {
+ arr.set(idx++, E);
}
return arr;
@@ -188,7 +188,7 @@ Error ConfigFile::_internal_save(FileAccess *file) {
for (OrderedHashMap<String, Variant>::Element F = E.get().front(); F; F = F.next()) {
String vstr;
VariantWriter::write_to_string(F.get(), vstr);
- file->store_string(F.key() + "=" + vstr + "\n");
+ file->store_string(F.key().property_name_encode() + "=" + vstr + "\n");
}
}
@@ -315,6 +315,8 @@ void ConfigFile::_bind_methods() {
ClassDB::bind_method(D_METHOD("parse", "data"), &ConfigFile::parse);
ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save);
+ BIND_METHOD_ERR_RETURN_DOC("load", ERR_FILE_CANT_OPEN);
+
ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted);
ClassDB::bind_method(D_METHOD("load_encrypted_pass", "path", "password"), &ConfigFile::load_encrypted_pass);
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index dfba00067f..3bff0a3fd5 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -93,8 +93,8 @@ static Error _erase_recursive(DirAccess *da) {
da->list_dir_end();
- for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
- Error err = da->change_dir(E->get());
+ for (const String &E : dirs) {
+ Error err = da->change_dir(E);
if (err == OK) {
err = _erase_recursive(da);
if (err) {
@@ -105,7 +105,7 @@ static Error _erase_recursive(DirAccess *da) {
if (err) {
return err;
}
- err = da->remove(da->get_current_dir().plus_file(E->get()));
+ err = da->remove(da->get_current_dir().plus_file(E));
if (err) {
return err;
}
@@ -114,8 +114,8 @@ static Error _erase_recursive(DirAccess *da) {
}
}
- for (List<String>::Element *E = files.front(); E; E = E->next()) {
- Error err = da->remove(da->get_current_dir().plus_file(E->get()));
+ for (const String &E : files) {
+ Error err = da->remove(da->get_current_dir().plus_file(E));
if (err) {
return err;
}
@@ -135,7 +135,7 @@ Error DirAccess::make_dir_recursive(String p_dir) {
String full_dir;
- if (p_dir.is_rel_path()) {
+ if (p_dir.is_relative_path()) {
//append current
full_dir = get_current_dir().plus_file(p_dir);
@@ -345,7 +345,7 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag
dirs.push_back(n);
} else {
const String &rel_path = n;
- if (!n.is_rel_path()) {
+ if (!n.is_relative_path()) {
list_dir_end();
return ERR_BUG;
}
@@ -362,16 +362,15 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag
list_dir_end();
- for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
- String rel_path = E->get();
+ for (const String &rel_path : dirs) {
String target_dir = p_to + rel_path;
if (!p_target_da->dir_exists(target_dir)) {
Error err = p_target_da->make_dir(target_dir);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'.");
}
- Error err = change_dir(E->get());
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'.");
+ Error err = change_dir(rel_path);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + rel_path + "'.");
err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links);
if (err) {
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index d21c0bd9a2..e6e79dff8a 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -316,52 +316,53 @@ String FileAccess::get_line() const {
}
Vector<String> FileAccess::get_csv_line(const String &p_delim) const {
- ERR_FAIL_COND_V(p_delim.length() != 1, Vector<String>());
+ ERR_FAIL_COND_V_MSG(p_delim.length() != 1, Vector<String>(), "Only single character delimiters are supported to parse CSV lines.");
+ ERR_FAIL_COND_V_MSG(p_delim[0] == '"', Vector<String>(), "The double quotation mark character (\") is not supported as a delimiter for CSV lines.");
- String l;
+ String line;
+
+ // CSV can support entries with line breaks as long as they are enclosed
+ // in double quotes. So our "line" might be more than a single line in the
+ // text file.
int qc = 0;
do {
if (eof_reached()) {
break;
}
-
- l += get_line() + "\n";
+ line += get_line() + "\n";
qc = 0;
- for (int i = 0; i < l.length(); i++) {
- if (l[i] == '"') {
+ for (int i = 0; i < line.length(); i++) {
+ if (line[i] == '"') {
qc++;
}
}
-
} while (qc % 2);
- l = l.substr(0, l.length() - 1);
+ // Remove the extraneous newline we've added above.
+ line = line.substr(0, line.length() - 1);
Vector<String> strings;
bool in_quote = false;
String current;
- for (int i = 0; i < l.length(); i++) {
- char32_t c = l[i];
- char32_t s[2] = { 0, 0 };
-
+ for (int i = 0; i < line.length(); i++) {
+ char32_t c = line[i];
+ // A delimiter ends the current entry, unless it's in a quoted string.
if (!in_quote && c == p_delim[0]) {
strings.push_back(current);
current = String();
} else if (c == '"') {
- if (l[i + 1] == '"' && in_quote) {
- s[0] = '"';
- current += s;
+ // Doubled quotes are escapes for intentional quotes in the string.
+ if (line[i + 1] == '"' && in_quote) {
+ current += '"';
i++;
} else {
in_quote = !in_quote;
}
} else {
- s[0] = c;
- current += s;
+ current += c;
}
}
-
strings.push_back(current);
return strings;
diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp
index e54c947340..df631053b8 100644
--- a/core/io/file_access_compressed.cpp
+++ b/core/io/file_access_compressed.cpp
@@ -233,7 +233,7 @@ uint64_t FileAccessCompressed::get_position() const {
if (writing) {
return write_pos;
} else {
- return read_block * block_size + read_pos;
+ return (uint64_t)read_block * block_size + read_pos;
}
}
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 7b43daf9c0..b2832b2a75 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -110,8 +110,8 @@ PackedData::PackedData() {
}
void PackedData::_free_packed_dirs(PackedDir *p_dir) {
- for (Map<String, PackedDir *>::Element *E = p_dir->subdirs.front(); E; E = E->next()) {
- _free_packed_dirs(E->get());
+ for (const KeyValue<String, PackedDir *> &E : p_dir->subdirs) {
+ _free_packed_dirs(E.value);
}
memdelete(p_dir);
}
@@ -395,8 +395,8 @@ Error DirAccessPack::list_dir_begin() {
list_dirs.clear();
list_files.clear();
- for (Map<String, PackedData::PackedDir *>::Element *E = current->subdirs.front(); E; E = E->next()) {
- list_dirs.push_back(E->key());
+ for (const KeyValue<String, PackedData::PackedDir *> &E : current->subdirs) {
+ list_dirs.push_back(E.key);
}
for (Set<String>::Element *E = current->files.front(); E; E = E->next()) {
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index b5c882e9ce..53bf7456e6 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -99,7 +99,7 @@ static int godot_testerror(voidpf opaque, voidpf stream) {
}
static voidpf godot_alloc(voidpf opaque, uInt items, uInt size) {
- return memalloc(items * size);
+ return memalloc((size_t)items * size);
}
static void godot_free(voidpf opaque, voidpf address) {
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 8000dd4290..5c1352c1b6 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -93,8 +93,7 @@ Dictionary HTTPClient::_get_response_headers_as_dictionary() {
List<String> rh;
get_response_headers(&rh);
Dictionary ret;
- for (const List<String>::Element *E = rh.front(); E; E = E->next()) {
- const String &s = E->get();
+ for (const String &s : rh) {
int sp = s.find(":");
if (sp == -1) {
continue;
@@ -113,8 +112,8 @@ PackedStringArray HTTPClient::_get_response_headers() {
PackedStringArray ret;
ret.resize(rh.size());
int idx = 0;
- for (const List<String>::Element *E = rh.front(); E; E = E->next()) {
- ret.set(idx++, E->get());
+ for (const String &E : rh) {
+ ret.set(idx++, E);
}
return ret;
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index f291086808..b3d35b3603 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -45,6 +45,8 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
conn_port = p_port;
conn_host = p_host;
+ ip_candidates.clear();
+
ssl = p_ssl;
ssl_verify_host = p_verify_host;
@@ -234,6 +236,7 @@ void HTTPClientTCP::close() {
resolving = IP::RESOLVER_INVALID_ID;
}
+ ip_candidates.clear();
response_headers.clear();
response_str.clear();
body_size = -1;
@@ -256,10 +259,17 @@ Error HTTPClientTCP::poll() {
return OK; // Still resolving
case IP::RESOLVER_STATUS_DONE: {
- IPAddress host = IP::get_singleton()->get_resolve_item_address(resolving);
- Error err = tcp_connection->connect_to_host(host, conn_port);
+ ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolving);
IP::get_singleton()->erase_resolve_item(resolving);
resolving = IP::RESOLVER_INVALID_ID;
+
+ Error err = ERR_BUG; // Should be at least one entry.
+ while (ip_candidates.size() > 0) {
+ err = tcp_connection->connect_to_host(ip_candidates.front(), conn_port);
+ if (err == OK) {
+ break;
+ }
+ }
if (err) {
status = STATUS_CANT_CONNECT;
return err;
@@ -313,6 +323,7 @@ Error HTTPClientTCP::poll() {
if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
// Handshake has been successful
handshaking = false;
+ ip_candidates.clear();
status = STATUS_CONNECTED;
return OK;
} else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
@@ -323,15 +334,24 @@ Error HTTPClientTCP::poll() {
}
// ... we will need to poll more for handshake to finish
} else {
+ ip_candidates.clear();
status = STATUS_CONNECTED;
}
return OK;
} break;
case StreamPeerTCP::STATUS_ERROR:
case StreamPeerTCP::STATUS_NONE: {
+ Error err = ERR_CANT_CONNECT;
+ while (ip_candidates.size() > 0) {
+ tcp_connection->disconnect_from_host();
+ err = tcp_connection->connect_to_host(ip_candidates.pop_front(), conn_port);
+ if (err == OK) {
+ return OK;
+ }
+ }
close();
status = STATUS_CANT_CONNECT;
- return ERR_CANT_CONNECT;
+ return err;
} break;
}
} break;
diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h
index e178399fbe..170afb551c 100644
--- a/core/io/http_client_tcp.h
+++ b/core/io/http_client_tcp.h
@@ -37,6 +37,7 @@ class HTTPClientTCP : public HTTPClient {
private:
Status status = STATUS_DISCONNECTED;
IP::ResolverID resolving = IP::RESOLVER_INVALID_ID;
+ Array ip_candidates;
int conn_port = -1;
String conn_host;
bool ssl = false;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 3112dd217f..c70f4b86bd 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -2506,7 +2506,7 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po
clipped_src_rect.position.y = ABS(p_dest.y);
}
- if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
+ if (clipped_src_rect.has_no_area()) {
return;
}
@@ -2561,7 +2561,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co
clipped_src_rect.position.y = ABS(p_dest.y);
}
- if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
+ if (clipped_src_rect.has_no_area()) {
return;
}
@@ -2615,7 +2615,7 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P
clipped_src_rect.position.y = ABS(p_dest.y);
}
- if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
+ if (clipped_src_rect.has_no_area()) {
return;
}
@@ -2664,7 +2664,7 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c
clipped_src_rect.position.y = ABS(p_dest.y);
}
- if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) {
+ if (clipped_src_rect.has_no_area()) {
return;
}
diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp
index b45e9d26b1..b9fc416f65 100644
--- a/core/io/image_loader.cpp
+++ b/core/io/image_loader.cpp
@@ -35,8 +35,8 @@
bool ImageFormatLoader::recognize(const String &p_extension) const {
List<String> extensions;
get_recognized_extensions(&extensions);
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- if (E->get().nocasecmp_to(p_extension) == 0) {
+ for (const String &E : extensions) {
+ if (E.nocasecmp_to(p_extension) == 0) {
return true;
}
}
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index 001b1c4757..68b4e4b354 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -83,8 +83,23 @@ struct _IP_ResolverPrivate {
continue;
}
- IP::get_singleton()->_resolve_hostname(queue[i].response, queue[i].hostname, queue[i].type);
- queue[i].status.set(queue[i].response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE);
+ mutex.lock();
+ List<IPAddress> response;
+ String hostname = queue[i].hostname;
+ IP::Type type = queue[i].type;
+ mutex.unlock();
+
+ // We should not lock while resolving the hostname,
+ // only when modifying the queue.
+ IP::get_singleton()->_resolve_hostname(response, hostname, type);
+
+ MutexLock lock(mutex);
+ // Could have been completed by another function, or deleted.
+ if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) {
+ continue;
+ }
+ queue[i].response = response;
+ queue[i].status.set(response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE);
}
}
@@ -93,8 +108,6 @@ struct _IP_ResolverPrivate {
while (!ipr->thread_abort) {
ipr->sem.wait();
-
- MutexLock lock(ipr->mutex);
ipr->resolve_queues();
}
}
@@ -107,17 +120,23 @@ struct _IP_ResolverPrivate {
};
IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) {
- MutexLock lock(resolver->mutex);
-
List<IPAddress> res;
-
String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
+
+ resolver->mutex.lock();
if (resolver->cache.has(key)) {
res = resolver->cache[key];
} else {
+ // This should be run unlocked so the resolver thread can keep
+ // resolving other requests.
+ resolver->mutex.unlock();
_resolve_hostname(res, p_hostname, p_type);
+ resolver->mutex.lock();
+ // We might be overriding another result, but we don't care (they are the
+ // same hostname).
resolver->cache[key] = res;
}
+ resolver->mutex.unlock();
for (int i = 0; i < res.size(); ++i) {
if (res[i].is_valid()) {
@@ -128,14 +147,23 @@ IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) {
}
Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) {
- MutexLock lock(resolver->mutex);
-
+ List<IPAddress> res;
String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
- if (!resolver->cache.has(key)) {
- _resolve_hostname(resolver->cache[key], p_hostname, p_type);
- }
- List<IPAddress> res = resolver->cache[key];
+ resolver->mutex.lock();
+ if (resolver->cache.has(key)) {
+ res = resolver->cache[key];
+ } else {
+ // This should be run unlocked so the resolver thread can keep resolving
+ // other requests.
+ resolver->mutex.unlock();
+ _resolve_hostname(res, p_hostname, p_type);
+ resolver->mutex.lock();
+ // We might be overriding another result, but we don't care (they are the
+ // same hostname).
+ resolver->cache[key] = res;
+ }
+ resolver->mutex.unlock();
Array result;
for (int i = 0; i < res.size(); ++i) {
@@ -178,13 +206,12 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ
IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const {
ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP::RESOLVER_STATUS_NONE);
- MutexLock lock(resolver->mutex);
-
- if (resolver->queue[p_id].status.get() == IP::RESOLVER_STATUS_NONE) {
+ IP::ResolverStatus res = resolver->queue[p_id].status.get();
+ if (res == IP::RESOLVER_STATUS_NONE) {
ERR_PRINT("Condition status == IP::RESOLVER_STATUS_NONE");
return IP::RESOLVER_STATUS_NONE;
}
- return resolver->queue[p_id].status.get();
+ return res;
}
IPAddress IP::get_resolve_item_address(ResolverID p_id) const {
@@ -230,8 +257,6 @@ Array IP::get_resolve_item_addresses(ResolverID p_id) const {
void IP::erase_resolve_item(ResolverID p_id) {
ERR_FAIL_INDEX(p_id, IP::RESOLVER_MAX_QUERIES);
- MutexLock lock(resolver->mutex);
-
resolver->queue[p_id].status.set(IP::RESOLVER_STATUS_NONE);
}
@@ -252,8 +277,8 @@ Array IP::_get_local_addresses() const {
Array addresses;
List<IPAddress> ip_addresses;
get_local_addresses(&ip_addresses);
- for (List<IPAddress>::Element *E = ip_addresses.front(); E; E = E->next()) {
- addresses.push_back(E->get());
+ for (const IPAddress &E : ip_addresses) {
+ addresses.push_back(E);
}
return addresses;
@@ -263,16 +288,16 @@ Array IP::_get_local_interfaces() const {
Array results;
Map<String, Interface_Info> interfaces;
get_local_interfaces(&interfaces);
- for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) {
- Interface_Info &c = E->get();
+ for (KeyValue<String, Interface_Info> &E : interfaces) {
+ Interface_Info &c = E.value;
Dictionary rc;
rc["name"] = c.name;
rc["friendly"] = c.name_friendly;
rc["index"] = c.index;
Array ips;
- for (const List<IPAddress>::Element *F = c.ip_addresses.front(); F; F = F->next()) {
- ips.push_front(F->get());
+ for (const IPAddress &F : c.ip_addresses) {
+ ips.push_front(F);
}
rc["addresses"] = ips;
@@ -285,9 +310,9 @@ Array IP::_get_local_interfaces() const {
void IP::get_local_addresses(List<IPAddress> *r_addresses) const {
Map<String, Interface_Info> interfaces;
get_local_interfaces(&interfaces);
- for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) {
- for (const List<IPAddress>::Element *F = E->get().ip_addresses.front(); F; F = F->next()) {
- r_addresses->push_front(F->get());
+ for (const KeyValue<String, Interface_Info> &E : interfaces) {
+ for (const IPAddress &F : E.value.ip_addresses) {
+ r_addresses->push_front(F);
}
}
}
diff --git a/core/io/json.cpp b/core/io/json.cpp
index b3a2498212..5823afbdcd 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -121,14 +121,17 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
keys.sort();
}
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- if (E != keys.front()) {
+ bool first_key = true;
+ for (const Variant &E : keys) {
+ if (first_key) {
+ first_key = false;
+ } else {
s += ",";
s += end_statement;
}
- s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(String(E->get()), p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
+ s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(String(E), p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
s += colon;
- s += _stringify(d[E->get()], p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
+ s += _stringify(d[E], p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
}
s += end_statement + _make_indent(p_indent, p_cur_indent) + "}";
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index 09539f716c..b68a8b20a5 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -50,7 +50,7 @@ void Logger::set_flush_stdout_on_print(bool value) {
_flush_stdout_on_print = value;
}
-void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {
if (!should_log(true)) {
return;
}
@@ -81,7 +81,11 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c
err_details = p_code;
}
- logf_error("%s: %s\n", err_type, err_details);
+ if (p_editor_notify) {
+ logf_error("%s: %s\n", err_type, err_details);
+ } else {
+ logf_error("USER %s: %s\n", err_type, err_details);
+ }
logf_error(" at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code);
}
@@ -256,7 +260,7 @@ void CompositeLogger::logv(const char *p_format, va_list p_list, bool p_err) {
}
}
-void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {
if (!should_log(true)) {
return;
}
diff --git a/core/io/logger.h b/core/io/logger.h
index ccf68562d6..f244f70e7e 100644
--- a/core/io/logger.h
+++ b/core/io/logger.h
@@ -54,7 +54,7 @@ public:
static void set_flush_stdout_on_print(bool value);
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);
+ virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR);
void logf(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
void logf_error(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
@@ -103,7 +103,7 @@ public:
CompositeLogger(Vector<Logger *> p_loggers);
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);
+ virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type = ERR_ERROR);
void add_logger(Logger *p_logger);
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index f342db2dad..e7d5b78d14 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -746,7 +746,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::PACKED_INT64_ARRAY: {
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
- int64_t count = decode_uint64(buf);
+ int32_t count = decode_uint32(buf);
buf += 4;
len -= 4;
ERR_FAIL_MUL_OF(count, 8, ERR_INVALID_DATA);
@@ -795,7 +795,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::PACKED_FLOAT64_ARRAY: {
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
- int64_t count = decode_uint64(buf);
+ int32_t count = decode_uint32(buf);
buf += 4;
len -= 4;
ERR_FAIL_MUL_OF(count, 8, ERR_INVALID_DATA);
@@ -804,7 +804,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Vector<double> data;
if (count) {
- //const double*rbuf=(const double*)buf;
data.resize(count);
double *w = data.ptrw();
for (int64_t i = 0; i < count; i++) {
@@ -1031,7 +1030,8 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) {
}
}
-Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects) {
+Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects, int p_depth) {
+ ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Potential inifite recursion detected. Bailing.");
uint8_t *buf = r_buffer;
r_len = 0;
@@ -1358,8 +1358,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
obj->get_property_list(&props);
int pc = 0;
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : props) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
pc++;
@@ -1372,18 +1372,16 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len += 4;
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : props) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- _encode_string(E->get().name, buf, r_len);
+ _encode_string(E.name, buf, r_len);
int len;
- Error err = encode_variant(obj->get(E->get().name), buf, len, p_full_objects);
- if (err) {
- return err;
- }
+ Error err = encode_variant(obj->get(E.name), buf, len, p_full_objects, p_depth + 1);
+ ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
if (buf) {
@@ -1418,7 +1416,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
List<Variant> keys;
d.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ for (const Variant &E : keys) {
/*
CharString utf8 = E->->utf8();
@@ -1433,15 +1431,17 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len++; //pad
*/
int len;
- encode_variant(E->get(), buf, len, p_full_objects);
+ Error err = encode_variant(E, buf, len, p_full_objects, p_depth + 1);
+ ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
if (buf) {
buf += len;
}
- Variant *v = d.getptr(E->get());
+ Variant *v = d.getptr(E);
ERR_FAIL_COND_V(!v, ERR_BUG);
- encode_variant(*v, buf, len, p_full_objects);
+ err = encode_variant(*v, buf, len, p_full_objects, p_depth + 1);
+ ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
if (buf) {
@@ -1462,7 +1462,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
for (int i = 0; i < v.size(); i++) {
int len;
- encode_variant(v.get(i), buf, len, p_full_objects);
+ Error err = encode_variant(v.get(i), buf, len, p_full_objects, p_depth + 1);
+ ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
if (buf) {
@@ -1517,7 +1518,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
int datasize = sizeof(int64_t);
if (buf) {
- encode_uint64(datalen, buf);
+ encode_uint32(datalen, buf);
buf += 4;
const int64_t *r = data.ptr();
for (int64_t i = 0; i < datalen; i++) {
diff --git a/core/io/marshalls.h b/core/io/marshalls.h
index 3ebed914a3..05804d5a46 100644
--- a/core/io/marshalls.h
+++ b/core/io/marshalls.h
@@ -213,6 +213,6 @@ public:
};
Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len = nullptr, bool p_allow_objects = false);
-Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false);
+Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false, int p_depth = 0);
#endif // MARSHALLS_H
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
deleted file mode 100644
index e99c60613a..0000000000
--- a/core/io/multiplayer_api.cpp
+++ /dev/null
@@ -1,1110 +0,0 @@
-/*************************************************************************/
-/* multiplayer_api.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "multiplayer_api.h"
-
-#include "core/debugger/engine_debugger.h"
-#include "core/io/marshalls.h"
-#include "scene/main/node.h"
-
-#include <stdint.h>
-
-#define NODE_ID_COMPRESSION_SHIFT 3
-#define NAME_ID_COMPRESSION_SHIFT 5
-#define BYTE_ONLY_OR_NO_ARGS_SHIFT 6
-
-#ifdef DEBUG_ENABLED
-#include "core/os/os.h"
-#endif
-
-String _get_rpc_md5(const Node *p_node) {
- String rpc_list;
- const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods();
- for (int i = 0; i < node_config.size(); i++) {
- rpc_list += String(node_config[i].name);
- }
- if (p_node->get_script_instance()) {
- const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
- for (int i = 0; i < script_config.size(); i++) {
- rpc_list += String(script_config[i].name);
- }
- }
- return rpc_list.md5_text();
-}
-
-const MultiplayerAPI::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) {
- const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods();
- for (int i = 0; i < node_config.size(); i++) {
- if (node_config[i].name == p_method) {
- r_id = ((uint16_t)i) & (1 << 15);
- return node_config[i];
- }
- }
- if (p_node->get_script_instance()) {
- const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
- for (int i = 0; i < script_config.size(); i++) {
- if (script_config[i].name == p_method) {
- r_id = (uint16_t)i;
- return script_config[i];
- }
- }
- }
- return MultiplayerAPI::RPCConfig();
-}
-
-const MultiplayerAPI::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) {
- Vector<MultiplayerAPI::RPCConfig> config;
- uint16_t id = p_id;
- if (id & (1 << 15)) {
- id = id & ~(1 << 15);
- config = p_node->get_node_rpc_methods();
- } else if (p_node->get_script_instance()) {
- config = p_node->get_script_instance()->get_rpc_methods();
- }
- if (id < config.size()) {
- return config[p_id];
- }
- return MultiplayerAPI::RPCConfig();
-}
-
-_FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) {
- switch (mode) {
- case MultiplayerAPI::RPC_MODE_DISABLED: {
- return false;
- } break;
- case MultiplayerAPI::RPC_MODE_REMOTE: {
- return true;
- } break;
- case MultiplayerAPI::RPC_MODE_MASTER: {
- return p_node->is_network_master();
- } break;
- case MultiplayerAPI::RPC_MODE_PUPPET: {
- return !p_node->is_network_master() && p_remote_id == p_node->get_network_master();
- } break;
- }
-
- return false;
-}
-
-void MultiplayerAPI::poll() {
- if (!network_peer.is_valid() || network_peer->get_connection_status() == MultiplayerPeer::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!");
- break; // Something is wrong!
- }
-
- 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();
- packet_cache.clear();
- last_send_cache_id = 1;
-}
-
-void MultiplayerAPI::set_root_node(Node *p_node) {
- root_node = p_node;
-}
-
-Node *MultiplayerAPI::get_root_node() {
- return root_node;
-}
-
-void MultiplayerAPI::set_network_peer(const Ref<MultiplayerPeer> &p_peer) {
- if (p_peer == network_peer) {
- return; // Nothing to do
- }
-
- ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED,
- "Supplied MultiplayerPeer must be connecting or connected.");
-
- if (network_peer.is_valid()) {
- network_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer));
- network_peer->disconnect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer));
- network_peer->disconnect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server));
- network_peer->disconnect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed));
- network_peer->disconnect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected));
- clear();
- }
-
- network_peer = p_peer;
-
- if (network_peer.is_valid()) {
- network_peer->connect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer));
- network_peer->connect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer));
- network_peer->connect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server));
- network_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed));
- network_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected));
- }
-}
-
-Ref<MultiplayerPeer> MultiplayerAPI::get_network_peer() const {
- return network_peer;
-}
-
-#ifdef DEBUG_ENABLED
-void _profile_node_data(const String &p_what, ObjectID p_id) {
- if (EngineDebugger::is_profiling("multiplayer")) {
- Array values;
- values.push_back("node");
- values.push_back(p_id);
- values.push_back(p_what);
- EngineDebugger::profiler_add_frame_data("multiplayer", values);
- }
-}
-
-void _profile_bandwidth_data(const String &p_inout, int p_size) {
- if (EngineDebugger::is_profiling("multiplayer")) {
- Array values;
- values.push_back("bandwidth");
- values.push_back(p_inout);
- values.push_back(OS::get_singleton()->get_ticks_msec());
- values.push_back(p_size);
- EngineDebugger::profiler_add_frame_data("multiplayer", values);
- }
-}
-#endif
-
-// Returns the packet size stripping the node path added when the node is not yet cached.
-int get_packet_len(uint32_t p_node_target, int p_packet_len) {
- if (p_node_target & 0x80000000) {
- int ofs = p_node_target & 0x7FFFFFFF;
- return p_packet_len - (p_packet_len - ofs);
- } else {
- return p_packet_len;
- }
-}
-
-void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it.");
- ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
-
-#ifdef DEBUG_ENABLED
- _profile_bandwidth_data("in", p_packet_len);
-#endif
-
- // Extract the `packet_type` from the LSB three bits:
- uint8_t packet_type = p_packet[0] & 7;
-
- 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: {
- // Extract packet meta
- int packet_min_size = 1;
- int name_id_offset = 1;
- ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
- // Compute the meta size, which depends on the compression level.
- int node_id_compression = (p_packet[0] & 24) >> NODE_ID_COMPRESSION_SHIFT;
- int name_id_compression = (p_packet[0] & 32) >> NAME_ID_COMPRESSION_SHIFT;
-
- switch (node_id_compression) {
- case NETWORK_NODE_ID_COMPRESSION_8:
- packet_min_size += 1;
- name_id_offset += 1;
- break;
- case NETWORK_NODE_ID_COMPRESSION_16:
- packet_min_size += 2;
- name_id_offset += 2;
- break;
- case NETWORK_NODE_ID_COMPRESSION_32:
- packet_min_size += 4;
- name_id_offset += 4;
- break;
- default:
- ERR_FAIL_MSG("Was not possible to extract the node id compression mode.");
- }
- switch (name_id_compression) {
- case NETWORK_NAME_ID_COMPRESSION_8:
- packet_min_size += 1;
- break;
- case NETWORK_NAME_ID_COMPRESSION_16:
- packet_min_size += 2;
- break;
- default:
- ERR_FAIL_MSG("Was not possible to extract the name id compression mode.");
- }
- ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
-
- uint32_t node_target = 0;
- switch (node_id_compression) {
- case NETWORK_NODE_ID_COMPRESSION_8:
- node_target = p_packet[1];
- break;
- case NETWORK_NODE_ID_COMPRESSION_16:
- node_target = decode_uint16(p_packet + 1);
- break;
- case NETWORK_NODE_ID_COMPRESSION_32:
- node_target = decode_uint32(p_packet + 1);
- break;
- default:
- // Unreachable, checked before.
- CRASH_NOW();
- }
-
- Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
- ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found.");
-
- uint16_t name_id = 0;
- switch (name_id_compression) {
- case NETWORK_NAME_ID_COMPRESSION_8:
- name_id = p_packet[name_id_offset];
- break;
- case NETWORK_NAME_ID_COMPRESSION_16:
- name_id = decode_uint16(p_packet + name_id_offset);
- break;
- default:
- // Unreachable, checked before.
- CRASH_NOW();
- }
-
- const int packet_len = get_packet_len(node_target, p_packet_len);
- _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
- } break;
-
- case NETWORK_COMMAND_RAW: {
- _process_raw(p_from, p_packet, p_packet_len);
- } break;
- }
-}
-
-Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
- Node *node = nullptr;
-
- if (p_node_target & 0x80000000) {
- // Use full path (not cached yet).
-
- int ofs = p_node_target & 0x7FFFFFFF;
-
- ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared.");
-
- 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_PRINT("Failed to get path from RPC: " + String(np) + ".");
- }
- } else {
- // Use cached path.
- int id = p_node_target;
-
- Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
- ERR_FAIL_COND_V_MSG(!E, nullptr, "Invalid packet received. Requests invalid peer cache.");
-
- Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id);
- ERR_FAIL_COND_V_MSG(!F, nullptr, "Invalid packet received. Unabled to find requested cached node.");
-
- PathGetCache::NodeInfo *ni = &F->get();
- // Do proper caching later.
-
- node = root_node->get_node(ni->path);
- if (!node) {
- ERR_PRINT("Failed to get cached path from RPC: " + String(ni->path) + ".");
- }
- }
- return node;
-}
-
-void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
- ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small.");
-
- // Check that remote can call the RPC on this node.
- const RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id);
- ERR_FAIL_COND(config.name == StringName());
-
- bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from);
- ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
-
- int argc = 0;
- bool byte_only = false;
-
- const bool byte_only_or_no_args = ((p_packet[0] & 64) >> BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1;
- if (byte_only_or_no_args) {
- if (p_offset < p_packet_len) {
- // This packet contains only bytes.
- argc = 1;
- byte_only = true;
- } else {
- // This rpc calls a method without parameters.
- }
- } else {
- // Normal variant, takes the argument count from the packet.
- ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
- argc = p_packet[p_offset];
- p_offset += 1;
- }
-
- Vector<Variant> args;
- Vector<const Variant *> argp;
- args.resize(argc);
- argp.resize(argc);
-
-#ifdef DEBUG_ENABLED
- _profile_node_data("in_rpc", p_node->get_instance_id());
-#endif
-
- if (byte_only) {
- Vector<uint8_t> pure_data;
- const int len = p_packet_len - p_offset;
- pure_data.resize(len);
- memcpy(pure_data.ptrw(), &p_packet[p_offset], len);
- args.write[0] = pure_data;
- argp.write[0] = &args[0];
- p_offset += len;
- } else {
- for (int i = 0; i < argc; i++) {
- ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
-
- int vlen;
- Error err = _decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
- ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument.");
-
- argp.write[i] = &args[i];
- p_offset += vlen;
- }
- }
-
- Callable::CallError ce;
-
- p_node->call(config.name, (const Variant **)argp.ptr(), argc, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce);
- error = "RPC - " + error;
- ERR_PRINT(error);
- }
-}
-
-void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
- int ofs = 1;
-
- String methods_md5;
- methods_md5.parse_utf8((const char *)(p_packet + ofs), 32);
- ofs += 33;
-
- int id = decode_uint32(&p_packet[ofs]);
- ofs += 4;
-
- String paths;
- paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
-
- NodePath path = paths;
-
- if (!path_get_cache.has(p_from)) {
- path_get_cache[p_from] = PathGetCache();
- }
-
- Node *node = root_node->get_node(path);
- ERR_FAIL_COND(node == nullptr);
- const bool valid_rpc_checksum = _get_rpc_md5(node) == methods_md5;
- if (valid_rpc_checksum == false) {
- ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
- }
-
- PathGetCache::NodeInfo ni;
- ni.path = path;
-
- path_get_cache[p_from].nodes[id] = ni;
-
- // Encode path to send ack.
- CharString pname = String(path).utf8();
- int len = encode_cstring(pname.get_data(), nullptr);
-
- Vector<uint8_t> packet;
-
- packet.resize(1 + 1 + len);
- packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH;
- packet.write[1] = valid_rpc_checksum;
- encode_cstring(pname.get_data(), &packet.write[2]);
-
- network_peer->set_transfer_mode(MultiplayerPeer::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) {
- ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small.");
-
- const bool valid_rpc_checksum = p_packet[1];
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2);
-
- NodePath path = paths;
-
- if (valid_rpc_checksum == false) {
- ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
- }
-
- PathSentCache *psc = path_send_cache.getptr(path);
- ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
-
- Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from);
- ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path.");
- E->get() = true;
-}
-
-bool MultiplayerAPI::_send_confirm_path(Node *p_node, 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()) {
- // 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;
- }
- }
-
- if (peers_to_add.size() > 0) {
- // Those that need to be added, send a message for this.
-
- // Encode function name.
- const CharString path = String(p_path).utf8();
- const int path_len = encode_cstring(path.get_data(), nullptr);
-
- // Extract MD5 from rpc methods list.
- const String methods_md5 = _get_rpc_md5(p_node);
- const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
-
- Vector<uint8_t> packet;
- packet.resize(1 + 4 + path_len + methods_md5_len);
- int ofs = 0;
-
- packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH;
- ofs += 1;
-
- ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]);
-
- ofs += encode_uint32(psc->id, &packet.write[ofs]);
-
- ofs += encode_cstring(path.get_data(), &packet.write[ofs]);
-
- for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) {
- network_peer->set_target_peer(E->get()); // To all of you.
- network_peer->set_transfer_mode(MultiplayerPeer::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;
-}
-
-// The variant is compressed and encoded; The first byte contains all the meta
-// information and the format is:
-// - The first LSB 5 bits are used for the variant type.
-// - The next two bits are used to store the encoding mode.
-// - The most significant is used to store the boolean value.
-#define VARIANT_META_TYPE_MASK 0x1F
-#define VARIANT_META_EMODE_MASK 0x60
-#define VARIANT_META_BOOL_MASK 0x80
-#define ENCODE_8 0 << 5
-#define ENCODE_16 1 << 5
-#define ENCODE_32 2 << 5
-#define ENCODE_64 3 << 5
-Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) {
- // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
- CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
-
- uint8_t *buf = r_buffer;
- r_len = 0;
- uint8_t encode_mode = 0;
-
- switch (p_variant.get_type()) {
- case Variant::BOOL: {
- if (buf) {
- // We still have 1 free bit in the meta, so let's use it.
- buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0;
- buf[0] |= encode_mode | p_variant.get_type();
- }
- r_len += 1;
- } break;
- case Variant::INT: {
- if (buf) {
- // Reserve the first byte for the meta.
- buf += 1;
- }
- r_len += 1;
- int64_t val = p_variant;
- if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) {
- // Use 8 bit
- encode_mode = ENCODE_8;
- if (buf) {
- buf[0] = val;
- }
- r_len += 1;
- } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) {
- // Use 16 bit
- encode_mode = ENCODE_16;
- if (buf) {
- encode_uint16(val, buf);
- }
- r_len += 2;
- } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) {
- // Use 32 bit
- encode_mode = ENCODE_32;
- if (buf) {
- encode_uint32(val, buf);
- }
- r_len += 4;
- } else {
- // Use 64 bit
- encode_mode = ENCODE_64;
- if (buf) {
- encode_uint64(val, buf);
- }
- r_len += 8;
- }
- // Store the meta
- if (buf) {
- buf -= 1;
- buf[0] = encode_mode | p_variant.get_type();
- }
- } break;
- default:
- // Any other case is not yet compressed.
- Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding);
- if (err != OK) {
- return err;
- }
- if (r_buffer) {
- // The first byte is not used by the marshalling, so store the type
- // so we know how to decompress and decode this variant.
- r_buffer[0] = p_variant.get_type();
- }
- }
-
- return OK;
-}
-
-Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) {
- const uint8_t *buf = p_buffer;
- int len = p_len;
-
- ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
- uint8_t type = buf[0] & VARIANT_META_TYPE_MASK;
- uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK;
-
- ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
-
- switch (type) {
- case Variant::BOOL: {
- bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0;
- r_variant = val;
- if (r_len) {
- *r_len = 1;
- }
- } break;
- case Variant::INT: {
- buf += 1;
- len -= 1;
- if (r_len) {
- *r_len = 1;
- }
- if (encode_mode == ENCODE_8) {
- // 8 bits.
- ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
- int8_t val = buf[0];
- r_variant = val;
- if (r_len) {
- (*r_len) += 1;
- }
- } else if (encode_mode == ENCODE_16) {
- // 16 bits.
- ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA);
- int16_t val = decode_uint16(buf);
- r_variant = val;
- if (r_len) {
- (*r_len) += 2;
- }
- } else if (encode_mode == ENCODE_32) {
- // 32 bits.
- ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
- int32_t val = decode_uint32(buf);
- r_variant = val;
- if (r_len) {
- (*r_len) += 4;
- }
- } else {
- // 64 bits.
- ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
- int64_t val = decode_uint64(buf);
- r_variant = val;
- if (r_len) {
- (*r_len) += 8;
- }
- }
- } break;
- default:
- Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding);
- if (err != OK) {
- return err;
- }
- }
-
- return OK;
-}
-
-void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) {
- ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree.");
-
- ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree.");
-
- ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected.");
-
- ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255.");
-
- if (p_to != 0 && !connected_peers.has(ABS(p_to))) {
- ERR_FAIL_COND_MSG(p_to == network_peer->get_unique_id(), "Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()) + ".");
-
- ERR_FAIL_MSG("Attempt to remote call unexisting ID: " + itos(p_to) + ".");
- }
-
- NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path());
- ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
-
- // 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++;
- }
-
- // See if all peers have cached path (if so, call can be fast).
- const bool has_all_peers = _send_confirm_path(p_from, from_path, psc, p_to);
-
- // 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 meta.
- // The meta is composed by a single byte that contains (starting from the least significant bit):
- // - `NetworkCommands` in the first three bits.
- // - `NetworkNodeIdCompression` in the next 2 bits.
- // - `NetworkNameIdCompression` in the next 1 bit.
- // - `byte_only_or_no_args` in the next 1 bit.
- // - So we still have the last bit free!
- uint8_t command_type = NETWORK_COMMAND_REMOTE_CALL;
- uint8_t node_id_compression = UINT8_MAX;
- uint8_t name_id_compression = UINT8_MAX;
- bool byte_only_or_no_args = false;
-
- MAKE_ROOM(1);
- // The meta is composed along the way, so just set 0 for now.
- packet_cache.write[0] = 0;
- ofs += 1;
-
- // Encode Node ID.
- if (has_all_peers) {
- // Compress the node ID only if all the target peers already know it.
- if (psc->id >= 0 && psc->id <= 255) {
- // We can encode the id in 1 byte
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_8;
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = static_cast<uint8_t>(psc->id);
- ofs += 1;
- } else if (psc->id >= 0 && psc->id <= 65535) {
- // We can encode the id in 2 bytes
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_16;
- MAKE_ROOM(ofs + 2);
- encode_uint16(static_cast<uint16_t>(psc->id), &(packet_cache.write[ofs]));
- ofs += 2;
- } else {
- // Too big, let's use 4 bytes.
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
- MAKE_ROOM(ofs + 4);
- encode_uint32(psc->id, &(packet_cache.write[ofs]));
- ofs += 4;
- }
- } else {
- // The targets don't know the node yet, so we need to use 32 bits int.
- node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
- MAKE_ROOM(ofs + 4);
- encode_uint32(psc->id, &(packet_cache.write[ofs]));
- ofs += 4;
- }
-
- // Encode method ID
- if (p_rpc_id <= UINT8_MAX) {
- // The ID fits in 1 byte
- name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id);
- ofs += 1;
- } else {
- // The ID is larger, let's use 2 bytes
- name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
- MAKE_ROOM(ofs + 2);
- encode_uint16(p_rpc_id, &(packet_cache.write[ofs]));
- ofs += 2;
- }
-
- if (p_argcount == 0) {
- byte_only_or_no_args = true;
- } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
- byte_only_or_no_args = true;
- // Special optimization when only the byte vector is sent.
- const Vector<uint8_t> data = *p_arg[0];
- MAKE_ROOM(ofs + data.size());
- memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
- ofs += data.size();
- } else {
- // Arguments
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = p_argcount;
- ofs += 1;
- for (int i = 0; i < p_argcount; i++) {
- int len(0);
- Error err = _encode_and_compress_variant(*p_arg[i], nullptr, len);
- ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
- MAKE_ROOM(ofs + len);
- _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
- ofs += len;
- }
- }
-
- ERR_FAIL_COND(command_type > 7);
- ERR_FAIL_COND(node_id_compression > 3);
- ERR_FAIL_COND(name_id_compression > 1);
-
- // We can now set the meta
- packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + ((byte_only_or_no_args ? 1 : 0) << BYTE_ONLY_OR_NO_ARGS_SHIFT);
-
-#ifdef DEBUG_ENABLED
- _profile_bandwidth_data("out", ofs);
-#endif
-
- // Take chance and set transfer mode, since all send methods will use it.
- network_peer->set_transfer_mode(p_config.transfer_mode);
-
- 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 {
- // Unreachable because the node ID is never compressed if the peers doesn't know it.
- CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32);
-
- // Not all verified path, so send one by one.
-
- // Append 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(), nullptr);
- MAKE_ROOM(ofs + path_len);
- encode_cstring(pname.get_data(), &(packet_cache.write[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()) {
- // This one confirmed path, so use id.
- encode_uint32(psc->id, &(packet_cache.write[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.write[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(SNAME("network_peer_connected"), p_id);
-}
-
-void MultiplayerAPI::_del_peer(int p_id) {
- connected_peers.erase(p_id);
- // Cleanup get cache.
- path_get_cache.erase(p_id);
- // Cleanup sent cache.
- // Some refactoring is needed to make this faster and do paths GC.
- List<NodePath> keys;
- path_send_cache.get_key_list(&keys);
- for (List<NodePath>::Element *E = keys.front(); E; E = E->next()) {
- PathSentCache *psc = path_send_cache.getptr(E->get());
- psc->confirmed_peers.erase(p_id);
- }
- emit_signal(SNAME("network_peer_disconnected"), p_id);
-}
-
-void MultiplayerAPI::_connected_to_server() {
- emit_signal(SNAME("connected_to_server"));
-}
-
-void MultiplayerAPI::_connection_failed() {
- emit_signal(SNAME("connection_failed"));
-}
-
-void MultiplayerAPI::_server_disconnected() {
- emit_signal(SNAME("server_disconnected"));
-}
-
-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_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active.");
- ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree.");
- ERR_FAIL_COND_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected.");
-
- int node_id = network_peer->get_unique_id();
- bool call_local_native = false;
- bool call_local_script = false;
- uint16_t rpc_id = UINT16_MAX;
- const RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id);
- ERR_FAIL_COND_MSG(config.name == StringName(),
- vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path()));
- if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
- if (rpc_id & (1 << 15)) {
- call_local_native = config.sync;
- } else {
- call_local_script = config.sync;
- }
- }
-
- if (p_peer_id != node_id) {
-#ifdef DEBUG_ENABLED
- _profile_node_data("out_rpc", p_node->get_instance_id());
-#endif
-
- _send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);
- }
-
- if (call_local_native) {
- int temp_id = rpc_sender_id;
- rpc_sender_id = get_network_unique_id();
- Callable::CallError ce;
- p_node->call(p_method, p_arg, p_argcount, ce);
- rpc_sender_id = temp_id;
- if (ce.error != Callable::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_PRINT(error);
- return;
- }
- }
-
- if (call_local_script) {
- int temp_id = rpc_sender_id;
- rpc_sender_id = get_network_unique_id();
- Callable::CallError ce;
- ce.error = Callable::CallError::CALL_OK;
- p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce);
- rpc_sender_id = temp_id;
- if (ce.error != Callable::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_PRINT(error);
- return;
- }
- }
-
- ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.sync, "RPC '" + p_method + "' on yourself is not allowed by selected mode.");
-}
-
-Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::TransferMode p_mode) {
- ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet.");
- ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active.");
- ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected.");
-
- MAKE_ROOM(p_data.size() + 1);
- const uint8_t *r = p_data.ptr();
- packet_cache.write[0] = NETWORK_COMMAND_RAW;
- memcpy(&packet_cache.write[1], &r[0], p_data.size());
-
- network_peer->set_target_peer(p_to);
- network_peer->set_transfer_mode(p_mode);
-
- return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1);
-}
-
-void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small.");
-
- Vector<uint8_t> out;
- int len = p_packet_len - 1;
- out.resize(len);
- {
- uint8_t *w = out.ptrw();
- memcpy(&w[0], &p_packet[1], len);
- }
- emit_signal(SNAME("network_peer_packet"), p_from, out);
-}
-
-int MultiplayerAPI::get_network_unique_id() const {
- ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID.");
- return network_peer->get_unique_id();
-}
-
-bool MultiplayerAPI::is_network_server() const {
- return network_peer.is_valid() && network_peer->is_server();
-}
-
-void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) {
- ERR_FAIL_COND_MSG(!network_peer.is_valid(), "No network peer is assigned. Unable to set 'refuse_new_connections'.");
- network_peer->set_refuse_new_connections(p_refuse);
-}
-
-bool MultiplayerAPI::is_refusing_new_network_connections() const {
- ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. Unable to get 'refuse_new_connections'.");
- return network_peer->is_refusing_new_connections();
-}
-
-Vector<int> MultiplayerAPI::get_network_connected_peers() const {
- ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), Vector<int>(), "No network peer is assigned. Assume no peers are connected.");
-
- Vector<int> ret;
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
- ret.push_back(E->get());
- }
-
- return ret;
-}
-
-void MultiplayerAPI::set_allow_object_decoding(bool p_enable) {
- allow_object_decoding = p_enable;
-}
-
-bool MultiplayerAPI::is_object_decoding_allowed() const {
- return allow_object_decoding;
-}
-
-void MultiplayerAPI::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
- ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node);
- ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE));
- 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("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("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);
- ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding);
- ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
- 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, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_network_peer", "get_network_peer");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node");
- ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false);
-
- ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id")));
- ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id")));
- ADD_SIGNAL(MethodInfo("network_peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet")));
- ADD_SIGNAL(MethodInfo("connected_to_server"));
- ADD_SIGNAL(MethodInfo("connection_failed"));
- ADD_SIGNAL(MethodInfo("server_disconnected"));
-
- BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
- BIND_ENUM_CONSTANT(RPC_MODE_REMOTE);
- BIND_ENUM_CONSTANT(RPC_MODE_MASTER);
- BIND_ENUM_CONSTANT(RPC_MODE_PUPPET);
-}
-
-MultiplayerAPI::MultiplayerAPI() {
- clear();
-}
-
-MultiplayerAPI::~MultiplayerAPI() {
- clear();
-}
diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h
deleted file mode 100644
index cc994a9852..0000000000
--- a/core/io/multiplayer_api.h
+++ /dev/null
@@ -1,163 +0,0 @@
-/*************************************************************************/
-/* multiplayer_api.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef MULTIPLAYER_API_H
-#define MULTIPLAYER_API_H
-
-#include "core/io/multiplayer_peer.h"
-#include "core/object/ref_counted.h"
-
-class MultiplayerAPI : public RefCounted {
- GDCLASS(MultiplayerAPI, RefCounted);
-
-public:
- enum RPCMode {
- RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
- RPC_MODE_REMOTE, // Using rpc() on it will call method in all remote peers
- RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote
- RPC_MODE_PUPPET, // Using rpc() on it will call method for all puppets
- };
-
- struct RPCConfig {
- StringName name;
- RPCMode rpc_mode = RPC_MODE_DISABLED;
- bool sync = false;
- MultiplayerPeer::TransferMode transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
- int channel = 0;
-
- bool operator==(RPCConfig const &p_other) const {
- return name == p_other.name;
- }
- };
-
- struct SortRPCConfig {
- StringName::AlphCompare compare;
- bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const {
- return compare(p_a.name, p_b.name);
- }
- };
-
-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<MultiplayerPeer> network_peer;
- int rpc_sender_id = 0;
- 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 = nullptr;
- bool allow_object_decoding = false;
-
-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, uint32_t p_node_target, int p_packet_len);
- void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
- void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
-
- void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount);
- bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);
-
- Error _encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len);
- Error _decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len);
-
-public:
- enum NetworkCommands {
- NETWORK_COMMAND_REMOTE_CALL = 0,
- NETWORK_COMMAND_SIMPLIFY_PATH,
- NETWORK_COMMAND_CONFIRM_PATH,
- NETWORK_COMMAND_RAW,
- };
-
- enum NetworkNodeIdCompression {
- NETWORK_NODE_ID_COMPRESSION_8 = 0,
- NETWORK_NODE_ID_COMPRESSION_16,
- NETWORK_NODE_ID_COMPRESSION_32,
- };
-
- enum NetworkNameIdCompression {
- NETWORK_NAME_ID_COMPRESSION_8 = 0,
- NETWORK_NAME_ID_COMPRESSION_16,
- };
-
- void poll();
- void clear();
- void set_root_node(Node *p_node);
- Node *get_root_node();
- void set_network_peer(const Ref<MultiplayerPeer> &p_peer);
- Ref<MultiplayerPeer> get_network_peer() const;
- Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE);
-
- // 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);
-
- 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;
-
- void set_allow_object_decoding(bool p_enable);
- bool is_object_decoding_allowed() const;
-
- MultiplayerAPI();
- ~MultiplayerAPI();
-};
-
-VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode);
-
-#endif // MULTIPLAYER_API_H
diff --git a/core/io/multiplayer_peer.cpp b/core/io/multiplayer_peer.cpp
deleted file mode 100644
index 8126b4cea3..0000000000
--- a/core/io/multiplayer_peer.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*************************************************************************/
-/* multiplayer_peer.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "multiplayer_peer.h"
-
-void MultiplayerPeer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode);
- ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode);
- ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer);
-
- ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer);
-
- ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll);
-
- ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status);
- ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id);
-
- ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections);
- ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode");
-
- BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE);
- BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED);
- BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE);
-
- BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED);
- BIND_ENUM_CONSTANT(CONNECTION_CONNECTING);
- BIND_ENUM_CONSTANT(CONNECTION_CONNECTED);
-
- BIND_CONSTANT(TARGET_PEER_BROADCAST);
- BIND_CONSTANT(TARGET_PEER_SERVER);
-
- ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
- ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
- ADD_SIGNAL(MethodInfo("server_disconnected"));
- ADD_SIGNAL(MethodInfo("connection_succeeded"));
- ADD_SIGNAL(MethodInfo("connection_failed"));
-}
diff --git a/core/io/packed_data_container.cpp b/core/io/packed_data_container.cpp
index cf6a0b6027..4a76f0191d 100644
--- a/core/io/packed_data_container.cpp
+++ b/core/io/packed_data_container.cpp
@@ -268,21 +268,21 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd
d.get_key_list(&keys);
List<DictKey> sortk;
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ for (const Variant &key : keys) {
DictKey dk;
- dk.hash = E->get().hash();
- dk.key = E->get();
+ dk.hash = key.hash();
+ dk.key = key;
sortk.push_back(dk);
}
sortk.sort();
int idx = 0;
- for (List<DictKey>::Element *E = sortk.front(); E; E = E->next()) {
- encode_uint32(E->get().hash, &tmpdata.write[pos + 8 + idx * 12 + 0]);
- uint32_t ofs = _pack(E->get().key, tmpdata, string_cache);
+ for (const DictKey &E : sortk) {
+ encode_uint32(E.hash, &tmpdata.write[pos + 8 + idx * 12 + 0]);
+ uint32_t ofs = _pack(E.key, tmpdata, string_cache);
encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 4]);
- ofs = _pack(d[E->get().key], tmpdata, string_cache);
+ ofs = _pack(d[E.key], tmpdata, string_cache);
encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 8]);
idx++;
}
diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp
index 8da44fd290..87d2b66e5b 100644
--- a/core/io/packet_peer.cpp
+++ b/core/io/packet_peer.cpp
@@ -138,6 +138,7 @@ Error PacketPeer::_get_packet_error() const {
void PacketPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &PacketPeer::_bnd_get_var, DEFVAL(false));
ClassDB::bind_method(D_METHOD("put_var", "var", "full_objects"), &PacketPeer::put_var, DEFVAL(false));
+
ClassDB::bind_method(D_METHOD("get_packet"), &PacketPeer::_get_packet);
ClassDB::bind_method(D_METHOD("put_packet", "buffer"), &PacketPeer::_put_packet);
ClassDB::bind_method(D_METHOD("get_packet_error"), &PacketPeer::_get_packet_error);
@@ -151,6 +152,51 @@ void PacketPeer::_bind_methods() {
/***************/
+int PacketPeerExtension::get_available_packet_count() const {
+ int count;
+ if (GDVIRTUAL_CALL(_get_available_packet_count, count)) {
+ return count;
+ }
+ WARN_PRINT_ONCE("PacketPeerExtension::_get_available_packet_count is unimplemented!");
+ return -1;
+}
+
+Error PacketPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("PacketPeerExtension::_get_packet_native is unimplemented!");
+ return FAILED;
+}
+
+Error PacketPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("PacketPeerExtension::_put_packet_native is unimplemented!");
+ return FAILED;
+}
+
+int PacketPeerExtension::get_max_packet_size() const {
+ int size;
+ if (GDVIRTUAL_CALL(_get_max_packet_size, size)) {
+ return size;
+ }
+ WARN_PRINT_ONCE("PacketPeerExtension::_get_max_packet_size is unimplemented!");
+ return 0;
+}
+
+void PacketPeerExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
+ GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
+ GDVIRTUAL_BIND(_get_available_packet_count);
+ GDVIRTUAL_BIND(_get_max_packet_size);
+}
+
+/***************/
+
void PacketPeerStream::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream_peer", "peer"), &PacketPeerStream::set_stream_peer);
ClassDB::bind_method(D_METHOD("get_stream_peer"), &PacketPeerStream::get_stream_peer);
diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h
index 9a345af3d0..bc1f4aaabf 100644
--- a/core/io/packet_peer.h
+++ b/core/io/packet_peer.h
@@ -35,6 +35,10 @@
#include "core/object/class_db.h"
#include "core/templates/ring_buffer.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
+#include "core/variant/native_ptr.h"
+
class PacketPeer : public RefCounted {
GDCLASS(PacketPeer, RefCounted);
@@ -73,6 +77,25 @@ public:
~PacketPeer() {}
};
+class PacketPeerExtension : public PacketPeer {
+ GDCLASS(PacketPeerExtension, PacketPeer);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ virtual int get_max_packet_size() const override;
+
+ /* GDExtension */
+ GDVIRTUAL0RC(int, _get_available_packet_count);
+ GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>);
+ GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int);
+ GDVIRTUAL0RC(int, _get_max_packet_size);
+};
+
class PacketPeerStream : public PacketPeer {
GDCLASS(PacketPeerStream, PacketPeer);
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index efa622d976..972076e397 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -33,6 +33,7 @@
#include "core/core_string_names.h"
#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
+#include "core/math/math_funcs.h"
#include "core/object/script_language.h"
#include "core/os/os.h"
#include "scene/main/node.h" //only so casting works
@@ -94,16 +95,48 @@ String Resource::get_path() const {
return path_cache;
}
-void Resource::set_subindex(int p_sub_index) {
- subindex = p_sub_index;
+String Resource::generate_scene_unique_id() {
+ // Generate a unique enough hash, but still user-readable.
+ // If it's not unique it does not matter because the saver will try again.
+ OS::Date date = OS::get_singleton()->get_date();
+ OS::Time time = OS::get_singleton()->get_time();
+ uint32_t hash = hash_djb2_one_32(OS::get_singleton()->get_ticks_usec());
+ hash = hash_djb2_one_32(date.year, hash);
+ hash = hash_djb2_one_32(date.month, hash);
+ hash = hash_djb2_one_32(date.day, hash);
+ hash = hash_djb2_one_32(time.hour, hash);
+ hash = hash_djb2_one_32(time.minute, hash);
+ hash = hash_djb2_one_32(time.second, hash);
+ hash = hash_djb2_one_32(Math::rand(), hash);
+
+ static constexpr uint32_t characters = 5;
+ static constexpr uint32_t char_count = ('z' - 'a');
+ static constexpr uint32_t base = char_count + ('9' - '0');
+ String id;
+ for (uint32_t i = 0; i < characters; i++) {
+ uint32_t c = hash % base;
+ if (c < char_count) {
+ id += String::chr('a' + c);
+ } else {
+ id += String::chr('0' + (c - char_count));
+ }
+ hash /= base;
+ }
+
+ return id;
}
-int Resource::get_subindex() const {
- return subindex;
+void Resource::set_scene_unique_id(const String &p_id) {
+ scene_unique_id = p_id;
+}
+
+String Resource::get_scene_unique_id() const {
+ return scene_unique_id;
}
void Resource::set_name(const String &p_name) {
name = p_name;
+ emit_changed();
}
String Resource::get_name() const {
@@ -133,15 +166,15 @@ Error Resource::copy_from(const Ref<Resource> &p_resource) {
List<PropertyInfo> pi;
p_resource->get_property_list(&pi);
- for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : pi) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- if (E->get().name == "resource_path") {
+ if (E.name == "resource_path") {
continue; //do not change path
}
- set(E->get().name, p_resource->get(E->get().name));
+ set(E.name, p_resource->get(E.name));
}
return OK;
}
@@ -169,11 +202,11 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res
r->local_scene = p_for_scene;
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : plist) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- Variant p = get(E->get().name);
+ Variant p = get(E.name);
if (p.get_type() == Variant::OBJECT) {
RES sr = p;
if (sr.is_valid()) {
@@ -189,7 +222,7 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res
}
}
- r->set(E->get().name, p);
+ r->set(E.name, p);
}
return r;
@@ -201,11 +234,11 @@ void Resource::configure_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, R
local_scene = p_for_scene;
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : plist) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- Variant p = get(E->get().name);
+ Variant p = get(E.name);
if (p.get_type() == Variant::OBJECT) {
RES sr = p;
if (sr.is_valid()) {
@@ -227,21 +260,21 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const {
Ref<Resource> r = (Resource *)ClassDB::instantiate(get_class());
ERR_FAIL_COND_V(r.is_null(), Ref<Resource>());
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ for (const PropertyInfo &E : plist) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- Variant p = get(E->get().name);
+ Variant p = get(E.name);
if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) {
- r->set(E->get().name, p.duplicate(p_subresources));
- } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) {
+ r->set(E.name, p.duplicate(p_subresources));
+ } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E.usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) {
RES sr = p;
if (sr.is_valid()) {
- r->set(E->get().name, sr->duplicate(p_subresources));
+ r->set(E.name, sr->duplicate(p_subresources));
}
} else {
- r->set(E->get().name, p);
+ r->set(E.name, p);
}
}
@@ -285,9 +318,9 @@ uint32_t Resource::hash_edited_version() const {
List<PropertyInfo> plist;
get_property_list(&plist);
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- if (E->get().usage & PROPERTY_USAGE_STORAGE && E->get().type == Variant::OBJECT && E->get().hint == PROPERTY_HINT_RESOURCE_TYPE) {
- RES res = get(E->get().name);
+ for (const PropertyInfo &E : plist) {
+ if (E.usage & PROPERTY_USAGE_STORAGE && E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ RES res = get(E.name);
if (res.is_valid()) {
hash = hash_djb2_one_32(res->hash_edited_version(), hash);
}
@@ -320,9 +353,8 @@ Node *Resource::get_local_scene() const {
}
void Resource::setup_local_to_scene() {
- if (get_script_instance()) {
- get_script_instance()->call("_setup_local_to_scene");
- }
+ // Can't use GDVIRTUAL in Resource, so this will have to be done with a signal
+ emit_signal(SNAME("setup_local_to_scene_requested"));
}
Node *(*Resource::_get_local_scene_func)() = nullptr;
@@ -350,8 +382,8 @@ bool Resource::is_translation_remapped() const {
#ifdef TOOLS_ENABLED
//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored
-void Resource::set_id_for_path(const String &p_path, int p_id) {
- if (p_id == -1) {
+void Resource::set_id_for_path(const String &p_path, const String &p_id) {
+ if (p_id == "") {
ResourceCache::path_cache_lock.write_lock();
ResourceCache::resource_path_cache[p_path].erase(get_path());
ResourceCache::path_cache_lock.write_unlock();
@@ -362,15 +394,15 @@ void Resource::set_id_for_path(const String &p_path, int p_id) {
}
}
-int Resource::get_id_for_path(const String &p_path) const {
+String Resource::get_id_for_path(const String &p_path) const {
ResourceCache::path_cache_lock.read_lock();
if (ResourceCache::resource_path_cache[p_path].has(get_path())) {
- int result = ResourceCache::resource_path_cache[p_path][get_path()];
+ String result = ResourceCache::resource_path_cache[p_path][get_path()];
ResourceCache::path_cache_lock.read_unlock();
return result;
} else {
ResourceCache::path_cache_lock.read_unlock();
- return -1;
+ return "";
}
}
#endif
@@ -390,12 +422,12 @@ void Resource::_bind_methods() {
ClassDB::bind_method(D_METHOD("duplicate", "subresources"), &Resource::duplicate, DEFVAL(false));
ADD_SIGNAL(MethodInfo("changed"));
+ ADD_SIGNAL(MethodInfo("setup_local_to_scene_requested"));
+
ADD_GROUP("Resource", "resource_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_path", "get_path");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "resource_name"), "set_name", "get_name");
-
- BIND_VMETHOD(MethodInfo("_setup_local_to_scene"));
}
Resource::Resource() :
@@ -414,7 +446,7 @@ Resource::~Resource() {
HashMap<String, Resource *> ResourceCache::resources;
#ifdef TOOLS_ENABLED
-HashMap<String, HashMap<String, int>> ResourceCache::resource_path_cache;
+HashMap<String, HashMap<String, String>> ResourceCache::resource_path_cache;
#endif
RWLock ResourceCache::lock;
@@ -509,9 +541,9 @@ void ResourceCache::dump(const char *p_file, bool p_short) {
}
}
- for (Map<String, int>::Element *E = type_count.front(); E; E = E->next()) {
+ for (const KeyValue<String, int> &E : type_count) {
if (f) {
- f->store_line(E->key() + " count: " + itos(E->get()));
+ f->store_line(E.key + " count: " + itos(E.value));
}
}
if (f) {
@@ -520,5 +552,7 @@ void ResourceCache::dump(const char *p_file, bool p_short) {
}
lock.read_unlock();
+#else
+ WARN_PRINT("ResourceCache::dump only with in debug builds.");
#endif
}
diff --git a/core/io/resource.h b/core/io/resource.h
index 028fed1c6e..9ccc247887 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -31,6 +31,7 @@
#ifndef RESOURCE_H
#define RESOURCE_H
+#include "core/io/resource_uid.h"
#include "core/object/class_db.h"
#include "core/object/ref_counted.h"
#include "core/templates/safe_refcount.h"
@@ -59,9 +60,7 @@ private:
String name;
String path_cache;
- int subindex = 0;
-
- virtual bool _use_builtin_script() const { return true; }
+ String scene_unique_id;
#ifdef TOOLS_ENABLED
uint64_t last_modified_time = 0;
@@ -105,8 +104,9 @@ public:
virtual void set_path(const String &p_path, bool p_take_over = false);
String get_path() const;
- void set_subindex(int p_sub_index);
- int get_subindex() const;
+ static String generate_scene_unique_id();
+ void set_scene_unique_id(const String &p_id);
+ String get_scene_unique_id() const;
virtual Ref<Resource> duplicate(bool p_subresources = false) const;
Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, Ref<Resource>> &remap_cache);
@@ -140,8 +140,8 @@ public:
#ifdef TOOLS_ENABLED
//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored
- void set_id_for_path(const String &p_path, int p_id);
- int get_id_for_path(const String &p_path) const;
+ void set_id_for_path(const String &p_path, const String &p_id);
+ String get_id_for_path(const String &p_path) const;
#endif
Resource();
@@ -156,7 +156,7 @@ class ResourceCache {
static RWLock lock;
static HashMap<String, Resource *> resources;
#ifdef TOOLS_ENABLED
- static HashMap<String, HashMap<String, int>> resource_path_cache; // each tscn has a set of resource paths and IDs
+ static HashMap<String, HashMap<String, String>> resource_path_cache; // Each tscn has a set of resource paths and IDs.
static RWLock path_cache_lock;
#endif // TOOLS_ENABLED
friend void unregister_core_types();
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 0e9815245f..cbb033f6c6 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -84,9 +84,10 @@ enum {
OBJECT_EXTERNAL_RESOURCE = 1,
OBJECT_INTERNAL_RESOURCE = 2,
OBJECT_EXTERNAL_RESOURCE_INDEX = 3,
- //version 2: added 64 bits support for float and int
- //version 3: changed nodepath encoding
- FORMAT_VERSION = 3,
+ // Version 2: added 64 bits support for float and int.
+ // Version 3: changed nodepath encoding.
+ // Version 4: new string ID for ext/subresources, breaks forward compat.
+ FORMAT_VERSION = 4,
FORMAT_VERSION_CAN_RENAME_DEPS = 1,
FORMAT_VERSION_NO_NODEPATH_PROPERTY = 3,
};
@@ -311,7 +312,14 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
} break;
case OBJECT_INTERNAL_RESOURCE: {
uint32_t index = f->get_32();
- String path = res_path + "::" + itos(index);
+ String path;
+
+ if (using_named_scene_ids) { // New format.
+ ERR_FAIL_INDEX_V((int)index, internal_resources.size(), ERR_PARSE_ERROR);
+ path = internal_resources[index].path;
+ } else {
+ path += res_path + "::" + itos(index);
+ }
//always use internal cache for loading internal resources
if (!internal_index_cache.has(path)) {
@@ -320,7 +328,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
} else {
r_v = internal_index_cache[path];
}
-
} break;
case OBJECT_EXTERNAL_RESOURCE: {
//old file format, still around for compatibility
@@ -328,7 +335,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
String exttype = get_unicode_string();
String path = get_unicode_string();
- if (path.find("://") == -1 && path.is_rel_path()) {
+ if (path.find("://") == -1 && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path));
}
@@ -378,7 +385,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
ERR_FAIL_V(ERR_FILE_CORRUPT);
} break;
}
-
} break;
case VARIANT_CALLABLE: {
r_v = Callable();
@@ -620,7 +626,7 @@ Error ResourceLoaderBinary::load() {
path = remaps[path];
}
- if (path.find("://") == -1 && path.is_rel_path()) {
+ if (path.find("://") == -1 && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(path.get_base_dir().plus_file(external_resources[i].path));
}
@@ -659,15 +665,17 @@ Error ResourceLoaderBinary::load() {
//maybe it is loaded already
String path;
- int subindex = 0;
+ String id;
if (!main) {
path = internal_resources[i].path;
if (path.begins_with("local://")) {
path = path.replace_first("local://", "");
- subindex = path.to_int();
+ id = path;
path = res_path + "::" + path;
+
+ internal_resources.write[i].path = path; // Update path.
}
if (cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) {
@@ -722,7 +730,7 @@ Error ResourceLoaderBinary::load() {
if (path != String() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); //if got here because the resource with same path has different type, replace it
}
- r->set_subindex(subindex);
+ r->set_scene_unique_id(id);
}
if (!main) {
@@ -808,13 +816,18 @@ String ResourceLoaderBinary::get_unicode_string() {
}
void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) {
- open(p_f);
+ open(p_f, false, true);
if (error) {
return;
}
for (int i = 0; i < external_resources.size(); i++) {
- String dep = external_resources[i].path;
+ String dep;
+ if (external_resources[i].uid != ResourceUID::INVALID_ID) {
+ dep = ResourceUID::get_singleton()->id_to_text(external_resources[i].uid);
+ } else {
+ dep = external_resources[i].path;
+ }
if (p_add_types && external_resources[i].type != String()) {
dep += "::" + external_resources[i].type;
@@ -824,7 +837,7 @@ void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dep
}
}
-void ResourceLoaderBinary::open(FileAccess *p_f) {
+void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_keep_uuid_paths) {
error = OK;
f = p_f;
@@ -879,10 +892,29 @@ void ResourceLoaderBinary::open(FileAccess *p_f) {
print_bl("type: " + type);
importmd_ofs = f->get_64();
- for (int i = 0; i < 14; i++) {
+ uint32_t flags = f->get_32();
+ if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS) {
+ using_named_scene_ids = true;
+ }
+ if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS) {
+ using_uids = true;
+ }
+
+ if (using_uids) {
+ uid = f->get_64();
+ } else {
+ f->get_64(); // skip over uid field
+ uid = ResourceUID::INVALID_ID;
+ }
+
+ for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
f->get_32(); //skip a few reserved fields
}
+ if (p_no_resources) {
+ return;
+ }
+
uint32_t string_table_size = f->get_32();
string_map.resize(string_table_size);
for (uint32_t i = 0; i < string_table_size; i++) {
@@ -896,8 +928,18 @@ void ResourceLoaderBinary::open(FileAccess *p_f) {
for (uint32_t i = 0; i < ext_resources_size; i++) {
ExtResource er;
er.type = get_unicode_string();
-
er.path = get_unicode_string();
+ if (using_uids) {
+ er.uid = f->get_64();
+ if (!p_keep_uuid_paths && er.uid != ResourceUID::INVALID_ID) {
+ if (ResourceUID::get_singleton()->has_id(er.uid)) {
+ // If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
+ er.path = ResourceUID::get_singleton()->get_id_path(er.uid);
+ } else {
+ WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UUID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
+ }
+ }
+ }
external_resources.push_back(er);
}
@@ -1013,8 +1055,8 @@ void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String
extensions.sort();
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- String ext = E->get().to_lower();
+ for (const String &E : extensions) {
+ String ext = E.to_lower();
p_extensions->push_back(ext);
}
}
@@ -1024,8 +1066,8 @@ void ResourceFormatLoaderBinary::get_recognized_extensions(List<String> *p_exten
ClassDB::get_resource_base_extensions(&extensions);
extensions.sort();
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- String ext = E->get().to_lower();
+ for (const String &E : extensions) {
+ String ext = E.to_lower();
p_extensions->push_back(ext);
}
}
@@ -1161,8 +1203,15 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
uint64_t importmd_ofs = f->get_64();
fw->store_64(0); //metadata offset
- for (int i = 0; i < 14; i++) {
- fw->store_32(0);
+ uint32_t flags = f->get_32();
+ bool using_uids = (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS);
+ uint64_t uid_data = f->get_64();
+
+ fw->store_32(flags);
+ fw->store_64(uid_data);
+
+ for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
+ fw->store_32(0); // reserved
f->get_32();
}
@@ -1183,6 +1232,16 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
String type = get_ustring(f);
String path = get_ustring(f);
+ if (using_uids) {
+ ResourceUID::ID uid = f->get_64();
+ if (uid != ResourceUID::INVALID_ID) {
+ if (ResourceUID::get_singleton()->has_id(uid)) {
+ // If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
+ path = ResourceUID::get_singleton()->get_id_path(uid);
+ }
+ }
+ }
+
bool relative = false;
if (!path.begins_with("res://")) {
path = local_path.plus_file(path).simplify_path();
@@ -1194,6 +1253,8 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
path = np;
}
+ String full_path = path;
+
if (relative) {
//restore relative
path = local_path.path_to_file(path);
@@ -1201,6 +1262,11 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
save_ustring(fw, type);
save_ustring(fw, path);
+
+ if (using_uids) {
+ ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(full_path);
+ fw->store_64(uid);
+ }
}
int64_t size_diff = (int64_t)fw->get_position() - (int64_t)f->get_position();
@@ -1256,6 +1322,28 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const
return ClassDB::get_compatibility_remapped_class(r);
}
+ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_path) const {
+ String ext = p_path.get_extension().to_lower();
+ if (!ClassDB::is_resource_extension(ext)) {
+ return ResourceUID::INVALID_ID;
+ }
+
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ if (!f) {
+ return ResourceUID::INVALID_ID; //could not read
+ }
+
+ ResourceLoaderBinary loader;
+ loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
+ loader.res_path = loader.local_path;
+ //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) );
+ loader.open(f, true);
+ if (loader.error != OK) {
+ return ResourceUID::INVALID_ID; //could not read
+ }
+ return loader.uid;
+}
+
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
@@ -1269,11 +1357,7 @@ void ResourceFormatSaverBinaryInstance::_pad_buffer(FileAccess *f, int p_bytes)
}
}
-void ResourceFormatSaverBinaryInstance::_write_variant(const Variant &p_property, const PropertyInfo &p_hint) {
- write_variant(f, p_property, resource_set, external_resources, string_map, p_hint);
-}
-
-void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Variant &p_property, Set<RES> &resource_set, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint) {
+void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint) {
switch (p_property.get_type()) {
case Variant::NIL: {
f->store_32(VARIANT_NIL);
@@ -1492,13 +1576,13 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
f->store_32(OBJECT_EXTERNAL_RESOURCE_INDEX);
f->store_32(external_resources[res]);
} else {
- if (!resource_set.has(res)) {
+ if (!resource_map.has(res)) {
f->store_32(OBJECT_EMPTY);
ERR_FAIL_MSG("Resource was not pre cached for the resource section, most likely due to circular reference.");
}
f->store_32(OBJECT_INTERNAL_RESOURCE);
- f->store_32(res->get_subindex());
+ f->store_32(resource_map[res]);
//internal resource
}
@@ -1520,14 +1604,14 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
List<Variant> keys;
d.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ for (const Variant &E : keys) {
/*
- if (!_check_type(dict[E->get()]))
+ if (!_check_type(dict[E]))
continue;
*/
- write_variant(f, E->get(), resource_set, external_resources, string_map);
- write_variant(f, d[E->get()], resource_set, external_resources, string_map);
+ write_variant(f, E, resource_map, external_resources, string_map);
+ write_variant(f, d[E], resource_map, external_resources, string_map);
}
} break;
@@ -1536,7 +1620,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
Array a = p_property;
f->store_32(uint32_t(a.size()));
for (int i = 0; i < a.size(); i++) {
- write_variant(f, a[i], resource_set, external_resources, string_map);
+ write_variant(f, a[i], resource_map, external_resources, string_map);
}
} break;
@@ -1677,15 +1761,15 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
res->get_property_list(&property_list);
- for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
- if (E->get().usage & PROPERTY_USAGE_STORAGE) {
- Variant value = res->get(E->get().name);
- if (E->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
+ for (const PropertyInfo &E : property_list) {
+ if (E.usage & PROPERTY_USAGE_STORAGE) {
+ Variant value = res->get(E.name);
+ if (E.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
RES sres = value;
if (sres.is_valid()) {
NonPersistentKey npk;
npk.base = res;
- npk.property = E->get().name;
+ npk.property = E.name;
non_persistent_map[npk] = sres;
resource_set.insert(sres);
saved_resources.push_back(sres);
@@ -1715,9 +1799,9 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
Dictionary d = p_variant;
List<Variant> keys;
d.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- _find_resources(E->get());
- Variant v = d[E->get()];
+ for (const Variant &E : keys) {
+ _find_resources(E);
+ Variant v = d[E];
_find_resources(v);
}
} break;
@@ -1816,46 +1900,49 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
save_unicode_string(f, p_resource->get_class());
f->store_64(0); //offset to import metadata
- for (int i = 0; i < 14; i++) {
+ f->store_32(FORMAT_FLAG_NAMED_SCENE_IDS | FORMAT_FLAG_UIDS);
+ ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p_path, true);
+ f->store_64(uid);
+ for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) {
f->store_32(0); // reserved
}
List<ResourceData> resources;
{
- for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
+ for (const RES &E : saved_resources) {
ResourceData &rd = resources.push_back(ResourceData())->get();
- rd.type = E->get()->get_class();
+ rd.type = E->get_class();
List<PropertyInfo> property_list;
- E->get()->get_property_list(&property_list);
+ E->get_property_list(&property_list);
- for (List<PropertyInfo>::Element *F = property_list.front(); F; F = F->next()) {
- if (skip_editor && F->get().name.begins_with("__editor")) {
+ for (const PropertyInfo &F : property_list) {
+ if (skip_editor && F.name.begins_with("__editor")) {
continue;
}
- if ((F->get().usage & PROPERTY_USAGE_STORAGE)) {
+ if ((F.usage & PROPERTY_USAGE_STORAGE)) {
Property p;
- p.name_idx = get_string_index(F->get().name);
+ p.name_idx = get_string_index(F.name);
- if (F->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
+ if (F.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
NonPersistentKey npk;
- npk.base = E->get();
- npk.property = F->get().name;
+ npk.base = E;
+ npk.property = F.name;
if (non_persistent_map.has(npk)) {
p.value = non_persistent_map[npk];
}
} else {
- p.value = E->get()->get(F->get().name);
+ p.value = E->get(F.name);
}
- Variant default_value = ClassDB::class_get_default_property_value(E->get()->get_class(), F->get().name);
+ Variant default_value = ClassDB::class_get_default_property_value(E->get_class(), F.name);
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, p.value, default_value))) {
continue;
}
- p.pi = F->get();
+ p.pi = F;
rd.properties.push_back(p);
}
@@ -1873,8 +1960,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
Vector<RES> save_order;
save_order.resize(external_resources.size());
- for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) {
- save_order.write[E->get()] = E->key();
+ for (const KeyValue<RES, int> &E : external_resources) {
+ save_order.write[E.value] = E.key;
}
for (int i = 0; i < save_order.size(); i++) {
@@ -1882,41 +1969,47 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
String path = save_order[i]->get_path();
path = relative_paths ? local_path.path_to_file(path) : path;
save_unicode_string(f, path);
+ ResourceUID::ID ruid = ResourceSaver::get_resource_id_for_path(save_order[i]->get_path(), false);
+ f->store_64(ruid);
}
// save internal resource table
f->store_32(saved_resources.size()); //amount of internal resources
Vector<uint64_t> ofs_pos;
- Set<int> used_indices;
+ Set<String> used_unique_ids;
- for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
- RES r = E->get();
+ for (RES &r : saved_resources) {
if (r->get_path() == "" || r->get_path().find("::") != -1) {
- if (r->get_subindex() != 0) {
- if (used_indices.has(r->get_subindex())) {
- r->set_subindex(0); //repeated
+ if (r->get_scene_unique_id() != "") {
+ if (used_unique_ids.has(r->get_scene_unique_id())) {
+ r->set_scene_unique_id("");
} else {
- used_indices.insert(r->get_subindex());
+ used_unique_ids.insert(r->get_scene_unique_id());
}
}
}
}
- for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
- RES r = E->get();
+ Map<RES, int> resource_map;
+ int res_index = 0;
+ for (RES &r : saved_resources) {
if (r->get_path() == "" || r->get_path().find("::") != -1) {
- if (r->get_subindex() == 0) {
- int new_subindex = 1;
- if (used_indices.size()) {
- new_subindex = used_indices.back()->get() + 1;
+ if (r->get_scene_unique_id() == "") {
+ String new_id;
+
+ while (true) {
+ new_id = r->get_class() + "_" + Resource::generate_scene_unique_id();
+ if (!used_unique_ids.has(new_id)) {
+ break;
+ }
}
- r->set_subindex(new_subindex);
- used_indices.insert(new_subindex);
+ r->set_scene_unique_id(new_id);
+ used_unique_ids.insert(new_id);
}
- save_unicode_string(f, "local://" + itos(r->get_subindex()));
+ save_unicode_string(f, "local://" + r->get_scene_unique_id());
if (takeover_paths) {
- r->set_path(p_path + "::" + itos(r->get_subindex()), true);
+ r->set_path(p_path + "::" + r->get_scene_unique_id(), true);
}
#ifdef TOOLS_ENABLED
r->set_edited(false);
@@ -1926,22 +2019,20 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
}
ofs_pos.push_back(f->get_position());
f->store_64(0); //offset in 64 bits
+ resource_map[r] = res_index++;
}
Vector<uint64_t> ofs_table;
//now actually save the resources
- for (List<ResourceData>::Element *E = resources.front(); E; E = E->next()) {
- ResourceData &rd = E->get();
-
+ for (const ResourceData &rd : resources) {
ofs_table.push_back(f->get_position());
save_unicode_string(f, rd.type);
f->store_32(rd.properties.size());
- for (List<Property>::Element *F = rd.properties.front(); F; F = F->next()) {
- Property &p = F->get();
+ for (const Property &p : rd.properties) {
f->store_32(p.name_idx);
- _write_variant(p.value, F->get().pi);
+ write_variant(f, p.value, resource_map, external_resources, string_map, p.pi);
}
}
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index abc7403935..a6e6d1848e 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -47,6 +47,8 @@ class ResourceLoaderBinary {
uint64_t importmd_ofs = 0;
+ ResourceUID::ID uid = ResourceUID::INVALID_ID;
+
Vector<char> str_buf;
List<RES> resource_cache;
@@ -57,9 +59,12 @@ class ResourceLoaderBinary {
struct ExtResource {
String path;
String type;
+ ResourceUID::ID uid = ResourceUID::INVALID_ID;
RES cache;
};
+ bool using_named_scene_ids = false;
+ bool using_uids = false;
bool use_sub_threads = false;
float *progress = nullptr;
Vector<ExtResource> external_resources;
@@ -93,7 +98,7 @@ public:
void set_translation_remapped(bool p_remapped);
void set_remaps(const Map<String, String> &p_remaps) { remaps = p_remaps; }
- void open(FileAccess *p_f);
+ void open(FileAccess *p_f, bool p_no_resources = false, bool p_keep_uuid_paths = false);
String recognize(FileAccess *p_f);
void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types);
@@ -108,6 +113,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
+ virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
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);
};
@@ -150,14 +156,19 @@ class ResourceFormatSaverBinaryInstance {
};
static void _pad_buffer(FileAccess *f, int p_bytes);
- void _write_variant(const Variant &p_property, const PropertyInfo &p_hint = PropertyInfo());
void _find_resources(const Variant &p_variant, bool p_main = false);
static void save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false);
int get_string_index(const String &p_string);
public:
+ enum {
+ FORMAT_FLAG_NAMED_SCENE_IDS = 1,
+ FORMAT_FLAG_UIDS = 2,
+ // Amount of reserved 32-bit fields in resource header
+ RESERVED_FIELDS = 11
+ };
Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
- static void write_variant(FileAccess *f, const Variant &p_property, Set<RES> &resource_set, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo());
+ static void write_variant(FileAccess *f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo());
};
class ResourceFormatSaverBinary : public ResourceFormatSaver {
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index b503655edd..cd44c537a8 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -93,6 +93,8 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
r_path_and_type.type = ClassDB::get_compatibility_remapped_class(value);
} else if (assign == "importer") {
r_path_and_type.importer = value;
+ } else if (assign == "uid") {
+ r_path_and_type.uid = ResourceUID::get_singleton()->text_to_id(value);
} else if (assign == "group_file") {
r_path_and_type.group_file = value;
} else if (assign == "metadata") {
@@ -146,10 +148,10 @@ void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extension
for (int i = 0; i < importers.size(); i++) {
List<String> local_exts;
importers[i]->get_recognized_extensions(&local_exts);
- for (List<String>::Element *F = local_exts.front(); F; F = F->next()) {
- if (!found.has(F->get())) {
- p_extensions->push_back(F->get());
- found.insert(F->get());
+ for (const String &F : local_exts) {
+ if (!found.has(F)) {
+ p_extensions->push_back(F);
+ found.insert(F);
}
}
}
@@ -175,10 +177,10 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_
List<String> local_exts;
importers[i]->get_recognized_extensions(&local_exts);
- for (List<String>::Element *F = local_exts.front(); F; F = F->next()) {
- if (!found.has(F->get())) {
- p_extensions->push_back(F->get());
- found.insert(F->get());
+ for (const String &F : local_exts) {
+ if (!found.has(F)) {
+ p_extensions->push_back(F);
+ found.insert(F);
}
}
}
@@ -336,6 +338,17 @@ String ResourceFormatImporter::get_resource_type(const String &p_path) const {
return pat.type;
}
+ResourceUID::ID ResourceFormatImporter::get_resource_uid(const String &p_path) const {
+ PathAndType pat;
+ Error err = _get_path_and_type(p_path, pat);
+
+ if (err != OK) {
+ return ResourceUID::INVALID_ID;
+ }
+
+ return pat.uid;
+}
+
Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const {
PathAndType pat;
Error err = _get_path_and_type(p_path, pat);
@@ -372,8 +385,8 @@ void ResourceFormatImporter::get_importers_for_extension(const String &p_extensi
for (int i = 0; i < importers.size(); i++) {
List<String> local_exts;
importers[i]->get_recognized_extensions(&local_exts);
- for (List<String>::Element *F = local_exts.front(); F; F = F->next()) {
- if (p_extension.to_lower() == F->get()) {
+ for (const String &F : local_exts) {
+ if (p_extension.to_lower() == F) {
r_importers->push_back(importers[i]);
}
}
@@ -393,8 +406,8 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const St
for (int i = 0; i < importers.size(); i++) {
List<String> local_exts;
importers[i]->get_recognized_extensions(&local_exts);
- for (List<String>::Element *F = local_exts.front(); F; F = F->next()) {
- if (p_extension.to_lower() == F->get() && importers[i]->get_priority() > priority) {
+ for (const String &F : local_exts) {
+ if (p_extension.to_lower() == F && importers[i]->get_priority() > priority) {
importer = importers[i];
priority = importers[i]->get_priority();
}
@@ -405,7 +418,7 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const St
}
String ResourceFormatImporter::get_import_base_path(const String &p_for_file) const {
- return ProjectSettings::IMPORTED_FILES_PATH.plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text());
+ return ProjectSettings::get_singleton()->get_imported_files_path().plus_file(p_for_file.get_file() + "-" + p_for_file.md5_text());
}
bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) const {
@@ -445,3 +458,8 @@ ResourceFormatImporter *ResourceFormatImporter::singleton = nullptr;
ResourceFormatImporter::ResourceFormatImporter() {
singleton = this;
}
+
+void ResourceImporter::_bind_methods() {
+ BIND_ENUM_CONSTANT(IMPORT_ORDER_DEFAULT);
+ BIND_ENUM_CONSTANT(IMPORT_ORDER_SCENE);
+}
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index 2ceeb176e5..a1cacbd306 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -42,6 +42,7 @@ class ResourceFormatImporter : public ResourceFormatLoader {
String importer;
String group_file;
Variant metadata;
+ uint64_t uid = ResourceUID::INVALID_ID;
};
Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = nullptr) const;
@@ -63,6 +64,8 @@ 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 ResourceUID::ID get_resource_uid(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);
@@ -96,6 +99,9 @@ public:
class ResourceImporter : public RefCounted {
GDCLASS(ResourceImporter, RefCounted);
+protected:
+ static void _bind_methods();
+
public:
virtual String get_importer_name() const = 0;
virtual String get_visible_name() const = 0;
@@ -103,7 +109,7 @@ public:
virtual String get_save_extension() const = 0;
virtual String get_resource_type() const = 0;
virtual float get_priority() const { return 1.0; }
- virtual int get_import_order() const { return 0; }
+ virtual int get_import_order() const { return IMPORT_ORDER_DEFAULT; }
virtual int get_format_version() const { return 0; }
struct ImportOption {
@@ -117,6 +123,11 @@ public:
ImportOption() {}
};
+ enum ImportOrder {
+ IMPORT_ORDER_DEFAULT = 0,
+ IMPORT_ORDER_SCENE = 100,
+ };
+
virtual bool has_advanced_options() const { return false; }
virtual void show_advanced_options(const String &p_path) {}
@@ -137,4 +148,6 @@ public:
virtual String get_import_settings_string() const { return String(); }
};
+VARIANT_ENUM_CAST(ResourceImporter::ImportOrder);
+
#endif // RESOURCE_IMPORTER_H
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index c5dfe1f2b0..2198761c2a 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -58,8 +58,8 @@ bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_
get_recognized_extensions_for_type(p_for_type, &extensions);
}
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- if (E->get().nocasecmp_to(extension) == 0) {
+ for (const String &E : extensions) {
+ if (E.nocasecmp_to(extension) == 0) {
return true;
}
}
@@ -68,22 +68,33 @@ bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_
}
bool ResourceFormatLoader::handles_type(const String &p_type) const {
- if (get_script_instance() && get_script_instance()->has_method("_handles_type")) {
- // I guess custom loaders for custom resources should use "Resource"
- return get_script_instance()->call("_handles_type", p_type);
+ bool success;
+ if (GDVIRTUAL_CALL(_handles_type, p_type, success)) {
+ return success;
}
return false;
}
String ResourceFormatLoader::get_resource_type(const String &p_path) const {
- if (get_script_instance() && get_script_instance()->has_method("_get_resource_type")) {
- return get_script_instance()->call("_get_resource_type", p_path);
+ String ret;
+
+ if (GDVIRTUAL_CALL(_get_resource_type, p_path, ret)) {
+ return ret;
}
return "";
}
+ResourceUID::ID ResourceFormatLoader::get_resource_uid(const String &p_path) const {
+ int64_t uid;
+ if (GDVIRTUAL_CALL(_get_resource_uid, p_path, uid)) {
+ return uid;
+ }
+
+ return ResourceUID::INVALID_ID;
+}
+
void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
if (p_type == "" || handles_type(p_type)) {
get_recognized_extensions(p_extensions);
@@ -97,27 +108,26 @@ void ResourceLoader::get_recognized_extensions_for_type(const String &p_type, Li
}
bool ResourceFormatLoader::exists(const String &p_path) const {
+ bool success;
+ if (GDVIRTUAL_CALL(_exists, p_path, success)) {
+ return success;
+ }
return FileAccess::exists(p_path); //by default just check file
}
void ResourceFormatLoader::get_recognized_extensions(List<String> *p_extensions) const {
- if (get_script_instance() && get_script_instance()->has_method("_get_recognized_extensions")) {
- PackedStringArray exts = get_script_instance()->call("_get_recognized_extensions");
-
- {
- const String *r = exts.ptr();
- for (int i = 0; i < exts.size(); ++i) {
- p_extensions->push_back(r[i]);
- }
+ PackedStringArray exts;
+ if (GDVIRTUAL_CALL(_get_recognized_extensions, exts)) {
+ const String *r = exts.ptr();
+ for (int i = 0; i < exts.size(); ++i) {
+ p_extensions->push_back(r[i]);
}
}
}
RES ResourceFormatLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- // Check user-defined loader if there's any. Hard fail if it returns an error.
- if (get_script_instance() && get_script_instance()->has_method("_load")) {
- Variant res = get_script_instance()->call("_load", p_path, p_original_path, p_use_sub_threads, p_cache_mode);
-
+ Variant res;
+ if (GDVIRTUAL_CALL(_load, p_path, p_original_path, p_use_sub_threads, p_cache_mode, res)) {
if (res.get_type() == Variant::INT) { // Error code, abort.
if (r_error) {
*r_error = (Error)res.operator int64_t();
@@ -135,48 +145,42 @@ RES ResourceFormatLoader::load(const String &p_path, const String &p_original_pa
}
void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
- if (get_script_instance() && get_script_instance()->has_method("_get_dependencies")) {
- PackedStringArray deps = get_script_instance()->call("_get_dependencies", p_path, p_add_types);
-
- {
- const String *r = deps.ptr();
- for (int i = 0; i < deps.size(); ++i) {
- p_dependencies->push_back(r[i]);
- }
+ PackedStringArray deps;
+ if (GDVIRTUAL_CALL(_get_dependencies, p_path, p_add_types, deps)) {
+ const String *r = deps.ptr();
+ for (int i = 0; i < deps.size(); ++i) {
+ p_dependencies->push_back(r[i]);
}
}
}
Error ResourceFormatLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) {
- if (get_script_instance() && get_script_instance()->has_method("_rename_dependencies")) {
- Dictionary deps_dict;
- for (Map<String, String>::Element *E = p_map.front(); E; E = E->next()) {
- deps_dict[E->key()] = E->value();
- }
+ Dictionary deps_dict;
+ for (KeyValue<String, String> E : p_map) {
+ deps_dict[E.key] = E.value;
+ }
- int64_t res = get_script_instance()->call("_rename_dependencies", deps_dict);
- return (Error)res;
+ int64_t err;
+ if (GDVIRTUAL_CALL(_rename_dependencies, p_path, deps_dict, err)) {
+ return (Error)err;
}
return OK;
}
void ResourceFormatLoader::_bind_methods() {
- {
- MethodInfo info = MethodInfo(Variant::NIL, "_load", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "original_path"), PropertyInfo(Variant::BOOL, "use_sub_threads"), PropertyInfo(Variant::INT, "cache_mode"));
- info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- BIND_VMETHOD(info);
- }
-
- BIND_VMETHOD(MethodInfo(Variant::PACKED_STRING_ARRAY, "_get_recognized_extensions"));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_handles_type", PropertyInfo(Variant::STRING_NAME, "typename")));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_resource_type", PropertyInfo(Variant::STRING, "path")));
- BIND_VMETHOD(MethodInfo("_get_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "add_types")));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_rename_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "renames")));
-
BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE);
BIND_ENUM_CONSTANT(CACHE_MODE_REUSE);
BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE);
+
+ GDVIRTUAL_BIND(_get_recognized_extensions);
+ GDVIRTUAL_BIND(_handles_type, "type");
+ GDVIRTUAL_BIND(_get_resource_type, "path");
+ GDVIRTUAL_BIND(_get_resource_uid, "path");
+ GDVIRTUAL_BIND(_get_dependencies, "path", "add_types");
+ GDVIRTUAL_BIND(_rename_dependencies, "path", "renames");
+ GDVIRTUAL_BIND(_exists, "path");
+ GDVIRTUAL_BIND(_load, "path", "original_path", "use_sub_threads", "cache_mode");
}
///////////////////////////////////
@@ -270,13 +274,18 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
thread_load_mutex->unlock();
}
-Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, const String &p_source_resource) {
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
+static String _validate_local_path(const String &p_path) {
+ ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(p_path);
+ if (uid != ResourceUID::INVALID_ID) {
+ return ResourceUID::get_singleton()->get_id_path(uid);
+ } else if (p_path.is_relative_path()) {
+ return "res://" + p_path;
} else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
+ return ProjectSettings::get_singleton()->localize_path(p_path);
}
+}
+Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, const String &p_source_resource) {
+ String local_path = _validate_local_path(p_path);
thread_load_mutex->lock();
@@ -399,12 +408,7 @@ float ResourceLoader::_dependency_get_progress(const String &p_path) {
}
ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, float *r_progress) {
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- }
+ String local_path = _validate_local_path(p_path);
thread_load_mutex->lock();
if (!thread_load_tasks.has(local_path)) {
@@ -424,12 +428,7 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const
}
RES ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) {
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- }
+ String local_path = _validate_local_path(p_path);
thread_load_mutex->lock();
if (!thread_load_tasks.has(local_path)) {
@@ -510,12 +509,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour
*r_error = ERR_CANT_OPEN;
}
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- }
+ String local_path = _validate_local_path(p_path);
if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
thread_load_mutex->lock();
@@ -612,12 +606,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour
}
bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) {
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- }
+ String local_path = _validate_local_path(p_path);
if (ResourceCache::has(local_path)) {
return true; // If cached, it probably exists
@@ -677,14 +666,7 @@ void ResourceLoader::remove_resource_format_loader(Ref<ResourceFormatLoader> p_f
}
int ResourceLoader::get_import_order(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);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -702,14 +684,7 @@ int ResourceLoader::get_import_order(const String &p_path) {
}
String ResourceLoader::get_import_group_file(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);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -727,14 +702,7 @@ String ResourceLoader::get_import_group_file(const String &p_path) {
}
bool ResourceLoader::is_import_valid(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);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -752,14 +720,7 @@ bool ResourceLoader::is_import_valid(const String &p_path) {
}
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);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -777,14 +738,7 @@ bool ResourceLoader::is_imported(const String &p_path) {
}
void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
- 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);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -800,14 +754,7 @@ void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_depe
}
Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) {
- 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);
- }
+ String local_path = _path_remap(_validate_local_path(p_path));
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path)) {
@@ -825,12 +772,7 @@ Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String
}
String ResourceLoader::get_resource_type(const String &p_path) {
- String local_path;
- if (p_path.is_rel_path()) {
- local_path = "res://" + p_path;
- } else {
- local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- }
+ String local_path = _validate_local_path(p_path);
for (int i = 0; i < loader_count; i++) {
String result = loader[i]->get_resource_type(local_path);
@@ -842,6 +784,19 @@ String ResourceLoader::get_resource_type(const String &p_path) {
return "";
}
+ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) {
+ String local_path = _validate_local_path(p_path);
+
+ for (int i = 0; i < loader_count; i++) {
+ ResourceUID::ID id = loader[i]->get_resource_uid(local_path);
+ if (id != ResourceUID::INVALID_ID) {
+ return id;
+ }
+ }
+
+ return ResourceUID::INVALID_ID;
+}
+
String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_remapped) {
String new_path = p_path;
@@ -978,15 +933,15 @@ void ResourceLoader::load_translation_remaps() {
Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps");
List<Variant> keys;
remaps.get_key_list(&keys);
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- Array langs = remaps[E->get()];
+ for (const Variant &E : keys) {
+ Array langs = remaps[E];
Vector<String> lang_remaps;
lang_remaps.resize(langs.size());
for (int i = 0; i < langs.size(); i++) {
lang_remaps.write[i] = langs[i];
}
- translation_remaps[String(E->get())] = lang_remaps;
+ translation_remaps[String(E)] = lang_remaps;
}
}
@@ -1071,8 +1026,7 @@ void ResourceLoader::add_custom_loaders() {
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
- for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
- StringName class_name = E->get();
+ for (const StringName &class_name : global_classes) {
StringName base_class = ScriptServer::get_global_class_native_base(class_name);
if (base_class == custom_loader_base_class) {
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index c656b9a69c..f1d9815635 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -32,6 +32,8 @@
#define RESOURCE_LOADER_H
#include "core/io/resource.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
#include "core/os/semaphore.h"
#include "core/os/thread.h"
@@ -40,14 +42,24 @@ class ResourceFormatLoader : public RefCounted {
public:
enum CacheMode {
- CACHE_MODE_IGNORE, //resource and subresources do not use path cache, no path is set into resource.
- CACHE_MODE_REUSE, //resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available
- CACHE_MODE_REPLACE, //resource and and subresource use path cache, but replace existing loaded resources when available with information from disk
+ CACHE_MODE_IGNORE, // Resource and subresources do not use path cache, no path is set into resource.
+ CACHE_MODE_REUSE, // Resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available.
+ CACHE_MODE_REPLACE, // Resource and subresource use path cache, but replace existing loaded resources when available with information from disk.
};
protected:
static void _bind_methods();
+ GDVIRTUAL0RC(Vector<String>, _get_recognized_extensions)
+ GDVIRTUAL1RC(bool, _handles_type, StringName)
+ GDVIRTUAL1RC(String, _get_resource_type, String)
+ GDVIRTUAL1RC(ResourceUID::ID, _get_resource_uid, String)
+ GDVIRTUAL2RC(Vector<String>, _get_dependencies, String, bool)
+ GDVIRTUAL2RC(int64_t, _rename_dependencies, String, Dictionary)
+ GDVIRTUAL1RC(bool, _exists, String)
+
+ GDVIRTUAL4RC(Variant, _load, String, String, bool, int)
+
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual bool exists(const String &p_path) const;
@@ -56,6 +68,7 @@ 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 ResourceUID::ID get_resource_uid(const String &p_path) const;
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; }
@@ -107,7 +120,7 @@ private:
friend class ResourceFormatImporter;
friend class ResourceInteractiveLoader;
- //internal load function
+ // Internal load function.
static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress);
static ResourceLoadedCallback _loaded_callback;
@@ -157,6 +170,7 @@ public:
static void add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front = false);
static void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader);
static String get_resource_type(const String &p_path);
+ static ResourceUID::ID get_resource_uid(const String &p_path);
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);
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 80cb85fba3..823b5f75b1 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -39,46 +39,40 @@ Ref<ResourceFormatSaver> ResourceSaver::saver[MAX_SAVERS];
int ResourceSaver::saver_count = 0;
bool ResourceSaver::timestamp_on_save = false;
ResourceSavedCallback ResourceSaver::save_callback = nullptr;
+ResourceSaverGetResourceIDForPath ResourceSaver::save_get_id_for_path = nullptr;
Error ResourceFormatSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
- if (get_script_instance() && get_script_instance()->has_method("_save")) {
- return (Error)get_script_instance()->call("_save", p_path, p_resource, p_flags).operator int64_t();
+ int64_t res;
+ if (GDVIRTUAL_CALL(_save, p_path, p_resource, p_flags, res)) {
+ return (Error)res;
}
return ERR_METHOD_NOT_FOUND;
}
bool ResourceFormatSaver::recognize(const RES &p_resource) const {
- if (get_script_instance() && get_script_instance()->has_method("_recognize")) {
- return get_script_instance()->call("_recognize", p_resource);
+ bool success;
+ if (GDVIRTUAL_CALL(_recognize, p_resource, success)) {
+ return success;
}
return false;
}
void ResourceFormatSaver::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
- if (get_script_instance() && get_script_instance()->has_method("_get_recognized_extensions")) {
- PackedStringArray exts = get_script_instance()->call("_get_recognized_extensions", p_resource);
-
- {
- const String *r = exts.ptr();
- for (int i = 0; i < exts.size(); ++i) {
- p_extensions->push_back(r[i]);
- }
+ PackedStringArray exts;
+ if (GDVIRTUAL_CALL(_get_recognized_extensions, p_resource, exts)) {
+ const String *r = exts.ptr();
+ for (int i = 0; i < exts.size(); ++i) {
+ p_extensions->push_back(r[i]);
}
}
}
void ResourceFormatSaver::_bind_methods() {
- {
- PropertyInfo arg0 = PropertyInfo(Variant::STRING, "path");
- PropertyInfo arg1 = PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource");
- PropertyInfo arg2 = PropertyInfo(Variant::INT, "flags");
- BIND_VMETHOD(MethodInfo(Variant::INT, "_save", arg0, arg1, arg2));
- }
-
- BIND_VMETHOD(MethodInfo(Variant::PACKED_STRING_ARRAY, "_get_recognized_extensions", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_recognize", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
+ GDVIRTUAL_BIND(_save, "path", "resource", "flags");
+ GDVIRTUAL_BIND(_recognize, "resource");
+ GDVIRTUAL_BIND(_get_recognized_extensions, "resource");
}
Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
@@ -94,8 +88,8 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t
bool recognized = false;
saver[i]->get_recognized_extensions(p_resource, &extensions);
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- if (E->get().nocasecmp_to(extension) == 0) {
+ for (const String &E : extensions) {
+ if (E.nocasecmp_to(extension) == 0) {
recognized = true;
}
}
@@ -236,8 +230,7 @@ void ResourceSaver::add_custom_savers() {
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
- for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
- StringName class_name = E->get();
+ for (const StringName &class_name : global_classes) {
StringName base_class = ScriptServer::get_global_class_native_base(class_name);
if (base_class == custom_saver_base_class) {
@@ -259,3 +252,14 @@ void ResourceSaver::remove_custom_savers() {
remove_resource_format_saver(custom_savers[i]);
}
}
+
+ResourceUID::ID ResourceSaver::get_resource_id_for_path(const String &p_path, bool p_generate) {
+ if (save_get_id_for_path) {
+ return save_get_id_for_path(p_path, p_generate);
+ }
+ return ResourceUID::INVALID_ID;
+}
+
+void ResourceSaver::set_get_resource_id_for_path(ResourceSaverGetResourceIDForPath p_callback) {
+ save_get_id_for_path = p_callback;
+}
diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h
index 07154aac4d..fcde835dab 100644
--- a/core/io/resource_saver.h
+++ b/core/io/resource_saver.h
@@ -32,6 +32,8 @@
#define RESOURCE_SAVER_H
#include "core/io/resource.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
class ResourceFormatSaver : public RefCounted {
GDCLASS(ResourceFormatSaver, RefCounted);
@@ -39,6 +41,10 @@ class ResourceFormatSaver : public RefCounted {
protected:
static void _bind_methods();
+ GDVIRTUAL3R(int64_t, _save, String, RES, uint32_t)
+ GDVIRTUAL1RC(bool, _recognize, RES)
+ GDVIRTUAL1RC(Vector<String>, _get_recognized_extensions, RES)
+
public:
virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
virtual bool recognize(const RES &p_resource) const;
@@ -48,6 +54,7 @@ public:
};
typedef void (*ResourceSavedCallback)(Ref<Resource> p_resource, const String &p_path);
+typedef ResourceUID::ID (*ResourceSaverGetResourceIDForPath)(const String &p_path, bool p_generate);
class ResourceSaver {
enum {
@@ -58,6 +65,7 @@ class ResourceSaver {
static int saver_count;
static bool timestamp_on_save;
static ResourceSavedCallback save_callback;
+ static ResourceSaverGetResourceIDForPath save_get_id_for_path;
static Ref<ResourceFormatSaver> _find_custom_resource_format_saver(String path);
@@ -80,7 +88,10 @@ public:
static void set_timestamp_on_save(bool p_timestamp) { timestamp_on_save = p_timestamp; }
static bool get_timestamp_on_save() { return timestamp_on_save; }
+ static ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false);
+
static void set_save_callback(ResourceSavedCallback p_callback);
+ static void set_get_resource_id_for_path(ResourceSaverGetResourceIDForPath p_callback);
static bool add_custom_resource_format_saver(String script_path);
static void remove_custom_resource_format_saver(String script_path);
diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp
new file mode 100644
index 0000000000..b7d01712ff
--- /dev/null
+++ b/core/io/resource_uid.cpp
@@ -0,0 +1,266 @@
+/*************************************************************************/
+/* resource_uid.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resource_uid.h"
+
+#include "core/config/project_settings.h"
+#include "core/crypto/crypto.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+
+static constexpr uint32_t char_count = ('z' - 'a');
+static constexpr uint32_t base = char_count + ('9' - '0');
+
+String ResourceUID::get_cache_file() {
+ return ProjectSettings::get_singleton()->get_project_data_path().plus_file("uid_cache.bin");
+}
+
+String ResourceUID::id_to_text(ID p_id) const {
+ if (p_id < 0) {
+ return "uid://<invalid>";
+ }
+ String txt;
+
+ while (p_id) {
+ uint32_t c = p_id % base;
+ if (c < char_count) {
+ txt = String::chr('a' + c) + txt;
+ } else {
+ txt = String::chr('0' + (c - char_count)) + txt;
+ }
+ p_id /= base;
+ }
+
+ return "uid://" + txt;
+}
+
+ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const {
+ if (!p_text.begins_with("uid://") || p_text == "uid://<invalid>") {
+ return INVALID_ID;
+ }
+
+ uint32_t l = p_text.length();
+ uint64_t uid = 0;
+ for (uint32_t i = 6; i < l; i++) {
+ uid *= base;
+ uint32_t c = p_text[i];
+ if (c >= 'a' && c <= 'z') {
+ uid += c - 'a';
+ } else if (c >= '0' && c <= '9') {
+ uid += c - '0' + char_count;
+ } else {
+ return INVALID_ID;
+ }
+ }
+ return ID(uid & 0x7FFFFFFFFFFFFFFF);
+}
+
+ResourceUID::ID ResourceUID::create_id() const {
+ mutex.lock();
+ if (crypto.is_null()) {
+ crypto = Ref<Crypto>(Crypto::create());
+ }
+ mutex.unlock();
+ while (true) {
+ PackedByteArray bytes = crypto->generate_random_bytes(8);
+ ERR_FAIL_COND_V(bytes.size() != 8, INVALID_ID);
+ const uint64_t *ptr64 = (const uint64_t *)bytes.ptr();
+ ID id = int64_t((*ptr64) & 0x7FFFFFFFFFFFFFFF);
+ mutex.lock();
+ bool exists = unique_ids.has(id);
+ mutex.unlock();
+ if (!exists) {
+ return id;
+ }
+ }
+}
+
+bool ResourceUID::has_id(ID p_id) const {
+ MutexLock l(mutex);
+ return unique_ids.has(p_id);
+}
+void ResourceUID::add_id(ID p_id, const String &p_path) {
+ MutexLock l(mutex);
+ ERR_FAIL_COND(unique_ids.has(p_id));
+ Cache c;
+ c.cs = p_path.utf8();
+ unique_ids[p_id] = c;
+ changed = true;
+}
+
+void ResourceUID::set_id(ID p_id, const String &p_path) {
+ MutexLock l(mutex);
+ ERR_FAIL_COND(!unique_ids.has(p_id));
+ CharString cs = p_path.utf8();
+ if (strcmp(cs.ptr(), unique_ids[p_id].cs.ptr()) != 0) {
+ unique_ids[p_id].cs = cs;
+ unique_ids[p_id].saved_to_cache = false; //changed
+ changed = true;
+ }
+}
+
+String ResourceUID::get_id_path(ID p_id) const {
+ MutexLock l(mutex);
+ ERR_FAIL_COND_V(!unique_ids.has(p_id), String());
+ const CharString &cs = unique_ids[p_id].cs;
+ return String::utf8(cs.ptr());
+}
+void ResourceUID::remove_id(ID p_id) {
+ MutexLock l(mutex);
+ ERR_FAIL_COND(!unique_ids.has(p_id));
+ unique_ids.erase(p_id);
+}
+
+Error ResourceUID::save_to_cache() {
+ String cache_file = get_cache_file();
+ if (!FileAccess::exists(cache_file)) {
+ DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ d->make_dir_recursive(String(cache_file).get_base_dir()); //ensure base dir exists
+ }
+
+ FileAccessRef f = FileAccess::open(cache_file, FileAccess::WRITE);
+ if (!f) {
+ return ERR_CANT_OPEN;
+ }
+
+ MutexLock l(mutex);
+ f->store_32(unique_ids.size());
+
+ cache_entries = 0;
+
+ for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {
+ f->store_64(E.key());
+ uint32_t s = E.get().cs.length();
+ f->store_32(s);
+ f->store_buffer((const uint8_t *)E.get().cs.ptr(), s);
+ E.get().saved_to_cache = true;
+ cache_entries++;
+ }
+
+ changed = false;
+ return OK;
+}
+
+Error ResourceUID::load_from_cache() {
+ FileAccessRef f = FileAccess::open(get_cache_file(), FileAccess::READ);
+ if (!f) {
+ return ERR_CANT_OPEN;
+ }
+
+ MutexLock l(mutex);
+ unique_ids.clear();
+
+ uint32_t entry_count = f->get_32();
+ for (uint32_t i = 0; i < entry_count; i++) {
+ int64_t id = f->get_64();
+ int32_t len = f->get_32();
+ Cache c;
+ c.cs.resize(len + 1);
+ ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // out of memory
+ c.cs[len] = 0;
+ int32_t rl = f->get_buffer((uint8_t *)c.cs.ptrw(), len);
+ ERR_FAIL_COND_V(rl != len, ERR_FILE_CORRUPT);
+
+ c.saved_to_cache = true;
+ unique_ids[id] = c;
+ }
+
+ cache_entries = entry_count;
+ changed = false;
+ return OK;
+}
+
+Error ResourceUID::update_cache() {
+ if (!changed) {
+ return OK;
+ }
+
+ if (cache_entries == 0) {
+ return save_to_cache();
+ }
+ MutexLock l(mutex);
+
+ FileAccess *f = nullptr;
+ for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) {
+ if (!E.get().saved_to_cache) {
+ if (f == nullptr) {
+ f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); //append
+ if (!f) {
+ return ERR_CANT_OPEN;
+ }
+ f->seek_end();
+ }
+ f->store_64(E.key());
+ uint32_t s = E.get().cs.length();
+ f->store_32(s);
+ f->store_buffer((const uint8_t *)E.get().cs.ptr(), s);
+ E.get().saved_to_cache = true;
+ cache_entries++;
+ }
+ }
+
+ if (f != nullptr) {
+ f->seek(0);
+ f->store_32(cache_entries); //update amount of entries
+ f->close();
+ memdelete(f);
+ }
+
+ changed = false;
+
+ return OK;
+}
+
+void ResourceUID::clear() {
+ cache_entries = 0;
+ unique_ids.clear();
+ changed = false;
+}
+void ResourceUID::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("id_to_text", "id"), &ResourceUID::id_to_text);
+ ClassDB::bind_method(D_METHOD("text_to_id", "text_id"), &ResourceUID::text_to_id);
+
+ ClassDB::bind_method(D_METHOD("create_id"), &ResourceUID::create_id);
+
+ ClassDB::bind_method(D_METHOD("has_id", "id"), &ResourceUID::has_id);
+ ClassDB::bind_method(D_METHOD("add_id", "id", "path"), &ResourceUID::add_id);
+ ClassDB::bind_method(D_METHOD("set_id", "id", "path"), &ResourceUID::set_id);
+ ClassDB::bind_method(D_METHOD("get_id_path", "id"), &ResourceUID::get_id_path);
+ ClassDB::bind_method(D_METHOD("remove_id", "id"), &ResourceUID::remove_id);
+
+ BIND_CONSTANT(INVALID_ID)
+}
+ResourceUID *ResourceUID::singleton = nullptr;
+ResourceUID::ResourceUID() {
+ ERR_FAIL_COND(singleton != nullptr);
+ singleton = this;
+}
+ResourceUID::~ResourceUID() {
+}
diff --git a/core/io/multiplayer_peer.h b/core/io/resource_uid.h
index 432f47280f..2f1bfdf243 100644
--- a/core/io/multiplayer_peer.h
+++ b/core/io/resource_uid.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* multiplayer_peer.h */
+/* resource_uid.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,55 +28,62 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef NETWORKED_MULTIPLAYER_PEER_H
-#define NETWORKED_MULTIPLAYER_PEER_H
+#ifndef RESOURCE_UUID_H
+#define RESOURCE_UUID_H
-#include "core/io/packet_peer.h"
-
-class MultiplayerPeer : public PacketPeer {
- GDCLASS(MultiplayerPeer, PacketPeer);
-
-protected:
- static void _bind_methods();
+#include "core/object/ref_counted.h"
+#include "core/string/string_name.h"
+#include "core/templates/ordered_hash_map.h"
+class Crypto;
+class ResourceUID : public Object {
+ GDCLASS(ResourceUID, Object)
public:
+ typedef int64_t ID;
enum {
- TARGET_PEER_BROADCAST = 0,
- TARGET_PEER_SERVER = 1
- };
- enum TransferMode {
- TRANSFER_MODE_UNRELIABLE,
- TRANSFER_MODE_UNRELIABLE_ORDERED,
- TRANSFER_MODE_RELIABLE,
+ INVALID_ID = -1
};
- enum ConnectionStatus {
- CONNECTION_DISCONNECTED,
- CONNECTION_CONNECTING,
- CONNECTION_CONNECTED,
+ static String get_cache_file();
+
+private:
+ mutable Ref<Crypto> crypto;
+ Mutex mutex;
+ struct Cache {
+ CharString cs;
+ bool saved_to_cache = false;
};
- virtual void set_transfer_mode(TransferMode p_mode) = 0;
- virtual TransferMode get_transfer_mode() const = 0;
- virtual void set_target_peer(int p_peer_id) = 0;
+ OrderedHashMap<ID, Cache> unique_ids; //unique IDs and utf8 paths (less memory used)
+ static ResourceUID *singleton;
- virtual int get_packet_peer() const = 0;
+ uint32_t cache_entries = 0;
+ bool changed = false;
- virtual bool is_server() const = 0;
+protected:
+ static void _bind_methods();
- virtual void poll() = 0;
+public:
+ String id_to_text(ID p_id) const;
+ ID text_to_id(const String &p_text) const;
- virtual int get_unique_id() const = 0;
+ ID create_id() const;
+ bool has_id(ID p_id) const;
+ void add_id(ID p_id, const String &p_path);
+ void set_id(ID p_id, const String &p_path);
+ String get_id_path(ID p_id) const;
+ void remove_id(ID p_id);
- virtual void set_refuse_new_connections(bool p_enable) = 0;
- virtual bool is_refusing_new_connections() const = 0;
+ Error load_from_cache();
+ Error save_to_cache();
+ Error update_cache();
- virtual ConnectionStatus get_connection_status() const = 0;
+ void clear();
- MultiplayerPeer() {}
-};
+ static ResourceUID *get_singleton() { return singleton; }
-VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode)
-VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus)
+ ResourceUID();
+ ~ResourceUID();
+};
-#endif // NETWORKED_MULTIPLAYER_PEER_H
+#endif // RESOURCEUUID_H
diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp
index 27f8d4e88f..8ab025dda1 100644
--- a/core/io/stream_peer.cpp
+++ b/core/io/stream_peer.cpp
@@ -410,6 +410,63 @@ void StreamPeer::_bind_methods() {
////////////////////////////////
+int StreamPeerExtension::get_available_bytes() const {
+ int count;
+ if (GDVIRTUAL_CALL(_get_available_bytes, count)) {
+ return count;
+ }
+ WARN_PRINT_ONCE("StreamPeerExtension::_get_available_bytes is unimplemented!");
+ return -1;
+}
+
+Error StreamPeerExtension::get_data(uint8_t *r_buffer, int p_bytes) {
+ int err;
+ int received = 0;
+ if (GDVIRTUAL_CALL(_get_data, r_buffer, p_bytes, &received, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("StreamPeerExtension::_get_data is unimplemented!");
+ return FAILED;
+}
+
+Error StreamPeerExtension::get_partial_data(uint8_t *r_buffer, int p_bytes, int &r_received) {
+ int err;
+ if (GDVIRTUAL_CALL(_get_partial_data, r_buffer, p_bytes, &r_received, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("StreamPeerExtension::_get_partial_data is unimplemented!");
+ return FAILED;
+}
+
+Error StreamPeerExtension::put_data(const uint8_t *p_data, int p_bytes) {
+ int err;
+ int sent = 0;
+ if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &sent, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("StreamPeerExtension::_put_data is unimplemented!");
+ return FAILED;
+}
+
+Error StreamPeerExtension::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
+ int err;
+ if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &r_sent, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("StreamPeerExtension::_put_partial_data is unimplemented!");
+ return FAILED;
+}
+
+void StreamPeerExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_data, "r_buffer", "r_bytes", "r_received");
+ GDVIRTUAL_BIND(_get_partial_data, "r_buffer", "r_bytes", "r_received");
+ GDVIRTUAL_BIND(_put_data, "p_data", "p_bytes", "r_sent");
+ GDVIRTUAL_BIND(_put_partial_data, "p_data", "p_bytes", "r_sent");
+ GDVIRTUAL_BIND(_get_available_bytes);
+}
+
+////////////////////////////////
+
void StreamPeerBuffer::_bind_methods() {
ClassDB::bind_method(D_METHOD("seek", "position"), &StreamPeerBuffer::seek);
ClassDB::bind_method(D_METHOD("get_size"), &StreamPeerBuffer::get_size);
diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h
index effc3850af..89432951c5 100644
--- a/core/io/stream_peer.h
+++ b/core/io/stream_peer.h
@@ -33,6 +33,10 @@
#include "core/object/ref_counted.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
+#include "core/variant/native_ptr.h"
+
class StreamPeer : public RefCounted {
GDCLASS(StreamPeer, RefCounted);
OBJ_CATEGORY("Networking");
@@ -58,6 +62,7 @@ public:
virtual int get_available_bytes() const = 0;
+ /* helpers */
void set_big_endian(bool p_big_endian);
bool is_big_endian_enabled() const;
@@ -92,6 +97,26 @@ public:
StreamPeer() {}
};
+class StreamPeerExtension : public StreamPeer {
+ GDCLASS(StreamPeerExtension, StreamPeer);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Error put_data(const uint8_t *p_data, int p_bytes) override;
+ virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
+ virtual Error get_data(uint8_t *p_buffer, int p_bytes) override;
+ virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
+ virtual int get_available_bytes() const override;
+
+ GDVIRTUAL3R(int, _put_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL3R(int, _put_partial_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL3R(int, _get_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL3R(int, _get_partial_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL0RC(int, _get_available_bytes);
+};
+
class StreamPeerBuffer : public StreamPeer {
GDCLASS(StreamPeerBuffer, StreamPeer);
diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp
index fb4c76aa7a..24808cc8d6 100644
--- a/core/io/zip_io.cpp
+++ b/core/io/zip_io.cpp
@@ -100,7 +100,7 @@ int zipio_testerror(voidpf opaque, voidpf stream) {
}
voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) {
- voidpf ptr = memalloc(items * size);
+ voidpf ptr = memalloc((size_t)items * size);
memset(ptr, 0, items * size);
return ptr;
}