diff options
81 files changed, 1720 insertions, 407 deletions
diff --git a/.gitignore b/.gitignore index f43f68f25f..0ee2a8b382 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Godot auto generated files *.gen.* +.import/ # Documentation generated by doxygen or from classes.xml doc/_build/ diff --git a/.travis.yml b/.travis.yml index e16f1ca8a7..305544d821 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,9 +84,9 @@ matrix: os: osx compiler: clang - - name: Linux headless editor (release_debug, GCC 9) + - name: Linux headless editor (release_debug, GCC 9, testing project exporting and script running) stage: build - env: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc-9 MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" EXTRA_ARGS="warnings=extra werror=yes" + env: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc-9 MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" EXTRA_ARGS="warnings=extra werror=yes" TEST_PROJECT=yes os: linux compiler: gcc-9 addons: @@ -136,4 +136,10 @@ script: sh ./misc/travis/clang-format.sh; else scons -j2 CC=$CC CXX=$CXX platform=$PLATFORM tools=$TOOLS target=$TARGET $OPTIONS $EXTRA_ARGS; + + if [ "$TEST_PROJECT" = "yes" ]; then + git clone --depth 1 "https://github.com/godotengine/godot-tests.git"; + sed -i "s:custom_template/release=\"\":custom_template/release=\"$(readlink -e bin/godot_server.x11.opt.tools.64)\":" godot-tests/tests/project_export/export_presets.cfg; + godot-tests/tests/project_export/test_project.sh "bin/godot_server.x11.opt.tools.64"; + fi fi diff --git a/core/SCsub b/core/SCsub index 85e5f1b089..ed9a0a231d 100644 --- a/core/SCsub +++ b/core/SCsub @@ -159,6 +159,7 @@ env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"] # Chain load SCsubs SConscript('os/SCsub') SConscript('math/SCsub') +SConscript('crypto/SCsub') SConscript('io/SCsub') SConscript('bind/SCsub') diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 56369a3cc6..8e0d156438 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -30,11 +30,11 @@ #include "core_bind.h" +#include "core/crypto/crypto_core.h" #include "core/io/file_access_compressed.h" #include "core/io/file_access_encrypted.h" #include "core/io/json.h" #include "core/io/marshalls.h" -#include "core/math/crypto_core.h" #include "core/math/geometry.h" #include "core/os/keyboard.h" #include "core/os/os.h" diff --git a/core/crypto/SCsub b/core/crypto/SCsub new file mode 100644 index 0000000000..0a3f05d87a --- /dev/null +++ b/core/crypto/SCsub @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +Import('env') + +env_crypto = env.Clone() + +is_builtin = env["builtin_mbedtls"] +has_module = env["module_mbedtls_enabled"] + +if is_builtin or not has_module: + # Use our headers for builtin or if the module is not going to be compiled. + # We decided not to depend on system mbedtls just for these few files that can + # be easily extracted. + env_crypto.Prepend(CPPPATH=["#thirdparty/mbedtls/include"]) + +# MbedTLS core functions (for CryptoCore). +# If the mbedtls module is compiled we don't need to add the .c files with our +# custom config since they will be built by the module itself. +# Only if the module is not enabled, we must compile here the required sources +# to make a "light" build with only the necessary mbedtls files. +if not has_module: + env_thirdparty = env_crypto.Clone() + env_thirdparty.disable_warnings() + # Custom config file + env_thirdparty.Append(CPPDEFINES=[('MBEDTLS_CONFIG_FILE', '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')]) + thirdparty_mbedtls_dir = "#thirdparty/mbedtls/library/" + thirdparty_mbedtls_sources = [ + "aes.c", + "base64.c", + "md5.c", + "sha1.c", + "sha256.c", + "godot_core_mbedtls_platform.c" + ] + thirdparty_mbedtls_sources = [thirdparty_mbedtls_dir + file for file in thirdparty_mbedtls_sources] + env_thirdparty.add_source_files(env.core_sources, thirdparty_mbedtls_sources) + +env_crypto.add_source_files(env.core_sources, "*.cpp") diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp new file mode 100644 index 0000000000..925a01b36a --- /dev/null +++ b/core/crypto/crypto.cpp @@ -0,0 +1,170 @@ +/*************************************************************************/ +/* crypto.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "crypto.h" + +#include "core/engine.h" +#include "core/io/certs_compressed.gen.h" +#include "core/io/compression.h" + +/// Resources + +CryptoKey *(*CryptoKey::_create)() = NULL; +CryptoKey *CryptoKey::create() { + if (_create) + return _create(); + return NULL; +} + +void CryptoKey::_bind_methods() { + ClassDB::bind_method(D_METHOD("save", "path"), &CryptoKey::save); + ClassDB::bind_method(D_METHOD("load", "path"), &CryptoKey::load); +} + +X509Certificate *(*X509Certificate::_create)() = NULL; +X509Certificate *X509Certificate::create() { + if (_create) + return _create(); + return NULL; +} + +void X509Certificate::_bind_methods() { + ClassDB::bind_method(D_METHOD("save", "path"), &X509Certificate::save); + ClassDB::bind_method(D_METHOD("load", "path"), &X509Certificate::load); +} + +/// Crypto + +void (*Crypto::_load_default_certificates)(String p_path) = NULL; +Crypto *(*Crypto::_create)() = NULL; +Crypto *Crypto::create() { + if (_create) + return _create(); + return memnew(Crypto); +} + +void Crypto::load_default_certificates(String p_path) { + + if (_load_default_certificates) + _load_default_certificates(p_path); +} + +void Crypto::_bind_methods() { + ClassDB::bind_method(D_METHOD("generate_random_bytes", "size"), &Crypto::generate_random_bytes); + ClassDB::bind_method(D_METHOD("generate_rsa", "size"), &Crypto::generate_rsa); + ClassDB::bind_method(D_METHOD("generate_self_signed_certificate", "key", "issuer_name", "not_before", "not_after"), &Crypto::generate_self_signed_certificate, DEFVAL("CN=myserver,O=myorganisation,C=IT"), DEFVAL("20140101000000"), DEFVAL("20340101000000")); +} + +PoolByteArray Crypto::generate_random_bytes(int p_bytes) { + ERR_FAIL_V_MSG(PoolByteArray(), "generate_random_bytes is not available when mbedtls module is disabled."); +} + +Ref<CryptoKey> Crypto::generate_rsa(int p_bytes) { + ERR_FAIL_V_MSG(NULL, "generate_rsa is not available when mbedtls module is disabled."); +} + +Ref<X509Certificate> Crypto::generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) { + ERR_FAIL_V_MSG(NULL, "generate_self_signed_certificate is not available when mbedtls module is disabled."); +} + +Crypto::Crypto() { +} + +/// Resource loader/saver + +RES ResourceFormatLoaderCrypto::load(const String &p_path, const String &p_original_path, Error *r_error) { + + String el = p_path.get_extension().to_lower(); + if (el == "crt") { + X509Certificate *cert = X509Certificate::create(); + if (cert) + cert->load(p_path); + return cert; + } else if (el == "key") { + CryptoKey *key = CryptoKey::create(); + if (key) + key->load(p_path); + return key; + } + return NULL; +} + +void ResourceFormatLoaderCrypto::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("crt"); + p_extensions->push_back("key"); +} + +bool ResourceFormatLoaderCrypto::handles_type(const String &p_type) const { + + return p_type == "X509Certificate" || p_type == "CryptoKey"; +} + +String ResourceFormatLoaderCrypto::get_resource_type(const String &p_path) const { + + String el = p_path.get_extension().to_lower(); + if (el == "crt") + return "X509Certificate"; + else if (el == "key") + return "CryptoKey"; + return ""; +} + +Error ResourceFormatSaverCrypto::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { + + Error err; + Ref<X509Certificate> cert = p_resource; + Ref<CryptoKey> key = p_resource; + if (cert.is_valid()) { + err = cert->save(p_path); + } else if (key.is_valid()) { + err = key->save(p_path); + } else { + ERR_FAIL_V(ERR_INVALID_PARAMETER); + } + ERR_FAIL_COND_V(err != OK, err); + return OK; +} + +void ResourceFormatSaverCrypto::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { + + const X509Certificate *cert = Object::cast_to<X509Certificate>(*p_resource); + const CryptoKey *key = Object::cast_to<CryptoKey>(*p_resource); + if (cert) { + p_extensions->push_back("crt"); + } + if (key) { + p_extensions->push_back("key"); + } +} +bool ResourceFormatSaverCrypto::recognize(const RES &p_resource) const { + + return Object::cast_to<X509Certificate>(*p_resource) || Object::cast_to<CryptoKey>(*p_resource); +} diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h new file mode 100644 index 0000000000..2de81f5b57 --- /dev/null +++ b/core/crypto/crypto.h @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* crypto.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 CRYPTO_H +#define CRYPTO_H + +#include "core/reference.h" +#include "core/resource.h" + +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" + +class CryptoKey : public Resource { + GDCLASS(CryptoKey, Resource); + +protected: + static void _bind_methods(); + static CryptoKey *(*_create)(); + +public: + static CryptoKey *create(); + virtual Error load(String p_path) = 0; + virtual Error save(String p_path) = 0; +}; + +class X509Certificate : public Resource { + GDCLASS(X509Certificate, Resource); + +protected: + static void _bind_methods(); + static X509Certificate *(*_create)(); + +public: + static X509Certificate *create(); + virtual Error load(String p_path) = 0; + virtual Error load_from_memory(const uint8_t *p_buffer, int p_len) = 0; + virtual Error save(String p_path) = 0; +}; + +class Crypto : public Reference { + GDCLASS(Crypto, Reference); + +protected: + static void _bind_methods(); + static Crypto *(*_create)(); + static void (*_load_default_certificates)(String p_path); + +public: + static Crypto *create(); + static void load_default_certificates(String p_path); + + virtual PoolByteArray generate_random_bytes(int p_bytes); + virtual Ref<CryptoKey> generate_rsa(int p_bytes); + virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after); + + Crypto(); +}; + +class ResourceFormatLoaderCrypto : public ResourceFormatLoader { + GDCLASS(ResourceFormatLoaderCrypto, ResourceFormatLoader); + +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + 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; +}; + +class ResourceFormatSaverCrypto : public ResourceFormatSaver { + GDCLASS(ResourceFormatSaverCrypto, ResourceFormatSaver); + +public: + virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const RES &p_resource) const; +}; + +#endif // CRYPTO_H diff --git a/core/math/crypto_core.cpp b/core/crypto/crypto_core.cpp index d7ba54e469..51c2e3c9e5 100644 --- a/core/math/crypto_core.cpp +++ b/core/crypto/crypto_core.cpp @@ -52,7 +52,7 @@ Error CryptoCore::MD5Context::start() { return ret ? FAILED : OK; } -Error CryptoCore::MD5Context::update(uint8_t *p_src, size_t p_len) { +Error CryptoCore::MD5Context::update(const uint8_t *p_src, size_t p_len) { int ret = mbedtls_md5_update_ret((mbedtls_md5_context *)ctx, p_src, p_len); return ret ? FAILED : OK; } @@ -62,6 +62,32 @@ Error CryptoCore::MD5Context::finish(unsigned char r_hash[16]) { return ret ? FAILED : OK; } +// SHA1 +CryptoCore::SHA1Context::SHA1Context() { + ctx = memalloc(sizeof(mbedtls_sha1_context)); + mbedtls_sha1_init((mbedtls_sha1_context *)ctx); +} + +CryptoCore::SHA1Context::~SHA1Context() { + mbedtls_sha1_free((mbedtls_sha1_context *)ctx); + memfree((mbedtls_sha1_context *)ctx); +} + +Error CryptoCore::SHA1Context::start() { + int ret = mbedtls_sha1_starts_ret((mbedtls_sha1_context *)ctx); + return ret ? FAILED : OK; +} + +Error CryptoCore::SHA1Context::update(const uint8_t *p_src, size_t p_len) { + int ret = mbedtls_sha1_update_ret((mbedtls_sha1_context *)ctx, p_src, p_len); + return ret ? FAILED : OK; +} + +Error CryptoCore::SHA1Context::finish(unsigned char r_hash[20]) { + int ret = mbedtls_sha1_finish_ret((mbedtls_sha1_context *)ctx, r_hash); + return ret ? FAILED : OK; +} + // SHA256 CryptoCore::SHA256Context::SHA256Context() { ctx = memalloc(sizeof(mbedtls_sha256_context)); @@ -78,12 +104,12 @@ Error CryptoCore::SHA256Context::start() { return ret ? FAILED : OK; } -Error CryptoCore::SHA256Context::update(uint8_t *p_src, size_t p_len) { +Error CryptoCore::SHA256Context::update(const uint8_t *p_src, size_t p_len) { int ret = mbedtls_sha256_update_ret((mbedtls_sha256_context *)ctx, p_src, p_len); return ret ? FAILED : OK; } -Error CryptoCore::SHA256Context::finish(unsigned char r_hash[16]) { +Error CryptoCore::SHA256Context::finish(unsigned char r_hash[32]) { int ret = mbedtls_sha256_finish_ret((mbedtls_sha256_context *)ctx, r_hash); return ret ? FAILED : OK; } diff --git a/core/math/crypto_core.h b/core/crypto/crypto_core.h index e28cb5a792..c859d612d4 100644 --- a/core/math/crypto_core.h +++ b/core/crypto/crypto_core.h @@ -46,10 +46,24 @@ public: ~MD5Context(); Error start(); - Error update(uint8_t *p_src, size_t p_len); + Error update(const uint8_t *p_src, size_t p_len); Error finish(unsigned char r_hash[16]); }; + class SHA1Context { + + private: + void *ctx; // To include, or not to include... + + public: + SHA1Context(); + ~SHA1Context(); + + Error start(); + Error update(const uint8_t *p_src, size_t p_len); + Error finish(unsigned char r_hash[20]); + }; + class SHA256Context { private: @@ -60,8 +74,8 @@ public: ~SHA256Context(); Error start(); - Error update(uint8_t *p_src, size_t p_len); - Error finish(unsigned char r_hash[16]); + Error update(const uint8_t *p_src, size_t p_len); + Error finish(unsigned char r_hash[32]); }; class AESContext { diff --git a/core/crypto/hashing_context.cpp b/core/crypto/hashing_context.cpp new file mode 100644 index 0000000000..bdccb258dd --- /dev/null +++ b/core/crypto/hashing_context.cpp @@ -0,0 +1,137 @@ +/*************************************************************************/ +/* hashing_context.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "hashing_context.h" + +#include "core/crypto/crypto_core.h" + +Error HashingContext::start(HashType p_type) { + ERR_FAIL_COND_V(ctx != NULL, ERR_ALREADY_IN_USE); + _create_ctx(p_type); + ERR_FAIL_COND_V(ctx == NULL, ERR_UNAVAILABLE); + switch (type) { + case HASH_MD5: + return ((CryptoCore::MD5Context *)ctx)->start(); + case HASH_SHA1: + return ((CryptoCore::SHA1Context *)ctx)->start(); + case HASH_SHA256: + return ((CryptoCore::SHA256Context *)ctx)->start(); + } + return ERR_UNAVAILABLE; +} + +Error HashingContext::update(PoolByteArray p_chunk) { + ERR_FAIL_COND_V(ctx == NULL, ERR_UNCONFIGURED); + size_t len = p_chunk.size(); + PoolByteArray::Read r = p_chunk.read(); + switch (type) { + case HASH_MD5: + return ((CryptoCore::MD5Context *)ctx)->update(&r[0], len); + case HASH_SHA1: + return ((CryptoCore::SHA1Context *)ctx)->update(&r[0], len); + case HASH_SHA256: + return ((CryptoCore::SHA256Context *)ctx)->update(&r[0], len); + } + return ERR_UNAVAILABLE; +} + +PoolByteArray HashingContext::finish() { + ERR_FAIL_COND_V(ctx == NULL, PoolByteArray()); + PoolByteArray out; + Error err = FAILED; + switch (type) { + case HASH_MD5: + out.resize(16); + err = ((CryptoCore::MD5Context *)ctx)->finish(out.write().ptr()); + break; + case HASH_SHA1: + out.resize(20); + err = ((CryptoCore::SHA1Context *)ctx)->finish(out.write().ptr()); + break; + case HASH_SHA256: + out.resize(32); + err = ((CryptoCore::SHA256Context *)ctx)->finish(out.write().ptr()); + break; + } + _delete_ctx(); + ERR_FAIL_COND_V(err != OK, PoolByteArray()); + return out; +} + +void HashingContext::_create_ctx(HashType p_type) { + type = p_type; + switch (type) { + case HASH_MD5: + ctx = memnew(CryptoCore::MD5Context); + break; + case HASH_SHA1: + ctx = memnew(CryptoCore::SHA1Context); + break; + case HASH_SHA256: + ctx = memnew(CryptoCore::SHA256Context); + break; + default: + ctx = NULL; + } +} + +void HashingContext::_delete_ctx() { + return; + switch (type) { + case HASH_MD5: + memdelete((CryptoCore::MD5Context *)ctx); + break; + case HASH_SHA1: + memdelete((CryptoCore::SHA1Context *)ctx); + break; + case HASH_SHA256: + memdelete((CryptoCore::SHA256Context *)ctx); + break; + } + ctx = NULL; +} + +void HashingContext::_bind_methods() { + ClassDB::bind_method(D_METHOD("start", "type"), &HashingContext::start); + ClassDB::bind_method(D_METHOD("update", "chunk"), &HashingContext::update); + ClassDB::bind_method(D_METHOD("finish"), &HashingContext::finish); + BIND_ENUM_CONSTANT(HASH_MD5); + BIND_ENUM_CONSTANT(HASH_SHA1); + BIND_ENUM_CONSTANT(HASH_SHA256); +} + +HashingContext::HashingContext() { + ctx = NULL; +} + +HashingContext::~HashingContext() { + if (ctx != NULL) + _delete_ctx(); +} diff --git a/core/crypto/hashing_context.h b/core/crypto/hashing_context.h new file mode 100644 index 0000000000..aa69636f2c --- /dev/null +++ b/core/crypto/hashing_context.h @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* hashing_context.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 HASHING_CONTEXT_H +#define HASHING_CONTEXT_H + +#include "core/reference.h" + +class HashingContext : public Reference { + GDCLASS(HashingContext, Reference); + +public: + enum HashType { + HASH_MD5, + HASH_SHA1, + HASH_SHA256 + }; + +private: + void *ctx; + HashType type; + +protected: + static void _bind_methods(); + void _create_ctx(HashType p_type); + void _delete_ctx(); + +public: + Error start(HashType p_type); + Error update(PoolByteArray p_chunk); + PoolByteArray finish(); + + HashingContext(); + ~HashingContext(); +}; + +VARIANT_ENUM_CAST(HashingContext::HashType); + +#endif // HASHING_CONTEXT_H diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 70bb816acd..9063e028be 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -201,7 +201,7 @@ Error ConfigFile::load(const String &p_path) { FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); if (!f) - return ERR_CANT_OPEN; + return err; return _internal_load(p_path, f); } diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 1452c61d1a..77decc107d 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -30,7 +30,7 @@ #include "file_access_encrypted.h" -#include "core/math/crypto_core.h" +#include "core/crypto/crypto_core.h" #include "core/os/copymem.h" #include "core/print_string.h" #include "core/variant.h" diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index ccce48ccd7..f2eaf57acc 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -30,10 +30,7 @@ #include "stream_peer_ssl.h" -#include "core/io/certs_compressed.gen.h" -#include "core/io/compression.h" -#include "core/os/file_access.h" -#include "core/project_settings.h" +#include "core/engine.h" StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL; @@ -44,22 +41,8 @@ StreamPeerSSL *StreamPeerSSL::create() { return NULL; } -StreamPeerSSL::LoadCertsFromMemory StreamPeerSSL::load_certs_func = NULL; bool StreamPeerSSL::available = false; -void StreamPeerSSL::load_certs_from_memory(const PoolByteArray &p_memory) { - if (load_certs_func) - load_certs_func(p_memory); -} - -void StreamPeerSSL::load_certs_from_file(String p_path) { - if (p_path != "") { - PoolByteArray certs = get_cert_file_as_array(p_path); - if (certs.size() > 0) - load_certs_func(certs); - } -} - bool StreamPeerSSL::is_available() { return available; } @@ -72,56 +55,11 @@ bool StreamPeerSSL::is_blocking_handshake_enabled() const { return blocking_handshake; } -PoolByteArray StreamPeerSSL::get_cert_file_as_array(String p_path) { - - PoolByteArray out; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (f) { - int flen = f->get_len(); - out.resize(flen + 1); - PoolByteArray::Write w = out.write(); - f->get_buffer(w.ptr(), flen); - w[flen] = 0; // Make sure it ends with string terminator - memdelete(f); -#ifdef DEBUG_ENABLED - print_verbose(vformat("Loaded certs from '%s'.", p_path)); -#endif - } - - return out; -} - -PoolByteArray StreamPeerSSL::get_project_cert_array() { - - PoolByteArray out; - String certs_path = GLOBAL_DEF("network/ssl/certificates", ""); - ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); - - if (certs_path != "") { - // Use certs defined in project settings. - return get_cert_file_as_array(certs_path); - } -#ifdef BUILTIN_CERTS_ENABLED - else { - // Use builtin certs only if user did not override it in project settings. - out.resize(_certs_uncompressed_size + 1); - PoolByteArray::Write w = out.write(); - Compression::decompress(w.ptr(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE); - w[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator -#ifdef DEBUG_ENABLED - print_verbose("Loaded builtin certs"); -#endif - } -#endif - - return out; -} - void StreamPeerSSL::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); - ClassDB::bind_method(D_METHOD("accept_stream", "base"), &StreamPeerSSL::accept_stream); - ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("accept_stream", "stream", "private_key", "certificate", "chain"), &StreamPeerSSL::accept_stream, DEFVAL(Ref<X509Certificate>())); + ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname", "valid_certificate"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String()), DEFVAL(Ref<X509Certificate>())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status); ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream); ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled); diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index 482576c4c6..dedc35b9ac 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -31,19 +31,16 @@ #ifndef STREAM_PEER_SSL_H #define STREAM_PEER_SSL_H +#include "core/crypto/crypto.h" #include "core/io/stream_peer.h" class StreamPeerSSL : public StreamPeer { GDCLASS(StreamPeerSSL, StreamPeer); -public: - typedef void (*LoadCertsFromMemory)(const PoolByteArray &p_certs); - protected: static StreamPeerSSL *(*_create)(); static void _bind_methods(); - static LoadCertsFromMemory load_certs_func; static bool available; bool blocking_handshake; @@ -61,18 +58,14 @@ public: bool is_blocking_handshake_enabled() const; virtual void poll() = 0; - virtual Error accept_stream(Ref<StreamPeer> p_base) = 0; - virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String()) = 0; + virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()) = 0; + virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>()) = 0; virtual Status get_status() const = 0; virtual void disconnect_from_stream() = 0; static StreamPeerSSL *create(); - static PoolByteArray get_cert_file_as_array(String p_path); - static PoolByteArray get_project_cert_array(); - static void load_certs_from_file(String p_path); - static void load_certs_from_memory(const PoolByteArray &p_memory); static bool is_available(); StreamPeerSSL(); diff --git a/core/math/SCsub b/core/math/SCsub index 0995298a4b..be438fcfbe 100644 --- a/core/math/SCsub +++ b/core/math/SCsub @@ -2,37 +2,6 @@ Import('env') -env_math = env.Clone() # Maybe make one specific for crypto? - -is_builtin = env["builtin_mbedtls"] -has_module = env["module_mbedtls_enabled"] - -if is_builtin or not has_module: - # Use our headers for builtin or if the module is not going to be compiled. - # We decided not to depend on system mbedtls just for these few files that can - # be easily extracted. - env_math.Prepend(CPPPATH=["#thirdparty/mbedtls/include"]) - -# MbedTLS core functions (for CryptoCore). -# If the mbedtls module is compiled we don't need to add the .c files with our -# custom config since they will be built by the module itself. -# Only if the module is not enabled, we must compile here the required sources -# to make a "light" build with only the necessary mbedtls files. -if not has_module: - env_thirdparty = env_math.Clone() - env_thirdparty.disable_warnings() - # Custom config file - env_thirdparty.Append(CPPDEFINES=[('MBEDTLS_CONFIG_FILE', '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')]) - thirdparty_mbedtls_dir = "#thirdparty/mbedtls/library/" - thirdparty_mbedtls_sources = [ - "aes.c", - "base64.c", - "md5.c", - "sha1.c", - "sha256.c", - "godot_core_mbedtls_platform.c" - ] - thirdparty_mbedtls_sources = [thirdparty_mbedtls_dir + file for file in thirdparty_mbedtls_sources] - env_thirdparty.add_source_files(env.core_sources, thirdparty_mbedtls_sources) +env_math = env.Clone() env_math.add_source_files(env.core_sources, "*.cpp") diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index b61119d8df..aea42a1edf 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -40,7 +40,17 @@ int AStar::get_available_point_id() const { return 1; } - return points.back()->key() + 1; + // calculate our new next available point id if bigger than before or next id already contained in set of points. + if (points.has(last_free_id)) { + int cur_new_id = last_free_id; + while (points.has(cur_new_id)) { + cur_new_id++; + } + int &non_const = const_cast<int &>(last_free_id); + non_const = cur_new_id; + } + + return last_free_id; } void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { @@ -48,7 +58,10 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { ERR_FAIL_COND(p_id < 0); ERR_FAIL_COND(p_weight_scale < 1); - if (!points.has(p_id)) { + Point *found_pt; + bool p_exists = points.lookup(p_id, found_pt); + + if (!p_exists) { Point *pt = memnew(Point); pt->id = p_id; pt->pos = p_pos; @@ -57,84 +70,98 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { pt->open_pass = 0; pt->closed_pass = 0; pt->enabled = true; - points[p_id] = pt; + points.set(p_id, pt); } else { - points[p_id]->pos = p_pos; - points[p_id]->weight_scale = p_weight_scale; + found_pt->pos = p_pos; + found_pt->weight_scale = p_weight_scale; } } Vector3 AStar::get_point_position(int p_id) const { - ERR_FAIL_COND_V(!points.has(p_id), Vector3()); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND_V(!p_exists, Vector3()); - return points[p_id]->pos; + return p->pos; } void AStar::set_point_position(int p_id, const Vector3 &p_pos) { - ERR_FAIL_COND(!points.has(p_id)); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND(!p_exists); - points[p_id]->pos = p_pos; + p->pos = p_pos; } real_t AStar::get_point_weight_scale(int p_id) const { - ERR_FAIL_COND_V(!points.has(p_id), 0); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND_V(!p_exists, 0); - return points[p_id]->weight_scale; + return p->weight_scale; } void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) { - ERR_FAIL_COND(!points.has(p_id)); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND(!p_exists); ERR_FAIL_COND(p_weight_scale < 1); - points[p_id]->weight_scale = p_weight_scale; + p->weight_scale = p_weight_scale; } void AStar::remove_point(int p_id) { - ERR_FAIL_COND(!points.has(p_id)); - - Point *p = points[p_id]; + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND(!p_exists); - for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { + for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { - Segment s(p_id, E->get()->id); + Segment s(p_id, (*it.key)); segments.erase(s); - E->get()->neighbours.erase(p); - E->get()->unlinked_neighbours.erase(p); + (*it.value)->neighbours.remove(p->id); + (*it.value)->unlinked_neighbours.remove(p->id); } - for (Set<Point *>::Element *E = p->unlinked_neighbours.front(); E; E = E->next()) { + for (OAHashMap<int, Point *>::Iterator it = p->unlinked_neighbours.iter(); it.valid; it = p->unlinked_neighbours.next_iter(it)) { - Segment s(p_id, E->get()->id); + Segment s(p_id, (*it.key)); segments.erase(s); - E->get()->neighbours.erase(p); - E->get()->unlinked_neighbours.erase(p); + (*it.value)->neighbours.remove(p->id); + (*it.value)->unlinked_neighbours.remove(p->id); } memdelete(p); - points.erase(p_id); + points.remove(p_id); + last_free_id = p_id; } void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { - ERR_FAIL_COND(!points.has(p_id)); - ERR_FAIL_COND(!points.has(p_with_id)); ERR_FAIL_COND(p_id == p_with_id); - Point *a = points[p_id]; - Point *b = points[p_with_id]; - a->neighbours.insert(b); + Point *a; + bool from_exists = points.lookup(p_id, a); + ERR_FAIL_COND(!from_exists); - if (bidirectional) - b->neighbours.insert(a); - else - b->unlinked_neighbours.insert(a); + Point *b; + bool to_exists = points.lookup(p_with_id, b); + ERR_FAIL_COND(!to_exists); + + a->neighbours.set(b->id, b); + + if (bidirectional) { + b->neighbours.set(a->id, a); + } else { + b->unlinked_neighbours.set(a->id, a); + } Segment s(p_id, p_with_id); if (s.from == p_id) { @@ -147,6 +174,7 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { segments.insert(s); } + void AStar::disconnect_points(int p_id, int p_with_id) { Segment s(p_id, p_with_id); @@ -154,12 +182,18 @@ void AStar::disconnect_points(int p_id, int p_with_id) { segments.erase(s); - Point *a = points[p_id]; - Point *b = points[p_with_id]; - a->neighbours.erase(b); - a->unlinked_neighbours.erase(b); - b->neighbours.erase(a); - b->unlinked_neighbours.erase(a); + Point *a; + bool a_exists = points.lookup(p_id, a); + CRASH_COND(!a_exists); + + Point *b; + bool b_exists = points.lookup(p_with_id, b); + CRASH_COND(!b_exists); + + a->neighbours.remove(b->id); + a->unlinked_neighbours.remove(b->id); + b->neighbours.remove(a->id); + b->unlinked_neighbours.remove(a->id); } bool AStar::has_point(int p_id) const { @@ -171,8 +205,8 @@ Array AStar::get_points() { Array point_list; - for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) { - point_list.push_back(E->key()); + for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { + point_list.push_back(*(it.key)); } return point_list; @@ -180,14 +214,14 @@ Array AStar::get_points() { PoolVector<int> AStar::get_point_connections(int p_id) { - ERR_FAIL_COND_V(!points.has(p_id), PoolVector<int>()); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND_V(!p_exists, PoolVector<int>()); PoolVector<int> point_list; - Point *p = points[p_id]; - - for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { - point_list.push_back(E->get()->id); + for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { + point_list.push_back((*it.key)); } return point_list; @@ -201,9 +235,9 @@ bool AStar::are_points_connected(int p_id, int p_with_id) const { void AStar::clear() { - for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) { - - memdelete(E->get()); + last_free_id = 0; + for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { + memdelete(*(it.value)); } segments.clear(); points.clear(); @@ -214,14 +248,14 @@ int AStar::get_closest_point(const Vector3 &p_point) const { int closest_id = -1; real_t closest_dist = 1e20; - for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) { + for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) { + + if (!(*it.value)->enabled) continue; // Disabled points should not be considered. - if (!E->get()->enabled) - continue; //Disabled points should not be considered - real_t d = p_point.distance_squared_to(E->get()->pos); + real_t d = p_point.distance_squared_to((*it.value)->pos); if (closest_id < 0 || d < closest_dist) { closest_dist = d; - closest_id = E->key(); + closest_id = *(it.key); } } @@ -230,8 +264,8 @@ int AStar::get_closest_point(const Vector3 &p_point) const { Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const { - real_t closest_dist = 1e20; bool found = false; + real_t closest_dist = 1e20; Vector3 closest_point; for (const Set<Segment>::Element *E = segments.front(); E; E = E->next()) { @@ -262,8 +296,7 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { pass++; - if (!end_point->enabled) - return false; + if (!end_point->enabled) return false; bool found_route = false; @@ -272,13 +305,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { begin_point->g_score = 0; begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); - open_list.push_back(begin_point); - while (true) { - - if (open_list.size() == 0) // No path found - break; + while (!open_list.empty()) { Point *p = open_list[0]; // The currently processed point @@ -291,24 +320,23 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { open_list.remove(open_list.size() - 1); p->closed_pass = pass; // Mark the point as closed - for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { + for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) { - Point *e = E->get(); // The neighbour point + Point *e = *(it.value); // The neighbour point - if (!e->enabled || e->closed_pass == pass) + if (!e->enabled || e->closed_pass == pass) { continue; + } real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale; bool new_point = false; - if (e->open_pass != pass) { // The point wasn't inside the open list - + if (e->open_pass != pass) { // The point wasn't inside the open list. e->open_pass = pass; open_list.push_back(e); new_point = true; - } else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous - + } else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous. continue; } @@ -316,10 +344,11 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { e->g_score = tentative_g_score; e->f_score = e->g_score + _estimate_cost(e->id, end_point->id); - if (new_point) // The position of the new points is already known + if (new_point) { // The position of the new points is already known. sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptrw()); - else + } else { sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw()); + } } } @@ -331,7 +360,15 @@ float AStar::_estimate_cost(int p_from_id, int p_to_id) { if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost)) return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id); - return points[p_from_id]->pos.distance_to(points[p_to_id]->pos); + Point *from_point; + bool from_exists = points.lookup(p_from_id, from_point); + CRASH_COND(!from_exists); + + Point *to_point; + bool to_exists = points.lookup(p_to_id, to_point); + CRASH_COND(!to_exists); + + return from_point->pos.distance_to(to_point->pos); } float AStar::_compute_cost(int p_from_id, int p_to_id) { @@ -339,16 +376,26 @@ float AStar::_compute_cost(int p_from_id, int p_to_id) { if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost)) return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id); - return points[p_from_id]->pos.distance_to(points[p_to_id]->pos); + Point *from_point; + bool from_exists = points.lookup(p_from_id, from_point); + CRASH_COND(!from_exists); + + Point *to_point; + bool to_exists = points.lookup(p_to_id, to_point); + CRASH_COND(!to_exists); + + return from_point->pos.distance_to(to_point->pos); } PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { - ERR_FAIL_COND_V(!points.has(p_from_id), PoolVector<Vector3>()); - ERR_FAIL_COND_V(!points.has(p_to_id), PoolVector<Vector3>()); + Point *a; + bool from_exists = points.lookup(p_from_id, a); + ERR_FAIL_COND_V(!from_exists, PoolVector<Vector3>()); - Point *a = points[p_from_id]; - Point *b = points[p_to_id]; + Point *b; + bool to_exists = points.lookup(p_to_id, b); + ERR_FAIL_COND_V(!to_exists, PoolVector<Vector3>()); if (a == b) { PoolVector<Vector3> ret; @@ -360,11 +407,8 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { Point *end_point = b; bool found_route = _solve(begin_point, end_point); + if (!found_route) return PoolVector<Vector3>(); - if (!found_route) - return PoolVector<Vector3>(); - - // Midpoints Point *p = end_point; int pc = 1; // Begin point while (p != begin_point) { @@ -393,11 +437,13 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) { - ERR_FAIL_COND_V(!points.has(p_from_id), PoolVector<int>()); - ERR_FAIL_COND_V(!points.has(p_to_id), PoolVector<int>()); + Point *a; + bool from_exists = points.lookup(p_from_id, a); + ERR_FAIL_COND_V(!from_exists, PoolVector<int>()); - Point *a = points[p_from_id]; - Point *b = points[p_to_id]; + Point *b; + bool to_exists = points.lookup(p_to_id, b); + ERR_FAIL_COND_V(!to_exists, PoolVector<int>()); if (a == b) { PoolVector<int> ret; @@ -409,11 +455,8 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) { Point *end_point = b; bool found_route = _solve(begin_point, end_point); + if (!found_route) return PoolVector<int>(); - if (!found_route) - return PoolVector<int>(); - - // Midpoints Point *p = end_point; int pc = 1; // Begin point while (p != begin_point) { @@ -442,16 +485,20 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) { void AStar::set_point_disabled(int p_id, bool p_disabled) { - ERR_FAIL_COND(!points.has(p_id)); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND(!p_exists); - points[p_id]->enabled = !p_disabled; + p->enabled = !p_disabled; } bool AStar::is_point_disabled(int p_id) const { - ERR_FAIL_COND_V(!points.has(p_id), false); + Point *p; + bool p_exists = points.lookup(p_id, p); + ERR_FAIL_COND_V(!p_exists, false); - return !points[p_id]->enabled; + return !p->enabled; } void AStar::_bind_methods() { @@ -487,13 +534,11 @@ void AStar::_bind_methods() { } AStar::AStar() { - + last_free_id = 0; pass = 1; } AStar::~AStar() { - - pass = 1; clear(); } diff --git a/core/math/a_star.h b/core/math/a_star.h index cbabcce974..53aaaa1f6c 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -31,6 +31,7 @@ #ifndef ASTAR_H #define ASTAR_H +#include "core/oa_hash_map.h" #include "core/reference.h" /** @@ -43,8 +44,6 @@ class AStar : public Reference { GDCLASS(AStar, Reference); - uint64_t pass; - struct Point { int id; @@ -52,10 +51,10 @@ class AStar : public Reference { real_t weight_scale; bool enabled; - Set<Point *> neighbours; - Set<Point *> unlinked_neighbours; + OAHashMap<int, Point *> neighbours; + OAHashMap<int, Point *> unlinked_neighbours; - // Used for pathfinding + // Used for pathfinding. Point *prev_point; real_t g_score; real_t f_score; @@ -63,16 +62,15 @@ class AStar : public Reference { uint64_t closed_pass; }; - Map<int, Point *> points; - struct SortPoints { - _FORCE_INLINE_ bool operator()(const Point *A, const Point *B) const { // Returns true when the Point A is worse than Point B - if (A->f_score > B->f_score) + _FORCE_INLINE_ bool operator()(const Point *A, const Point *B) const { // Returns true when the Point A is worse than Point B. + if (A->f_score > B->f_score) { return true; - else if (A->f_score < B->f_score) + } else if (A->f_score < B->f_score) { return false; - else - return A->g_score < B->g_score; // If the f_costs are the same then prioritize the points that are further away from the start + } else { + return A->g_score < B->g_score; // If the f_costs are the same then prioritize the points that are further away from the start. + } } }; @@ -100,6 +98,10 @@ class AStar : public Reference { } }; + int last_free_id; + uint64_t pass; + + OAHashMap<int, Point *> points; Set<Segment> segments; bool _solve(Point *begin_point, Point *end_point); diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h index e52d36a859..83621bec14 100644 --- a/core/oa_hash_map.h +++ b/core/oa_hash_map.h @@ -62,7 +62,7 @@ private: static const uint32_t EMPTY_HASH = 0; static const uint32_t DELETED_HASH_BIT = 1 << 31; - _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) { + _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const { uint32_t hash = Hasher::hash(p_key); if (hash == EMPTY_HASH) { @@ -74,7 +74,7 @@ private: return hash; } - _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) { + _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) const { p_hash = p_hash & ~DELETED_HASH_BIT; // we don't care if it was deleted or not uint32_t original_pos = p_hash % capacity; @@ -90,7 +90,7 @@ private: num_elements++; } - bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) { + bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const { uint32_t hash = _hash(p_key); uint32_t pos = hash % capacity; uint32_t distance = 0; @@ -151,6 +151,7 @@ private: distance++; } } + void _resize_and_rehash() { TKey *old_keys = keys; @@ -190,6 +191,26 @@ public: _FORCE_INLINE_ uint32_t get_capacity() const { return capacity; } _FORCE_INLINE_ uint32_t get_num_elements() const { return num_elements; } + bool empty() const { + return num_elements == 0; + } + + void clear() { + + for (uint32_t i = 0; i < capacity; i++) { + + if (hashes[i] & DELETED_HASH_BIT) { + continue; + } + + hashes[i] |= DELETED_HASH_BIT; + values[i].~TValue(); + keys[i].~TKey(); + } + + num_elements = 0; + } + void insert(const TKey &p_key, const TValue &p_value) { if ((float)num_elements / (float)capacity > 0.9) { @@ -219,7 +240,7 @@ public: * if r_data is not NULL then the value will be written to the object * it points to. */ - bool lookup(const TKey &p_key, TValue &r_data) { + bool lookup(const TKey &p_key, TValue &r_data) const { uint32_t pos = 0; bool exists = _lookup_pos(p_key, pos); @@ -232,7 +253,7 @@ public: return false; } - _FORCE_INLINE_ bool has(const TKey &p_key) { + _FORCE_INLINE_ bool has(const TKey &p_key) const { uint32_t _pos = 0; return _lookup_pos(p_key, _pos); } @@ -302,6 +323,9 @@ public: return it; } + OAHashMap(const OAHashMap &) = delete; // Delete the copy constructor so we don't get unexpected copies and dangling pointers. + OAHashMap &operator=(const OAHashMap &) = delete; // Same for assignment operator. + OAHashMap(uint32_t p_initial_capacity = 64) { capacity = p_initial_capacity; diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index ba94e87da6..9a8315a3bb 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -30,9 +30,9 @@ #include "file_access.h" +#include "core/crypto/crypto_core.h" #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" -#include "core/math/crypto_core.h" #include "core/os/os.h" #include "core/project_settings.h" diff --git a/core/os/input.cpp b/core/os/input.cpp index f04d4a1b3e..51cb41b184 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -80,6 +80,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string); ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); + ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::vibrate_handheld, DEFVAL(500)); ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity); ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer); ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer); diff --git a/core/os/input.h b/core/os/input.h index de04f239e6..a12ded176b 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -100,6 +100,7 @@ public: virtual uint64_t get_joy_vibration_timestamp(int p_device) = 0; virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0) = 0; virtual void stop_joy_vibration(int p_device) = 0; + virtual void vibrate_handheld(int p_duration_ms = 500) = 0; virtual Point2 get_mouse_position() const = 0; virtual Point2 get_last_mouse_speed() const = 0; diff --git a/core/os/os.cpp b/core/os/os.cpp index 0d7d150914..7531900480 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -186,6 +186,11 @@ int OS::get_process_id() const { return -1; }; +void OS::vibrate_handheld(int p_duration_ms) { + + WARN_PRINTS("vibrate_handheld() only works with Android and iOS"); +} + bool OS::is_stdout_verbose() const { return _verbose_stdout; diff --git a/core/os/os.h b/core/os/os.h index c0c346e9aa..e627773d88 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -265,6 +265,7 @@ public: virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL) = 0; virtual Error kill(const ProcessID &p_pid) = 0; virtual int get_process_id() const; + virtual void vibrate_handheld(int p_duration_ms = 500); virtual Error shell_open(String p_uri); virtual Error set_cwd(const String &p_cwd); diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index e442546124..efc77bde48 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -34,6 +34,8 @@ #include "core/class_db.h" #include "core/compressed_translation.h" #include "core/core_string_names.h" +#include "core/crypto/crypto.h" +#include "core/crypto/hashing_context.h" #include "core/engine.h" #include "core/func_ref.h" #include "core/input_map.h" @@ -70,6 +72,8 @@ static Ref<ResourceFormatLoaderBinary> resource_loader_binary; static Ref<ResourceFormatImporter> resource_format_importer; static Ref<ResourceFormatLoaderImage> resource_format_image; static Ref<TranslationLoaderPO> resource_format_po; +static Ref<ResourceFormatSaverCrypto> resource_format_saver_crypto; +static Ref<ResourceFormatLoaderCrypto> resource_format_loader_crypto; static _ResourceLoader *_resource_loader = NULL; static _ResourceSaver *_resource_saver = NULL; @@ -151,7 +155,19 @@ void register_core_types() { ClassDB::register_class<StreamPeerTCP>(); ClassDB::register_class<TCP_Server>(); ClassDB::register_class<PacketPeerUDP>(); + + // Crypto + ClassDB::register_class<HashingContext>(); + ClassDB::register_custom_instance_class<X509Certificate>(); + ClassDB::register_custom_instance_class<CryptoKey>(); + ClassDB::register_custom_instance_class<Crypto>(); ClassDB::register_custom_instance_class<StreamPeerSSL>(); + + resource_format_saver_crypto.instance(); + ResourceSaver::add_resource_format_saver(resource_format_saver_crypto); + resource_format_loader_crypto.instance(); + ResourceLoader::add_resource_format_loader(resource_format_loader_crypto); + ClassDB::register_virtual_class<IP>(); ClassDB::register_virtual_class<PacketPeer>(); ClassDB::register_class<PacketPeerStream>(); @@ -211,6 +227,9 @@ void register_core_settings() { ProjectSettings::get_singleton()->set_custom_property_info("network/limits/tcp/connect_timeout_seconds", PropertyInfo(Variant::INT, "network/limits/tcp/connect_timeout_seconds", PROPERTY_HINT_RANGE, "1,1800,1")); GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16)); ProjectSettings::get_singleton()->set_custom_property_info("network/limits/packet_peer_stream/max_buffer_po2", PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "0,64,1,or_greater")); + + GLOBAL_DEF("network/ssl/certificates", ""); + ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); } void register_core_singletons() { @@ -272,6 +291,11 @@ void unregister_core_types() { ResourceLoader::remove_resource_format_loader(resource_format_po); resource_format_po.unref(); + ResourceSaver::remove_resource_format_saver(resource_format_saver_crypto); + resource_format_saver_crypto.unref(); + ResourceLoader::remove_resource_format_loader(resource_format_loader_crypto); + resource_format_loader_crypto.unref(); + if (ip) memdelete(ip); diff --git a/core/ustring.cpp b/core/ustring.cpp index ed401c3763..4e9ab7be6b 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -31,7 +31,7 @@ #include "ustring.h" #include "core/color.h" -#include "core/math/crypto_core.h" +#include "core/crypto/crypto_core.h" #include "core/math/math_funcs.h" #include "core/os/memory.h" #include "core/print_string.h" diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 9ea2fed5ae..02c6cd73d8 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -32,8 +32,8 @@ #include "core/color_names.inc" #include "core/core_string_names.h" +#include "core/crypto/crypto_core.h" #include "core/io/compression.h" -#include "core/math/crypto_core.h" #include "core/object.h" #include "core/os/os.h" #include "core/script_language.h" @@ -606,6 +606,13 @@ struct _VariantCall { r_ret = s; } + static void _call_PoolByteArray_hex_encode(Variant &r_ret, Variant &p_self, const Variant **p_args) { + PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem); + PoolByteArray::Read r = ba->read(); + String s = String::hex_encode_buffer(&r[0], ba->size()); + r_ret = s; + } + VCALL_LOCALMEM0R(PoolByteArray, size); VCALL_LOCALMEM2(PoolByteArray, set); VCALL_LOCALMEM1R(PoolByteArray, get); @@ -1763,6 +1770,7 @@ void register_variant_methods() { ADDFUNC0R(POOL_BYTE_ARRAY, STRING, PoolByteArray, get_string_from_ascii, varray()); ADDFUNC0R(POOL_BYTE_ARRAY, STRING, PoolByteArray, get_string_from_utf8, varray()); ADDFUNC0R(POOL_BYTE_ARRAY, STRING, PoolByteArray, sha256_string, varray()); + ADDFUNC0R(POOL_BYTE_ARRAY, STRING, PoolByteArray, hex_encode, varray()); ADDFUNC1R(POOL_BYTE_ARRAY, POOL_BYTE_ARRAY, PoolByteArray, compress, INT, "compression_mode", varray(0)); ADDFUNC2R(POOL_BYTE_ARRAY, POOL_BYTE_ARRAY, PoolByteArray, decompress, INT, "buffer_size", INT, "compression_mode", varray(0)); diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml index e78f21b274..4f1e8cc309 100644 --- a/doc/classes/FileDialog.xml +++ b/doc/classes/FileDialog.xml @@ -134,6 +134,8 @@ </theme_item> <theme_item name="folder" type="Texture"> </theme_item> + <theme_item name="folder_icon_modulate" type="Color" default="Color( 1, 1, 1, 1 )"> + </theme_item> <theme_item name="parent_folder" type="Texture"> </theme_item> <theme_item name="reload" type="Texture"> diff --git a/doc/classes/GeometryInstance.xml b/doc/classes/GeometryInstance.xml index b108e1be7c..02f2c27043 100644 --- a/doc/classes/GeometryInstance.xml +++ b/doc/classes/GeometryInstance.xml @@ -46,22 +46,26 @@ </member> <member name="lod_max_distance" type="float" setter="set_lod_max_distance" getter="get_lod_max_distance" default="0.0"> The GeometryInstance's max LOD distance. + [b]Note:[/b] This property currently has no effect. </member> <member name="lod_max_hysteresis" type="float" setter="set_lod_max_hysteresis" getter="get_lod_max_hysteresis" default="0.0"> The GeometryInstance's max LOD margin. + [b]Note:[/b] This property currently has no effect. </member> <member name="lod_min_distance" type="float" setter="set_lod_min_distance" getter="get_lod_min_distance" default="0.0"> The GeometryInstance's min LOD distance. + [b]Note:[/b] This property currently has no effect. </member> <member name="lod_min_hysteresis" type="float" setter="set_lod_min_hysteresis" getter="get_lod_min_hysteresis" default="0.0"> The GeometryInstance's min LOD margin. + [b]Note:[/b] This property currently has no effect. </member> <member name="material_override" type="Material" setter="set_material_override" getter="get_material_override"> The material override for the whole geometry. - If there is a material in [code]material_override[/code], it will be used instead of any material set in any material slot of the mesh. + If a material is assigned to this property, it will be used instead of any material set in any material slot of the mesh. </member> <member name="use_in_baked_light" type="bool" setter="set_flag" getter="get_flag" default="false"> - If [code]true[/code], this GeometryInstance will be used when baking lights using a [GIProbe] and/or any other form of baked lighting. + If [code]true[/code], this GeometryInstance will be used when baking lights using a [GIProbe] or [BakedLightmap]. </member> </members> <constants> @@ -78,10 +82,10 @@ </constant> <constant name="SHADOW_CASTING_SETTING_SHADOWS_ONLY" value="3" enum="ShadowCastingSetting"> Will only show the shadows casted from this object. - In other words: The actual mesh will not be visible, only the shadows casted from the mesh. + In other words, the actual mesh will not be visible, only the shadows casted from the mesh will be. </constant> <constant name="FLAG_USE_BAKED_LIGHT" value="0" enum="Flags"> - Will allow the GeometryInstance to be used when baking lights using a [GIProbe] and/or any other form of baked lighting. + Will allow the GeometryInstance to be used when baking lights using a [GIProbe] or [BakedLightmap]. </constant> <constant name="FLAG_DRAW_NEXT_FRAME_IF_VISIBLE" value="1" enum="Flags"> Unused in this class, exposed for consistency with [enum VisualServer.InstanceFlags]. diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 33b9da6fdf..5fd5e8c3c0 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -372,6 +372,16 @@ Stops the vibration of the joypad. </description> </method> + <method name="vibrate_handheld"> + <return type="void"> + </return> + <argument index="0" name="duration_ms" type="int" default="500"> + </argument> + <description> + Vibrate Android and iOS devices. + [b]Note:[/b] It needs VIBRATE permission for Android at export settings. iOS does not support duration. + </description> + </method> <method name="warp_mouse_position"> <return type="void"> </return> diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 5820a59019..071734eb48 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -38,6 +38,8 @@ #include <sys/stat.h> #include <sys/types.h> +#include <errno.h> + #if defined(UNIX_ENABLED) #include <unistd.h> #endif @@ -112,8 +114,15 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { f = fopen(path.utf8().get_data(), mode_string); if (f == NULL) { - last_error = ERR_FILE_CANT_OPEN; - return ERR_FILE_CANT_OPEN; + switch (errno) { + case ENOENT: { + last_error = ERR_FILE_NOT_FOUND; + } break; + default: { + last_error = ERR_FILE_CANT_OPEN; + } break; + } + return last_error; } else { last_error = OK; flags = p_mode_flags; diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index c12a2d75a8..fb21c0c5a1 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -38,6 +38,7 @@ #include <shlwapi.h> #include <windows.h> +#include <errno.h> #include <sys/stat.h> #include <sys/types.h> #include <tchar.h> @@ -114,11 +115,18 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { path = path + ".tmp"; } - _wfopen_s(&f, path.c_str(), mode_string); + errno_t errcode = _wfopen_s(&f, path.c_str(), mode_string); if (f == NULL) { - last_error = ERR_FILE_CANT_OPEN; - return ERR_FILE_CANT_OPEN; + switch (errcode) { + case ENOENT: { + last_error = ERR_FILE_NOT_FOUND; + } break; + default: { + last_error = ERR_FILE_CANT_OPEN; + } break; + } + return last_error; } else { last_error = OK; flags = p_mode_flags; diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index bea1980b09..b2567249d8 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -181,14 +181,14 @@ EditorAbout::EditorAbout() { // Thirdparty License VBoxContainer *license_thirdparty = memnew(VBoxContainer); - license_thirdparty->set_name(TTR("Thirdparty License")); + license_thirdparty->set_name(TTR("Third-party Licenses")); license_thirdparty->set_h_size_flags(Control::SIZE_EXPAND_FILL); tc->add_child(license_thirdparty); Label *tpl_label = memnew(Label); tpl_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); tpl_label->set_autowrap(true); - tpl_label->set_text(TTR("Godot Engine relies on a number of thirdparty free and open source libraries, all compatible with the terms of its MIT license. The following is an exhaustive list of all such thirdparty components with their respective copyright statements and license terms.")); + tpl_label->set_text(TTR("Godot Engine relies on a number of third-party free and open source libraries, all compatible with the terms of its MIT license. The following is an exhaustive list of all such third-party components with their respective copyright statements and license terms.")); tpl_label->set_size(Size2(630, 1) * EDSCALE); license_thirdparty->add_child(tpl_label); diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp index b706f2cae6..98e670f952 100644 --- a/editor/editor_asset_installer.cpp +++ b/editor/editor_asset_installer.cpp @@ -90,7 +90,7 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) { unzFile pkg = unzOpen2(p_path.utf8().get_data(), &io); if (!pkg) { - error->set_text(TTR("Error opening package file, not in zip format.")); + error->set_text(TTR("Error opening package file, not in ZIP format.")); return; } @@ -217,7 +217,7 @@ void EditorAssetInstaller::ok_pressed() { unzFile pkg = unzOpen2(package_path.utf8().get_data(), &io); if (!pkg) { - error->set_text(TTR("Error opening package file, not in zip format.")); + error->set_text(TTR("Error opening package file, not in ZIP format.")); return; } diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index cb1754bb10..e2a750cf08 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -30,11 +30,11 @@ #include "editor_export.h" +#include "core/crypto/crypto_core.h" #include "core/io/config_file.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/io/zip_io.h" -#include "core/math/crypto_core.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "core/project_settings.h" diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 8c9aedc7b7..80aeeef868 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -257,7 +257,7 @@ void EditorFileDialog::_post_popup() { if (is_visible_in_tree()) { Ref<Texture> folder = get_icon("folder", "FileDialog"); - const Color folder_color = get_color("folder", "FileDialog"); + const Color folder_color = get_color("folder_icon_modulate", "FileDialog"); recent->clear(); bool res = access == ACCESS_RESOURCES; @@ -736,7 +736,7 @@ void EditorFileDialog::update_file_list() { dir_access->list_dir_begin(); Ref<Texture> folder = get_icon("folder", "FileDialog"); - const Color folder_color = get_color("folder", "FileDialog"); + const Color folder_color = get_color("folder_icon_modulate", "FileDialog"); List<String> files; List<String> dirs; @@ -1204,7 +1204,7 @@ void EditorFileDialog::_update_favorites() { String current = get_current_dir(); Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons"); - const Color folder_color = get_color("folder", "FileDialog"); + const Color folder_color = get_color("folder_icon_modulate", "FileDialog"); favorites->clear(); favorite->set_pressed(false); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 5ec071d71a..3e97dbd96c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2954,7 +2954,6 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, EditorPlugin *ep = memnew(EditorPlugin); ep->set_script(script.get_ref_ptr()); - ep->set_dir_cache(p_addon); plugin_addons[p_addon] = ep; add_editor_plugin(ep, p_config_changed); @@ -3828,9 +3827,13 @@ void EditorNode::show_accept(const String &p_text, const String &p_title) { void EditorNode::show_warning(const String &p_text, const String &p_title) { - warning->set_text(p_text); - warning->set_title(p_title); - warning->popup_centered_minsize(); + if (warning->is_inside_tree()) { + warning->set_text(p_text); + warning->set_title(p_title); + warning->popup_centered_minsize(); + } else { + WARN_PRINTS(p_title + " " + p_text); + } } void EditorNode::_copy_warning(const String &p_str) { diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 4b6afcbb86..e27f1ab9eb 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -324,13 +324,6 @@ void EditorPlugin::remove_autoload_singleton(const String &p_name) { EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_remove(p_name); } -Ref<ConfigFile> EditorPlugin::get_config() { - Ref<ConfigFile> cf = memnew(ConfigFile); - Error err = cf->load(_dir_cache.plus_file("plugin.cfg")); - ERR_FAIL_COND_V(err != OK, cf); - return cf; -} - ToolButton *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) { ERR_FAIL_NULL_V(p_control, NULL); return EditorNode::get_singleton()->add_bottom_panel_item(p_title, p_control); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 7b6f55e93d..8941dfa28c 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -117,7 +117,6 @@ class EditorPlugin : public Node { bool force_draw_over_forwarding_enabled; String last_main_screen_name; - String _dir_cache; protected: static void _bind_methods(); @@ -236,10 +235,6 @@ public: void add_autoload_singleton(const String &p_name, const String &p_path); void remove_autoload_singleton(const String &p_name); - void set_dir_cache(const String &p_dir) { _dir_cache = p_dir; } - String get_dir_cache() { return _dir_cache; } - Ref<ConfigFile> get_config(); - void enable_plugin(); void disable_plugin(); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index cb3bfa3a49..e342b784c9 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -608,6 +608,18 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("run/output/always_open_output_on_play", true); _initial_set("run/output/always_close_output_on_stop", false); + /* Network */ + + // Debug + _initial_set("network/debug/remote_host", "127.0.0.1"); // Hints provided in setup_network + + _initial_set("network/debug/remote_port", 6007); + hints["network/debug/remote_port"] = PropertyInfo(Variant::INT, "network/debug/remote_port", PROPERTY_HINT_RANGE, "1,65535,1"); + + // SSL + _initial_set("network/ssl/editor_ssl_certificates", _SYSTEM_CERTS_PATH); + hints["network/ssl/editor_ssl_certificates"] = PropertyInfo(Variant::STRING, "network/ssl/editor_ssl_certificates", PROPERTY_HINT_GLOBAL_FILE, "*.crt,*.pem"); + /* Extra config */ _initial_set("project_manager/sorting_order", 0); @@ -993,11 +1005,11 @@ void EditorSettings::setup_network() { List<IP_Address> local_ip; IP::get_singleton()->get_local_addresses(&local_ip); - String lip = "127.0.0.1"; String hint; String current = has_setting("network/debug/remote_host") ? get("network/debug/remote_host") : ""; - int port = has_setting("network/debug/remote_port") ? (int)get("network/debug/remote_port") : 6007; + String selected = "127.0.0.1"; + // Check that current remote_host is a valid interface address and populate hints. for (List<IP_Address>::Element *E = local_ip.front(); E; E = E->next()) { String ip = E->get(); @@ -1008,22 +1020,18 @@ void EditorSettings::setup_network() { // Same goes for IPv4 link-local (APIPA) addresses. if (ip.begins_with("169.254.")) // 169.254.0.0/16 continue; + // Select current IP (found) if (ip == current) - lip = current; //so it saves + selected = ip; if (hint != "") hint += ","; hint += ip; } - _initial_set("network/debug/remote_host", lip); + // Add hints with valid IP addresses to remote_host property. add_property_hint(PropertyInfo(Variant::STRING, "network/debug/remote_host", PROPERTY_HINT_ENUM, hint)); - - _initial_set("network/debug/remote_port", port); - add_property_hint(PropertyInfo(Variant::INT, "network/debug/remote_port", PROPERTY_HINT_RANGE, "1,65535,1")); - - // Editor SSL certificates override - _initial_set("network/ssl/editor_ssl_certificates", _SYSTEM_CERTS_PATH); - add_property_hint(PropertyInfo(Variant::STRING, "network/ssl/editor_ssl_certificates", PROPERTY_HINT_GLOBAL_FILE, "*.crt,*.pem")); + // Fix potentially invalid remote_host due to network change. + set("network/debug/remote_host", selected); } void EditorSettings::save() { @@ -1216,7 +1224,7 @@ void EditorSettings::set_project_metadata(const String &p_section, const String String path = get_project_settings_dir().plus_file("project_metadata.cfg"); Error err; err = cf->load(path); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND(err != OK && err != ERR_FILE_NOT_FOUND); cf->set_value(p_section, p_key, p_data); err = cf->save(path); ERR_FAIL_COND(err != OK); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index d962001b38..56eed96e31 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1068,7 +1068,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("folder", "FileDialog", theme->get_icon("Folder", "EditorIcons")); // Use a different color for folder icons to make them easier to distinguish from files. // On a light theme, the icon will be dark, so we need to lighten it before blending it with the accent color. - theme->set_color("folder", "FileDialog", (dark_theme ? Color(1, 1, 1) : Color(5, 5, 5)).linear_interpolate(accent_color, 0.7)); + theme->set_color("folder_icon_modulate", "FileDialog", (dark_theme ? Color(1, 1, 1) : Color(5, 5, 5)).linear_interpolate(accent_color, 0.7)); theme->set_color("files_disabled", "FileDialog", font_color_disabled); // color picker diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index 68e859966c..1447a143d4 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -590,7 +590,7 @@ Error ExportTemplateManager::install_android_template() { zlib_filefunc_def io = zipio_create_io_from_file(&src_f); unzFile pkg = unzOpen2(source_zip.utf8().get_data(), &io); - ERR_FAIL_COND_V_MSG(!pkg, ERR_CANT_OPEN, "Android sources not in zip format."); + ERR_FAIL_COND_V_MSG(!pkg, ERR_CANT_OPEN, "Android sources not in ZIP format."); int ret = unzGoToFirstFile(pkg); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 83904800e9..b7a74f0035 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -64,7 +64,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory subdirectory_item->set_text(0, dname); subdirectory_item->set_icon(0, get_icon("Folder", "EditorIcons")); - subdirectory_item->set_icon_color(0, get_color("folder", "FileDialog")); + subdirectory_item->set_icon_color(0, get_color("folder_icon_modulate", "FileDialog")); subdirectory_item->set_selectable(0, true); String lpath = p_dir->get_path(); subdirectory_item->set_metadata(0, lpath); @@ -187,7 +187,7 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo continue; Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons"); - const Color folder_color = get_color("folder", "FileDialog"); + const Color folder_color = get_color("folder_icon_modulate", "FileDialog"); String text; Ref<Texture> icon; @@ -646,7 +646,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { } Ref<Texture> folder_icon = (use_thumbnails) ? folder_thumbnail : get_icon("folder", "FileDialog"); - const Color folder_color = get_color("folder", "FileDialog"); + const Color folder_color = get_color("folder_icon_modulate", "FileDialog"); // Build the FileInfo list List<FileInfo> filelist; diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 84dd1aa866..9ea7c86e0c 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "editor_scene_importer_gltf.h" +#include "core/crypto/crypto_core.h" #include "core/io/json.h" -#include "core/math/crypto_core.h" #include "core/math/math_defs.h" #include "core/os/file_access.h" #include "core/os/os.h" diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index cc707dbf44..5942e8e5f2 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -3321,6 +3321,10 @@ void CanvasItemEditor::update_viewport() { viewport->update(); } +void CanvasItemEditor::set_current_tool(Tool p_tool) { + _button_tool_select(p_tool); +} + void CanvasItemEditor::_notification(int p_what) { if (p_what == NOTIFICATION_PHYSICS_PROCESS) { diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index e6eab57810..08c71c94f4 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -581,6 +581,7 @@ public: void update_viewport(); Tool get_current_tool() { return tool; } + void set_current_tool(Tool p_tool); void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } void edit(CanvasItem *p_canvas_item); diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index b4cce9745e..26fae96025 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -972,7 +972,7 @@ static inline Vector<Point2i> line(int x0, int x1, int y0, int y1) { bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { - if (!node || !node->get_tileset().is_valid() || !node->is_visible_in_tree()) + if (!node || !node->get_tileset().is_valid() || !node->is_visible_in_tree() || CanvasItemEditor::get_singleton()->get_current_tool() != CanvasItemEditor::TOOL_SELECT) return false; Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform(); @@ -1533,7 +1533,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { - if (!node) + if (!node || CanvasItemEditor::get_singleton()->get_current_tool() != CanvasItemEditor::TOOL_SELECT) return; Transform2D cell_xf = node->get_cell_transform(); @@ -2136,6 +2136,7 @@ void TileMapEditorPlugin::make_visible(bool p_visible) { tile_map_editor->show(); tile_map_editor->get_toolbar()->show(); tile_map_editor->get_toolbar_right()->show(); + CanvasItemEditor::get_singleton()->set_current_tool(CanvasItemEditor::TOOL_SELECT); //Change to TOOL_SELECT when TileMap node is selected, to prevent accidental movement. } else { tile_map_editor->hide(); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index a418773131..b88b2b38a0 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -195,7 +195,7 @@ private: unzFile pkg = unzOpen2(valid_path.utf8().get_data(), &io); if (!pkg) { - set_message(TTR("Error opening package file, not in zip format."), MESSAGE_ERROR); + set_message(TTR("Error opening package file, not in ZIP format."), MESSAGE_ERROR); memdelete(d); get_ok()->set_disabled(true); unzClose(pkg); @@ -519,7 +519,7 @@ private: unzFile pkg = unzOpen2(zip_path.utf8().get_data(), &io); if (!pkg) { - dialog_error->set_text(TTR("Error opening package file, not in zip format.")); + dialog_error->set_text(TTR("Error opening package file, not in ZIP format.")); dialog_error->popup_centered_minsize(); return; } @@ -2110,7 +2110,7 @@ void ProjectManager::_erase_project() { void ProjectManager::_erase_missing_projects() { - erase_missing_ask->set_text(TTR("Remove all missing projects from the list? (Folders contents will not be modified)")); + erase_missing_ask->set_text(TTR("Remove all missing projects from the list? The project folders' contents won't be modified.")); erase_missing_ask->popup_centered_minsize(); } diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index f7ba5e5e3c..4c5371769f 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -2037,8 +2037,11 @@ PortalSpatialGizmo::PortalSpatialGizmo(Portal *p_portal) { RayCastSpatialGizmoPlugin::RayCastSpatialGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); } bool RayCastSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { @@ -2064,7 +2067,8 @@ void RayCastSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { lines.push_back(Vector3()); lines.push_back(raycast->get_cast_to()); - Ref<SpatialMaterial> material = get_material("shape_material", p_gizmo); + const Ref<SpatialMaterial> material = + get_material(raycast->is_enabled() ? "shape_material" : "shape_material_disabled", p_gizmo); p_gizmo->add_lines(lines, material); p_gizmo->add_collision_segments(lines); @@ -3108,8 +3112,11 @@ void BakedIndirectLightGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { //// CollisionShapeSpatialGizmoPlugin::CollisionShapeSpatialGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); create_handle_material("handles"); } @@ -3432,7 +3439,8 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { if (s.is_null()) return; - Ref<Material> material = get_material("shape_material", p_gizmo); + const Ref<Material> material = + get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); Ref<Material> handles_material = get_material("handles"); if (Object::cast_to<SphereShape>(*s)) { @@ -3733,8 +3741,11 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { ///// CollisionPolygonSpatialGizmoPlugin::CollisionPolygonSpatialGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); } bool CollisionPolygonSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { @@ -3770,7 +3781,8 @@ void CollisionPolygonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { lines.push_back(Vector3(points[i].x, points[i].y, -depth)); } - Ref<Material> material = get_material("shape_material", p_gizmo); + const Ref<Material> material = + get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); p_gizmo->add_lines(lines, material); p_gizmo->add_collision_segments(lines); diff --git a/main/input_default.cpp b/main/input_default.cpp index 5ba98c20e2..60675f1115 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -472,6 +472,10 @@ void InputDefault::stop_joy_vibration(int p_device) { joy_vibration[p_device] = vibration; } +void InputDefault::vibrate_handheld(int p_duration_ms) { + OS::get_singleton()->vibrate_handheld(p_duration_ms); +} + void InputDefault::set_gravity(const Vector3 &p_gravity) { _THREAD_SAFE_METHOD_ diff --git a/main/input_default.h b/main/input_default.h index 80ee17656c..4fc4ad6506 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -226,6 +226,7 @@ public: virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0); virtual void stop_joy_vibration(int p_device); + virtual void vibrate_handheld(int p_duration_ms = 500); void set_main_loop(MainLoop *p_main_loop); void set_mouse_position(const Point2 &p_posf); diff --git a/main/main.cpp b/main/main.cpp index 027273c4f4..582df4e866 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -30,6 +30,7 @@ #include "main.h" +#include "core/crypto/crypto.h" #include "core/input_map.h" #include "core/io/file_access_network.h" #include "core/io/file_access_pack.h" @@ -37,8 +38,6 @@ #include "core/io/image_loader.h" #include "core/io/ip.h" #include "core/io/resource_loader.h" -#include "core/io/stream_peer_ssl.h" -#include "core/io/stream_peer_tcp.h" #include "core/message_queue.h" #include "core/os/dir_access.h" #include "core/os/os.h" @@ -1741,7 +1740,7 @@ bool Main::start() { if (!project_manager && !editor) { // game // Load SSL Certificates from Project Settings (or builtin). - StreamPeerSSL::load_certs_from_memory(StreamPeerSSL::get_project_cert_array()); + Crypto::load_default_certificates(GLOBAL_DEF("network/ssl/certificates", "")); if (game_path != "") { Node *scene = NULL; @@ -1793,17 +1792,15 @@ bool Main::start() { } if (project_manager || editor) { - // Load SSL Certificates from Editor Settings (or builtin). - String certs = EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String(); - if (certs != "") - StreamPeerSSL::load_certs_from_file(certs); - else - StreamPeerSSL::load_certs_from_memory(StreamPeerSSL::get_project_cert_array()); - // Hide console window if requested (Windows-only). bool hide_console = EditorSettings::get_singleton()->get_setting("interface/editor/hide_console_window"); OS::get_singleton()->set_console_visible(!hide_console); } + + if (project_manager || editor) { + // Load SSL Certificates from Editor Settings (or builtin) + Crypto::load_default_certificates(EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String()); + } #endif } diff --git a/misc/ide/jetbrains/build.gradle b/misc/ide/jetbrains/build.gradle index eb2fbc0e69..dea81b4ea3 100644 --- a/misc/ide/jetbrains/build.gradle +++ b/misc/ide/jetbrains/build.gradle @@ -4,7 +4,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.4.2' } } diff --git a/misc/ide/jetbrains/gradle/wrapper/gradle-wrapper.properties b/misc/ide/jetbrains/gradle/wrapper/gradle-wrapper.properties index b477add150..22e7a5c60b 100644 --- a/misc/ide/jetbrains/gradle/wrapper/gradle-wrapper.properties +++ b/misc/ide/jetbrains/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Mar 31 16:34:43 PDT 2019 +#Wed Aug 21 13:47:30 PDT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp new file mode 100644 index 0000000000..9c8eb40ca4 --- /dev/null +++ b/modules/mbedtls/crypto_mbedtls.cpp @@ -0,0 +1,285 @@ +/*************************************************************************/ +/* crypto_mbedtls.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "crypto_mbedtls.h" + +#include "core/os/file_access.h" + +#include "core/engine.h" +#include "core/io/certs_compressed.gen.h" +#include "core/io/compression.h" +#include "core/project_settings.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif +#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n" +#define PEM_END_CRT "-----END CERTIFICATE-----\n" + +#include "mbedtls/pem.h" +#include <mbedtls/debug.h> + +CryptoKey *CryptoKeyMbedTLS::create() { + return memnew(CryptoKeyMbedTLS); +} + +Error CryptoKeyMbedTLS::load(String p_path) { + ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Key is in use"); + + PoolByteArray out; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V(!f, ERR_INVALID_PARAMETER); + + int flen = f->get_len(); + out.resize(flen + 1); + { + PoolByteArray::Write w = out.write(); + f->get_buffer(w.ptr(), flen); + w[flen] = 0; //end f string + } + memdelete(f); + + int ret = mbedtls_pk_parse_key(&pkey, out.read().ptr(), out.size(), NULL, 0); + // We MUST zeroize the memory for safety! + mbedtls_platform_zeroize(out.write().ptr(), out.size()); + ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing some certificates: " + itos(ret)); + + return OK; +} + +Error CryptoKeyMbedTLS::save(String p_path) { + FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V(!f, ERR_INVALID_PARAMETER); + + unsigned char w[16000]; + memset(w, 0, sizeof(w)); + + int ret = mbedtls_pk_write_key_pem(&pkey, w, sizeof(w)); + if (ret != 0) { + memdelete(f); + memset(w, 0, sizeof(w)); // Zeroize anything we might have written. + ERR_FAIL_V_MSG(FAILED, "Error writing key: " + itos(ret)); + } + + size_t len = strlen((char *)w); + f->store_buffer(w, len); + memdelete(f); + memset(w, 0, sizeof(w)); // Zeroize temporary buffer. + return OK; +} + +X509Certificate *X509CertificateMbedTLS::create() { + return memnew(X509CertificateMbedTLS); +} + +Error X509CertificateMbedTLS::load(String p_path) { + ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Certificate is in use"); + + PoolByteArray out; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V(!f, ERR_INVALID_PARAMETER); + + int flen = f->get_len(); + out.resize(flen + 1); + { + PoolByteArray::Write w = out.write(); + f->get_buffer(w.ptr(), flen); + w[flen] = 0; //end f string + } + memdelete(f); + + int ret = mbedtls_x509_crt_parse(&cert, out.read().ptr(), out.size()); + ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing some certificates: " + itos(ret)); + + return OK; +} + +Error X509CertificateMbedTLS::load_from_memory(const uint8_t *p_buffer, int p_len) { + ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Certificate is in use"); + + int ret = mbedtls_x509_crt_parse(&cert, p_buffer, p_len); + ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing certificates: " + itos(ret)); + return OK; +} + +Error X509CertificateMbedTLS::save(String p_path) { + FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V(!f, ERR_INVALID_PARAMETER); + + mbedtls_x509_crt *crt = &cert; + while (crt) { + unsigned char w[4096]; + size_t wrote = 0; + int ret = mbedtls_pem_write_buffer(PEM_BEGIN_CRT, PEM_END_CRT, cert.raw.p, cert.raw.len, w, sizeof(w), &wrote); + if (ret != 0 || wrote == 0) { + memdelete(f); + ERR_FAIL_V_MSG(FAILED, "Error writing certificate: " + itos(ret)); + } + + f->store_buffer(w, wrote - 1); // don't write the string terminator + crt = crt->next; + } + memdelete(f); + return OK; +} + +Crypto *CryptoMbedTLS::create() { + return memnew(CryptoMbedTLS); +} + +void CryptoMbedTLS::initialize_crypto() { + +#ifdef DEBUG_ENABLED + mbedtls_debug_set_threshold(1); +#endif + + Crypto::_create = create; + Crypto::_load_default_certificates = load_default_certificates; + X509CertificateMbedTLS::make_default(); + CryptoKeyMbedTLS::make_default(); +} + +void CryptoMbedTLS::finalize_crypto() { + Crypto::_create = NULL; + Crypto::_load_default_certificates = NULL; + if (default_certs) { + memdelete(default_certs); + default_certs = NULL; + } + X509CertificateMbedTLS::finalize(); + CryptoKeyMbedTLS::finalize(); +} + +CryptoMbedTLS::CryptoMbedTLS() { + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); + if (ret != 0) { + ERR_PRINTS(" failed\n ! mbedtls_ctr_drbg_seed returned an error" + itos(ret)); + } +} + +CryptoMbedTLS::~CryptoMbedTLS() { + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); +} + +X509CertificateMbedTLS *CryptoMbedTLS::default_certs = NULL; + +X509CertificateMbedTLS *CryptoMbedTLS::get_default_certificates() { + return default_certs; +} + +void CryptoMbedTLS::load_default_certificates(String p_path) { + ERR_FAIL_COND(default_certs != NULL); + + default_certs = memnew(X509CertificateMbedTLS); + ERR_FAIL_COND(default_certs == NULL); + + String certs_path = GLOBAL_DEF("network/ssl/certificates", ""); + + if (p_path != "") { + // Use certs defined in project settings. + default_certs->load(p_path); + } +#ifdef BUILTIN_CERTS_ENABLED + else { + // Use builtin certs only if user did not override it in project settings. + PoolByteArray out; + out.resize(_certs_uncompressed_size + 1); + PoolByteArray::Write w = out.write(); + Compression::decompress(w.ptr(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE); + w[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator +#ifdef DEBUG_ENABLED + print_verbose("Loaded builtin certs"); +#endif + default_certs->load_from_memory(out.read().ptr(), out.size()); + } +#endif +} + +Ref<CryptoKey> CryptoMbedTLS::generate_rsa(int p_bytes) { + Ref<CryptoKeyMbedTLS> out; + out.instance(); + int ret = mbedtls_pk_setup(&(out->pkey), mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); + ERR_FAIL_COND_V(ret != 0, NULL); + ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(out->pkey), mbedtls_ctr_drbg_random, &ctr_drbg, p_bytes, 65537); + ERR_FAIL_COND_V(ret != 0, NULL); + return out; +} + +Ref<X509Certificate> CryptoMbedTLS::generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) { + Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS> >(p_key); + mbedtls_x509write_cert crt; + mbedtls_x509write_crt_init(&crt); + + mbedtls_x509write_crt_set_subject_key(&crt, &(key->pkey)); + mbedtls_x509write_crt_set_issuer_key(&crt, &(key->pkey)); + mbedtls_x509write_crt_set_subject_name(&crt, p_issuer_name.utf8().get_data()); + mbedtls_x509write_crt_set_issuer_name(&crt, p_issuer_name.utf8().get_data()); + mbedtls_x509write_crt_set_version(&crt, MBEDTLS_X509_CRT_VERSION_3); + mbedtls_x509write_crt_set_md_alg(&crt, MBEDTLS_MD_SHA256); + + mbedtls_mpi serial; + mbedtls_mpi_init(&serial); + uint8_t rand_serial[20]; + mbedtls_ctr_drbg_random(&ctr_drbg, rand_serial, 20); + ERR_FAIL_COND_V(mbedtls_mpi_read_binary(&serial, rand_serial, 20), NULL); + mbedtls_x509write_crt_set_serial(&crt, &serial); + + mbedtls_x509write_crt_set_validity(&crt, p_not_before.utf8().get_data(), p_not_after.utf8().get_data()); + mbedtls_x509write_crt_set_basic_constraints(&crt, 1, -1); + mbedtls_x509write_crt_set_basic_constraints(&crt, 1, 0); + + unsigned char buf[4096]; + memset(buf, 0, 4096); + Ref<X509CertificateMbedTLS> out; + out.instance(); + mbedtls_x509write_crt_pem(&crt, buf, 4096, mbedtls_ctr_drbg_random, &ctr_drbg); + + int err = mbedtls_x509_crt_parse(&(out->cert), buf, 4096); + if (err != 0) { + mbedtls_mpi_free(&serial); + mbedtls_x509write_crt_free(&crt); + ERR_PRINTS("Generated invalid certificate: " + itos(err)); + return NULL; + } + + mbedtls_mpi_free(&serial); + mbedtls_x509write_crt_free(&crt); + return out; +} + +PoolByteArray CryptoMbedTLS::generate_random_bytes(int p_bytes) { + PoolByteArray out; + out.resize(p_bytes); + mbedtls_ctr_drbg_random(&ctr_drbg, out.write().ptr(), p_bytes); + return out; +} diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h new file mode 100644 index 0000000000..06b3ecd234 --- /dev/null +++ b/modules/mbedtls/crypto_mbedtls.h @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* crypto_mbedtls.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 CRYPTO_MBEDTLS_H +#define CRYPTO_MBEDTLS_H + +#include "core/crypto/crypto.h" +#include "core/resource.h" + +#include <mbedtls/ctr_drbg.h> +#include <mbedtls/entropy.h> +#include <mbedtls/ssl.h> + +class CryptoMbedTLS; +class SSLContextMbedTLS; +class CryptoKeyMbedTLS : public CryptoKey { + +private: + mbedtls_pk_context pkey; + int locks; + +public: + static CryptoKey *create(); + static void make_default() { CryptoKey::_create = create; } + static void finalize() { CryptoKey::_create = NULL; } + + virtual Error load(String p_path); + virtual Error save(String p_path); + + CryptoKeyMbedTLS() { + mbedtls_pk_init(&pkey); + locks = 0; + } + ~CryptoKeyMbedTLS() { + mbedtls_pk_free(&pkey); + } + + _FORCE_INLINE_ void lock() { locks++; } + _FORCE_INLINE_ void unlock() { locks--; } + + friend class CryptoMbedTLS; + friend class SSLContextMbedTLS; +}; + +class X509CertificateMbedTLS : public X509Certificate { + +private: + mbedtls_x509_crt cert; + int locks; + +public: + static X509Certificate *create(); + static void make_default() { X509Certificate::_create = create; } + static void finalize() { X509Certificate::_create = NULL; } + + virtual Error load(String p_path); + virtual Error load_from_memory(const uint8_t *p_buffer, int p_len); + virtual Error save(String p_path); + + X509CertificateMbedTLS() { + mbedtls_x509_crt_init(&cert); + locks = 0; + } + ~X509CertificateMbedTLS() { + mbedtls_x509_crt_free(&cert); + } + + _FORCE_INLINE_ void lock() { locks++; } + _FORCE_INLINE_ void unlock() { locks--; } + + friend class CryptoMbedTLS; + friend class SSLContextMbedTLS; +}; + +class CryptoMbedTLS : public Crypto { + +private: + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + static X509CertificateMbedTLS *default_certs; + +public: + static Crypto *create(); + static void initialize_crypto(); + static void finalize_crypto(); + static X509CertificateMbedTLS *get_default_certificates(); + static void load_default_certificates(String p_path); + + virtual PoolByteArray generate_random_bytes(int p_bytes); + virtual Ref<CryptoKey> generate_rsa(int p_bytes); + virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after); + + CryptoMbedTLS(); + ~CryptoMbedTLS(); +}; + +#endif // CRYPTO_MBEDTLS_H diff --git a/modules/mbedtls/register_types.cpp b/modules/mbedtls/register_types.cpp index 121ed5eb02..f7dc6c785f 100755 --- a/modules/mbedtls/register_types.cpp +++ b/modules/mbedtls/register_types.cpp @@ -30,15 +30,17 @@ #include "register_types.h" -#include "stream_peer_mbed_tls.h" +#include "crypto_mbedtls.h" +#include "stream_peer_mbedtls.h" void register_mbedtls_types() { - ClassDB::register_class<StreamPeerMbedTLS>(); + CryptoMbedTLS::initialize_crypto(); StreamPeerMbedTLS::initialize_ssl(); } void unregister_mbedtls_types() { StreamPeerMbedTLS::finalize_ssl(); + CryptoMbedTLS::finalize_crypto(); } diff --git a/modules/mbedtls/ssl_context_mbedtls.cpp b/modules/mbedtls/ssl_context_mbedtls.cpp new file mode 100644 index 0000000000..014a201f9c --- /dev/null +++ b/modules/mbedtls/ssl_context_mbedtls.cpp @@ -0,0 +1,148 @@ +/*************************************************************************/ +/* ssl_context_mbed_tls.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "ssl_context_mbedtls.h" + +static void my_debug(void *ctx, int level, + const char *file, int line, + const char *str) { + + printf("%s:%04d: %s", file, line, str); + fflush(stdout); +} + +Error SSLContextMbedTLS::_setup(int p_endpoint, int p_transport, int p_authmode) { + ERR_FAIL_COND_V_MSG(inited, ERR_ALREADY_IN_USE, "This SSL context is already active"); + + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&conf); + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + inited = true; + + int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); + if (ret != 0) { + clear(); // Never leave unusable resources around. + ERR_FAIL_V_MSG(FAILED, "mbedtls_ctr_drbg_seed returned an error" + itos(ret)); + } + + ret = mbedtls_ssl_config_defaults(&conf, p_endpoint, p_transport, MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) { + clear(); + ERR_FAIL_V_MSG(FAILED, "mbedtls_ssl_config_defaults returned an error" + itos(ret)); + } + mbedtls_ssl_conf_authmode(&conf, p_authmode); + mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_dbg(&conf, my_debug, stdout); + return OK; +} + +Error SSLContextMbedTLS::init_server(int p_transport, int p_authmode, Ref<CryptoKeyMbedTLS> p_pkey, Ref<X509CertificateMbedTLS> p_cert) { + ERR_FAIL_COND_V(!p_pkey.is_valid(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(!p_cert.is_valid(), ERR_INVALID_PARAMETER); + + Error err = _setup(MBEDTLS_SSL_IS_SERVER, p_transport, p_authmode); + ERR_FAIL_COND_V(err != OK, err); + + // Locking key and certificate(s) + pkey = p_pkey; + certs = p_cert; + if (pkey.is_valid()) + pkey->lock(); + if (certs.is_valid()) + certs->lock(); + + // Adding key and certificate + int ret = mbedtls_ssl_conf_own_cert(&conf, &(certs->cert), &(pkey->pkey)); + if (ret != 0) { + clear(); + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Invalid cert/key combination " + itos(ret)); + } + // Adding CA chain if available. + if (certs->cert.next) { + mbedtls_ssl_conf_ca_chain(&conf, certs->cert.next, NULL); + } + mbedtls_ssl_setup(&ssl, &conf); + return OK; +} + +Error SSLContextMbedTLS::init_client(int p_transport, int p_authmode, Ref<X509CertificateMbedTLS> p_valid_cas) { + X509CertificateMbedTLS *cas = NULL; + + if (certs.is_valid()) { + // Locking CA certificates + certs = p_valid_cas; + certs->lock(); + cas = certs.ptr(); + } else { + // Fall back to default certificates (no need to lock those). + cas = CryptoMbedTLS::get_default_certificates(); + ERR_FAIL_COND_V(cas == NULL, ERR_UNCONFIGURED); + } + + Error err = _setup(MBEDTLS_SSL_IS_CLIENT, p_transport, p_authmode); + ERR_FAIL_COND_V(err != OK, err); + + // Set valid CAs + mbedtls_ssl_conf_ca_chain(&conf, &(cas->cert), NULL); + mbedtls_ssl_setup(&ssl, &conf); + return OK; +} + +void SSLContextMbedTLS::clear() { + if (!inited) + return; + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + + // Unlock and key and certificates + if (certs.is_valid()) + certs->unlock(); + certs = Ref<X509Certificate>(); + if (pkey.is_valid()) + pkey->unlock(); + pkey = Ref<CryptoKeyMbedTLS>(); + inited = false; +} + +mbedtls_ssl_context *SSLContextMbedTLS::get_context() { + ERR_FAIL_COND_V(!inited, NULL); + return &ssl; +} + +SSLContextMbedTLS::SSLContextMbedTLS() { + inited = false; +} + +SSLContextMbedTLS::~SSLContextMbedTLS() { + clear(); +} diff --git a/modules/mbedtls/ssl_context_mbedtls.h b/modules/mbedtls/ssl_context_mbedtls.h new file mode 100644 index 0000000000..b78ee37b03 --- /dev/null +++ b/modules/mbedtls/ssl_context_mbedtls.h @@ -0,0 +1,73 @@ +/*************************************************************************/ +/* ssl_context_mbed_tls.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 SSL_CONTEXT_MBED_TLS_H +#define SSL_CONTEXT_MBED_TLS_H + +#include "crypto_mbedtls.h" + +#include "core/os/file_access.h" +#include "core/pool_vector.h" +#include "core/reference.h" + +#include <mbedtls/config.h> +#include <mbedtls/ctr_drbg.h> +#include <mbedtls/debug.h> +#include <mbedtls/entropy.h> +#include <mbedtls/ssl.h> + +class SSLContextMbedTLS : public Reference { + +protected: + bool inited; + + static PoolByteArray _read_file(String p_path); + +public: + Ref<X509CertificateMbedTLS> certs; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + + Ref<CryptoKeyMbedTLS> pkey; + + Error _setup(int p_endpoint, int p_transport, int p_authmode); + Error init_server(int p_transport, int p_authmode, Ref<CryptoKeyMbedTLS> p_pkey, Ref<X509CertificateMbedTLS> p_cert); + Error init_client(int p_transport, int p_authmode, Ref<X509CertificateMbedTLS> p_valid_cas); + void clear(); + + mbedtls_ssl_context *get_context(); + + SSLContextMbedTLS(); + ~SSLContextMbedTLS(); +}; + +#endif // SSL_CONTEXT_MBED_TLS_H diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbedtls.cpp index 4bb7557150..e2eb19fc74 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.cpp +++ b/modules/mbedtls/stream_peer_mbedtls.cpp @@ -28,19 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "stream_peer_mbed_tls.h" +#include "stream_peer_mbedtls.h" #include "core/io/stream_peer_tcp.h" #include "core/os/file_access.h" -static void my_debug(void *ctx, int level, - const char *file, int line, - const char *str) { - - printf("%s:%04d: %s", file, line, str); - fflush(stdout); -} - void _print_error(int ret) { printf("mbedtls error: returned -0x%x\n\n", -ret); fflush(stdout); @@ -86,18 +78,14 @@ int StreamPeerMbedTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) { void StreamPeerMbedTLS::_cleanup() { - mbedtls_ssl_free(&ssl); - mbedtls_ssl_config_free(&conf); - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - + ssl_ctx->clear(); base = Ref<StreamPeer>(); status = STATUS_DISCONNECTED; } Error StreamPeerMbedTLS::_do_handshake() { int ret = 0; - while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { + while ((ret = mbedtls_ssl_handshake(ssl_ctx->get_context())) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { // An error occurred. ERR_PRINTS("TLS handshake error: " + itos(ret)); @@ -118,7 +106,7 @@ Error StreamPeerMbedTLS::_do_handshake() { return OK; } -Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname) { +Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname, Ref<X509Certificate> p_ca_certs) { ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER); @@ -126,31 +114,11 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida int ret = 0; int authmode = p_validate_certs ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE; - mbedtls_ssl_init(&ssl); - mbedtls_ssl_config_init(&conf); - mbedtls_ctr_drbg_init(&ctr_drbg); - mbedtls_entropy_init(&entropy); - - ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); - if (ret != 0) { - ERR_PRINTS(" failed\n ! mbedtls_ctr_drbg_seed returned an error" + itos(ret)); - _cleanup(); - return FAILED; - } + Error err = ssl_ctx->init_client(MBEDTLS_SSL_TRANSPORT_STREAM, authmode, p_ca_certs); + ERR_FAIL_COND_V(err != OK, err); - mbedtls_ssl_config_defaults(&conf, - MBEDTLS_SSL_IS_CLIENT, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT); - - mbedtls_ssl_conf_authmode(&conf, authmode); - mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL); - mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); - mbedtls_ssl_conf_dbg(&conf, my_debug, stdout); - mbedtls_ssl_setup(&ssl, &conf); - mbedtls_ssl_set_hostname(&ssl, p_for_hostname.utf8().get_data()); - - mbedtls_ssl_set_bio(&ssl, this, bio_send, bio_recv, NULL); + mbedtls_ssl_set_hostname(ssl_ctx->get_context(), p_for_hostname.utf8().get_data()); + mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, NULL); status = STATUS_HANDSHAKING; @@ -162,11 +130,26 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida return OK; } -Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base) { +Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain) { + + ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER); + + Error err = ssl_ctx->init_server(MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert); + ERR_FAIL_COND_V(err != OK, err); + + base = p_base; + + mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, NULL); + + status = STATUS_HANDSHAKING; + + if ((err = _do_handshake()) != OK) { + return FAILED; + } + status = STATUS_CONNECTED; return OK; } - Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) { ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED); @@ -197,7 +180,7 @@ Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, in if (p_bytes == 0) return OK; - int ret = mbedtls_ssl_write(&ssl, p_data, p_bytes); + int ret = mbedtls_ssl_write(ssl_ctx->get_context(), p_data, p_bytes); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { // Non blocking IO ret = 0; @@ -243,7 +226,7 @@ Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r r_received = 0; - int ret = mbedtls_ssl_read(&ssl, p_buffer, p_bytes); + int ret = mbedtls_ssl_read(ssl_ctx->get_context(), p_buffer, p_bytes); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { ret = 0; // non blocking io } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { @@ -273,7 +256,7 @@ void StreamPeerMbedTLS::poll() { // We could pass NULL as second parameter, but some behaviour sanitizers doesn't seem to like that. // Passing a 1 byte buffer to workaround it. uint8_t byte; - int ret = mbedtls_ssl_read(&ssl, &byte, 0); + int ret = mbedtls_ssl_read(ssl_ctx->get_context(), &byte, 0); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { // Nothing to read/write (non blocking IO) @@ -298,10 +281,11 @@ int StreamPeerMbedTLS::get_available_bytes() const { ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0); - return mbedtls_ssl_get_bytes_avail(&ssl); + return mbedtls_ssl_get_bytes_avail(&(ssl_ctx->ssl)); } StreamPeerMbedTLS::StreamPeerMbedTLS() { + ssl_ctx.instance(); status = STATUS_DISCONNECTED; } @@ -317,7 +301,7 @@ void StreamPeerMbedTLS::disconnect_from_stream() { Ref<StreamPeerTCP> tcp = base; if (tcp.is_valid() && tcp->get_status() == StreamPeerTCP::STATUS_CONNECTED) { // We are still connected on the socket, try to send close notify. - mbedtls_ssl_close_notify(&ssl); + mbedtls_ssl_close_notify(ssl_ctx->get_context()); } _cleanup(); @@ -333,28 +317,9 @@ StreamPeerSSL *StreamPeerMbedTLS::_create_func() { return memnew(StreamPeerMbedTLS); } -mbedtls_x509_crt StreamPeerMbedTLS::cacert; - -void StreamPeerMbedTLS::_load_certs(const PoolByteArray &p_array) { - int arr_len = p_array.size(); - PoolByteArray::Read r = p_array.read(); - int err = mbedtls_x509_crt_parse(&cacert, &r[0], arr_len); - if (err != 0) { - WARN_PRINTS("Error parsing some certificates: " + itos(err)); - } -} - void StreamPeerMbedTLS::initialize_ssl() { _create = _create_func; - load_certs_func = _load_certs; - - mbedtls_x509_crt_init(&cacert); - -#ifdef DEBUG_ENABLED - mbedtls_debug_set_threshold(1); -#endif - available = true; } @@ -362,6 +327,4 @@ void StreamPeerMbedTLS::finalize_ssl() { available = false; _create = NULL; - load_certs_func = NULL; - mbedtls_x509_crt_free(&cacert); } diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbedtls.h index ab87b779c1..060e76b4f3 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.h +++ b/modules/mbedtls/stream_peer_mbedtls.h @@ -32,15 +32,7 @@ #define STREAM_PEER_OPEN_SSL_H #include "core/io/stream_peer_ssl.h" - -#include <mbedtls/config.h> -#include <mbedtls/ctr_drbg.h> -#include <mbedtls/debug.h> -#include <mbedtls/entropy.h> -#include <mbedtls/ssl.h> - -#include <stdio.h> -#include <stdlib.h> +#include "ssl_context_mbedtls.h" class StreamPeerMbedTLS : public StreamPeerSSL { private: @@ -50,19 +42,13 @@ private: Ref<StreamPeer> base; static StreamPeerSSL *_create_func(); - static void _load_certs(const PoolByteArray &p_array); static int bio_recv(void *ctx, unsigned char *buf, size_t len); static int bio_send(void *ctx, const unsigned char *buf, size_t len); void _cleanup(); protected: - static mbedtls_x509_crt cacert; - - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ssl_context ssl; - mbedtls_ssl_config conf; + Ref<SSLContextMbedTLS> ssl_ctx; static void _bind_methods(); @@ -70,8 +56,8 @@ protected: public: virtual void poll(); - virtual Error accept_stream(Ref<StreamPeer> p_base); - virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String()); + virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()); + virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>()); virtual Status get_status() const; virtual void disconnect_from_stream(); diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp index 58b68d926b..6f97842064 100644 --- a/modules/webrtc/register_types.cpp +++ b/modules/webrtc/register_types.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "register_types.h" +#include "core/project_settings.h" #include "webrtc_data_channel.h" #include "webrtc_peer_connection.h" @@ -43,6 +44,12 @@ #include "webrtc_multiplayer.h" void register_webrtc_types() { +#define _SET_HINT(NAME, _VAL_, _MAX_) \ + GLOBAL_DEF(NAME, _VAL_); \ + ProjectSettings::get_singleton()->set_custom_property_info(NAME, PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater")); + + _SET_HINT(WRTC_IN_BUF, 64, 4096); + #ifdef JAVASCRIPT_ENABLED WebRTCPeerConnectionJS::make_default(); #elif defined(WEBRTC_GDNATIVE_ENABLED) diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp index 2bd30e68f5..7b3843410a 100644 --- a/modules/webrtc/webrtc_data_channel.cpp +++ b/modules/webrtc/webrtc_data_channel.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "webrtc_data_channel.h" +#include "core/project_settings.h" void WebRTCDataChannel::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &WebRTCDataChannel::poll); @@ -58,6 +59,7 @@ void WebRTCDataChannel::_bind_methods() { } WebRTCDataChannel::WebRTCDataChannel() { + _in_buffer_shift = nearest_shift((int)GLOBAL_GET(WRTC_IN_BUF) - 1) + 10; } WebRTCDataChannel::~WebRTCDataChannel() { diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h index 0b161da784..7e2c08d9d7 100644 --- a/modules/webrtc/webrtc_data_channel.h +++ b/modules/webrtc/webrtc_data_channel.h @@ -33,6 +33,8 @@ #include "core/io/packet_peer.h" +#define WRTC_IN_BUF "network/limits/webrtc/max_channel_in_buffer_kb" + class WebRTCDataChannel : public PacketPeer { GDCLASS(WebRTCDataChannel, PacketPeer); @@ -50,6 +52,8 @@ public: }; protected: + unsigned int _in_buffer_shift; + static void _bind_methods(); public: diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp index 996db35cba..2edd212a50 100644 --- a/modules/webrtc/webrtc_data_channel_js.cpp +++ b/modules/webrtc/webrtc_data_channel_js.cpp @@ -56,7 +56,7 @@ EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_message(void *obj, uint8_t *p_data, uint3 } void WebRTCDataChannelJS::_on_open() { - in_buffer.resize(16); + in_buffer.resize(_in_buffer_shift); } void WebRTCDataChannelJS::_on_close() { diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index b11bd2b70f..f94f3dfff7 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -35,7 +35,7 @@ #include "wsl_client.h" #include "wsl_server.h" -#include "core/math/crypto_core.h" +#include "core/crypto/crypto_core.h" #include "core/math/random_number_generator.h" #include "core/os/os.h" diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index 6e1841fa8b..f493b5f33f 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -56,6 +56,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Messenger; +import android.os.Vibrator; import android.provider.Settings.Secure; import android.support.v4.content.ContextCompat; import android.view.Display; @@ -98,6 +99,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC static final int MAX_SINGLETONS = 64; static final int REQUEST_RECORD_AUDIO_PERMISSION = 1; static final int REQUEST_CAMERA_PERMISSION = 2; + static final int REQUEST_VIBRATE_PERMISSION = 3; private IStub mDownloaderClientStub; private IDownloaderService mRemoteService; private TextView mStatusText; @@ -324,6 +326,15 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC }); } + public void vibrate(int p_duration_ms) { + if (requestPermission("VIBRATE")) { + Vibrator v = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE); + if (v != null) { + v.vibrate(p_duration_ms); + } + } + } + public void restart() { // HACK: // @@ -625,6 +636,10 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC GodotLib.ondestroy(this); super.onDestroy(); + + // TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each + // native Godot components that is started in Godot#onVideoInit. + forceQuit(); } @Override @@ -985,6 +1000,13 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC return false; } } + + if (p_name.equals("VIBRATE")) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION); + return false; + } + } return true; } diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 339b14974c..c7dc1d124c 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -62,6 +62,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) { _init_input_devices = p_env->GetMethodID(cls, "initInputDevices", "()V"); _get_surface = p_env->GetMethodID(cls, "getSurface", "()Landroid/view/Surface;"); _is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z"); + _vibrate = p_env->GetMethodID(cls, "vibrate", "(I)V"); } GodotJavaWrapper::~GodotJavaWrapper() { @@ -211,3 +212,10 @@ bool GodotJavaWrapper::is_activity_resumed() { return false; } } + +void GodotJavaWrapper::vibrate(int p_duration_ms) { + if (_vibrate) { + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms); + } +} diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 82c2a5d122..3e0e950180 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -57,6 +57,7 @@ private: jmethodID _init_input_devices = 0; jmethodID _get_surface = 0; jmethodID _is_activity_resumed = 0; + jmethodID _vibrate = 0; public: GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance); @@ -82,6 +83,7 @@ public: void init_input_devices(); jobject get_surface(); bool is_activity_resumed(); + void vibrate(int p_duration_ms); }; #endif /* !JAVA_GODOT_WRAPPER_H */ diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 701a351203..9b2df50f6c 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -700,6 +700,10 @@ String OS_Android::get_joy_guid(int p_device) const { return input->get_joy_guid_remapped(p_device); } +void OS_Android::vibrate_handheld(int p_duration_ms) { + godot_java->vibrate(p_duration_ms); +} + bool OS_Android::_check_internal_feature_support(const String &p_feature) { if (p_feature == "mobile") { //TODO support etc2 only if GLES3 driver is selected diff --git a/platform/android/os_android.h b/platform/android/os_android.h index e74d4cfd43..a17941f7c0 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -198,6 +198,7 @@ public: virtual bool is_joy_known(int p_device); virtual String get_joy_guid(int p_device) const; void joy_connection_changed(int p_device, bool p_connected, String p_name); + void vibrate_handheld(int p_duration_ms); virtual bool _check_internal_feature_support(const String &p_feature); OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion); diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm index 64405bfa5b..3f1230faa8 100644 --- a/platform/iphone/app_delegate.mm +++ b/platform/iphone/app_delegate.mm @@ -37,6 +37,7 @@ #include "os_iphone.h" #import "GameController/GameController.h" +#import <AudioToolbox/AudioServices.h> #define kFilteringFactor 0.1 #define kRenderingFrequency 60 @@ -61,6 +62,10 @@ void _set_keep_screen_on(bool p_enabled) { [[UIApplication sharedApplication] setIdleTimerDisabled:(BOOL)p_enabled]; }; +void _vibrate() { + AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); +}; + @implementation AppDelegate @synthesize window; diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 825342f911..353078c51c 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -470,6 +470,7 @@ extern void _show_keyboard(String p_existing); extern void _hide_keyboard(); extern Error _shell_open(String p_uri); extern void _set_keep_screen_on(bool p_enabled); +extern void _vibrate(); void OSIPhone::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect) { _show_keyboard(p_existing_text); @@ -585,6 +586,11 @@ void OSIPhone::native_video_stop() { _stop_video(); } +void OSIPhone::vibrate_handheld(int p_duration_ms) { + // iOS does not support duration for vibration + _vibrate(); +} + bool OSIPhone::_check_internal_feature_support(const String &p_feature) { return p_feature == "mobile"; diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index c16c29a858..804ba0b1af 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -195,6 +195,7 @@ public: virtual void native_video_unpause(); virtual void native_video_focus_out(); virtual void native_video_stop(); + virtual void vibrate_handheld(int p_duration_ms = 500); virtual bool _check_internal_feature_support(const String &p_feature); OSIPhone(int width, int height, String p_data_dir); diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index 662ee49935..ea110b11ca 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -30,9 +30,9 @@ #include "export.h" #include "core/bind/core_bind.h" +#include "core/crypto/crypto_core.h" #include "core/io/marshalls.h" #include "core/io/zip_io.h" -#include "core/math/crypto_core.h" #include "core/object.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" diff --git a/scene/3d/collision_polygon.cpp b/scene/3d/collision_polygon.cpp index db07059b32..37aa95fb43 100644 --- a/scene/3d/collision_polygon.cpp +++ b/scene/3d/collision_polygon.cpp @@ -151,6 +151,8 @@ float CollisionPolygon::get_depth() const { void CollisionPolygon::set_disabled(bool p_disabled) { disabled = p_disabled; + update_gizmo(); + if (parent) { parent->shape_owner_set_disabled(owner_id, p_disabled); } diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp index 10f92058e0..30eed8f1a7 100644 --- a/scene/3d/ray_cast.cpp +++ b/scene/3d/ray_cast.cpp @@ -102,6 +102,8 @@ Vector3 RayCast::get_collision_normal() const { void RayCast::set_enabled(bool p_enabled) { enabled = p_enabled; + update_gizmo(); + if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) set_physics_process_internal(p_enabled); if (!p_enabled) diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 7305d7459c..0c096f0d97 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -400,7 +400,7 @@ void FileDialog::update_file_list() { TreeItem *root = tree->create_item(); Ref<Texture> folder = get_icon("folder"); - const Color folder_color = get_color("folder"); + const Color folder_color = get_color("folder_icon_modulate"); List<String> files; List<String> dirs; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index a771794f25..f137b2618b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -6112,10 +6112,30 @@ void TextEdit::query_code_comple() { c--; } - if (ofs > 0 && (inquote || _is_completable(l[ofs - 1]) || completion_prefixes.has(String::chr(l[ofs - 1])))) - emit_signal("request_completion"); - else if (ofs > 1 && l[ofs - 1] == ' ' && completion_prefixes.has(String::chr(l[ofs - 2]))) // Make it work with a space too, it's good enough. - emit_signal("request_completion"); + bool ignored = completion_active && !completion_options.empty(); + if (ignored) { + ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_PLAIN_TEXT; + const ScriptCodeCompletionOption *previous_option = NULL; + for (int i = 0; i < completion_options.size(); i++) { + const ScriptCodeCompletionOption ¤t_option = completion_options[i]; + if (!previous_option) { + previous_option = ¤t_option; + kind = current_option.kind; + } + if (previous_option->kind != current_option.kind) { + ignored = false; + break; + } + } + ignored = ignored && (kind == ScriptCodeCompletionOption::KIND_FILE_PATH || kind == ScriptCodeCompletionOption::KIND_NODE_PATH || kind == ScriptCodeCompletionOption::KIND_SIGNAL); + } + + if (!ignored) { + if (ofs > 0 && (inquote || _is_completable(l[ofs - 1]) || completion_prefixes.has(String::chr(l[ofs - 1])))) + emit_signal("request_completion"); + else if (ofs > 1 && l[ofs - 1] == ' ' && completion_prefixes.has(String::chr(l[ofs - 2]))) // Make it work with a space too, it's good enough. + emit_signal("request_completion"); + } } void TextEdit::set_code_hint(const String &p_hint) { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 90787780d3..2664abdd2a 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -760,7 +760,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // FileDialog theme->set_icon("folder", "FileDialog", make_icon(icon_folder_png)); - theme->set_color("folder", "FileDialog", Color(1, 1, 1)); + theme->set_color("folder_icon_modulate", "FileDialog", Color(1, 1, 1)); theme->set_color("files_disabled", "FileDialog", Color(0, 0, 0, 0.7)); // colorPicker diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index f7d7c2d1bc..08ce47692c 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -612,6 +612,25 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port global_code += String() + "shader_type canvas_item;\n"; + String global_expressions; + for (int i = 0, index = 0; i < TYPE_MAX; i++) { + for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { + Ref<VisualShaderNodeGlobalExpression> global_expression = Object::cast_to<VisualShaderNodeGlobalExpression>(E->get().node.ptr()); + if (global_expression.is_valid()) { + + String expr = ""; + expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n"; + expr += global_expression->generate_global(get_mode(), Type(i), -1); + expr = expr.replace("\n", "\n\t"); + expr += "\n"; + global_expressions += expr; + } + } + } + + global_code += "\n"; + global_code += global_expressions; + //make it faster to go around through shader VMap<ConnectionKey, const List<Connection>::Element *> input_connections; VMap<ConnectionKey, const List<Connection>::Element *> output_connections; |