diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 399fca03e8..3bbe47af2a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,7 +30,6 @@ doc_classes/* @godotengine/documentation /modules/bullet/ @AndreaCatania /modules/csg/ @BastiaanOlij /modules/enet/ @godotengine/network -/modules/gdnative/ @karroffel /modules/gdnative/*arvr/ @BastiaanOlij /modules/gdscript/ @vnen @bojidar-bg /modules/mbedtls/ @godotengine/network @@ -45,6 +44,6 @@ doc_classes/* @godotengine/documentation /platform/uwp/ @vnen /server/physics*/ @reduz @AndreaCatania -/server/visual*/ @reduz @karroffel +/server/visual*/ @reduz /thirdparty/ @akien-mga diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 1ca0b3694c..0000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,21 +0,0 @@ - - -**Godot version:** - - - -**OS/device including version:** - - - -**Issue description:** - - - -**Steps to reproduce:** - - -**Minimal reproduction project:** - diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..91528465c0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,18 @@ +--- +name: Bug report +about: Report a bug in Godot +title: '' +labels: '' +assignees: '' + +--- + +**Godot version:** + +**OS/device including version:** + +**Issue description:** + +**Steps to reproduce:** + +**Minimal reproduction project:** diff --git a/.github/ISSUE_TEMPLATE/feature---enhancement-request.md b/.github/ISSUE_TEMPLATE/feature---enhancement-request.md new file mode 100644 index 0000000000..496e0197ca --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature---enhancement-request.md @@ -0,0 +1,19 @@ +--- +name: Feature / Enhancement Request +about: Adding new features or improving existing ones. +title: 'IMPORTANT: This repository no longer accepts feature / enhancement requests.' +labels: '' +assignees: '' + +--- + +**IMPORTANT, PLEASE READ** + +Feature / Enhancement requests are no longer accepted in the main Godot repository. +Please open an item by filling the relevant fields in the *Proposals* repository: + +https://github.com/godotengine/godot-proposals/issues/new/choose + +Do not submit to this repository or your issue will be closed. + +**IMPORTANT, PLEASE READ** 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 b52e40200f..a763fa5376 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,10 @@ language: cpp # OS config, depends on actual 'os' in build matrix dist: xenial -sudo: false + +stages: + - check + - build env: global: @@ -18,6 +21,7 @@ cache: matrix: include: - name: Static checks (clang-format) + stage: check env: STATIC_CHECKS=yes os: linux compiler: gcc @@ -29,6 +33,7 @@ matrix: - clang-format-8 - name: Linux editor (debug, GCC 9, with Mono) + stage: build env: PLATFORM=x11 TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-mono-gcc-9 MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" EXTRA_ARGS="module_mono_enabled=yes mono_glue=no warnings=extra werror=yes" os: linux compiler: gcc-9 @@ -52,6 +57,7 @@ matrix: branch_pattern: coverity_scan - name: Linux export template (release, Clang) + stage: build env: PLATFORM=x11 TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="warnings=extra werror=yes" os: linux compiler: clang @@ -61,22 +67,26 @@ matrix: - *linux_deps - name: Android export template (release_debug, Clang) + stage: build env: PLATFORM=android TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="warnings=extra werror=yes" os: linux compiler: clang - name: macOS editor (debug, Clang) + stage: build env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang os: osx compiler: clang - name: iOS export template (debug, Clang) + stage: build env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang os: osx compiler: clang - - name: Linux headless editor (release_debug, GCC 9) - 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" + - 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" TEST_PROJECT=yes os: linux compiler: gcc-9 addons: @@ -88,6 +98,7 @@ matrix: - *linux_deps - name: Linux export template (release_debug, GCC 5, without 3D support) + stage: build env: PLATFORM=x11 TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-gcc-5 EXTRA_ARGS="disable_3d=yes" os: linux compiler: gcc @@ -124,5 +135,10 @@ script: - if [ "$STATIC_CHECKS" = "yes" ]; then sh ./misc/travis/clang-format.sh; else - scons -j2 CC=$CC CXX=$CXX platform=$PLATFORM tools=$TOOLS target=$TARGET $OPTIONS $EXTRA_ARGS; + 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/AUTHORS.md b/AUTHORS.md index 43b4917382..523f253228 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -36,6 +36,7 @@ name is available. Andy Moss (MillionOstrich) Anish Bhobe (KidRigger) Anton Yabchinskiy (a12n) + Anutrix Aren Villanueva (kurikaesu) Ariel Manzur (punto-) Bastiaan Olij (BastiaanOlij) @@ -56,6 +57,7 @@ name is available. Dharkael (lupoDharkael) Dmitry Koteroff (Krakean) DualMatrix + Emmanuel Barroga (sparkart) Emmanuel Leblond (touilleMan) Eric Lasota (elasota) est31 @@ -68,6 +70,7 @@ name is available. Gerrit Großkopf (Grosskopf) Gilles Roudiere (groud) Guilherme Felipe de C. G. da Silva (guilhermefelipecgs) + Hanif A (hbina) Hein-Pieter van Braam (hpvb) Hiroshi Ogawa (hi-ogawa) homer666 @@ -104,6 +107,7 @@ name is available. Masoud BH (masoudbh3) Matthias Hölzl (hoelzl) Max Hilbrunner (mhilbrunner) + merumelu Michael Alexsander Silva Dias (YeldhamDev) mrezai Nathan Warden (NathanWarden) @@ -128,6 +132,7 @@ name is available. romulox-x Ruslan Mustakov (endragor) Saniko (sanikoyes) + santouits SaracenOne sersoong Simon Wenner (swenner) diff --git a/DONORS.md b/DONORS.md index 947c12923b..ce58721697 100644 --- a/DONORS.md +++ b/DONORS.md @@ -17,11 +17,12 @@ generous deed immortalized in the next stable release of Godot Engine. ## Gold sponsors Gamblify + Moonwards ## Mini sponsors + AD Ford Alan Beauchamp - Aleksandar Kordic Anandarup Mallik Andrew Dunai Brandon Lamb @@ -30,12 +31,16 @@ generous deed immortalized in the next stable release of Godot Engine. Christoph Woinke Denis Malyavin Edward Flick + Gamechuck GameDev.net GameDev.tv + Grady Hein-Pieter van Braam Jacob McKenney Javary Co. + Jeppe Zapp Justin Arnold + Justo Delgado Baudí Kyle Szklenski Leonard Meagher Matthieu Huvé @@ -46,7 +51,6 @@ generous deed immortalized in the next stable release of Godot Engine. Patrick Aarstad Slobodan Milnovic Stephan Lanfermann - Stephen Telford Steve VilliHaukka Xananax @@ -56,10 +60,11 @@ generous deed immortalized in the next stable release of Godot Engine. Andrei cheese65536 + Daniel Hartmann + Dave David Gehrig Ed Morley Florian Krick - Grady Jakub Grzesik K9Kraken Manuele Finocchiaro @@ -70,56 +75,62 @@ generous deed immortalized in the next stable release of Godot Engine. Zaven Muradyan Alexander Trey Saunders - Allen Schade Asher Glick Austen McRae + beVR Brian van der Stel + Cameron MacNair Carlo Cabanilla Daniel James David Giardi + David Snopek + Default Name Edward E Florian Breisch Gero + GiulianoB Javier Roman Jay Horton - Jon Smith + Jonathan Turner Jon Woodward - Justo Delgado Baudí + Jose Fernando Alexandre Karl Werf Kommentgames - Krzysztof Dluzniewski Luke - Moonwards + Maciej Pendolski + Matthew Hillier Mored1984 - paul gruenbacher Paul LaMotte Péter Magyar Rob Messick - Ross Esmond Ryan Badour Scott Wadden Sergey Shawn Yu Svenne Krap + thechris Tom Langwaldt + tukon William Wold - Wyatt Goodin Alex Khayrullin + Branwyn Tylwyth Chris Goddard Chris Serino Christian Padilla Conrad Curry Craig Smith + Darrian Little Dean Harmon Ian Richard Kunert Ivan Trombley Joan Fons + Joshua Flores Krzysztof Jankowski Lord Bloodhound Lucas Ferreira Franca - Michele Zilli Nathan Lundquist + Nicklas Breum Pascal Grüter Petr Malac Rami @@ -135,9 +146,10 @@ generous deed immortalized in the next stable release of Godot Engine. Xavier PATRICELLI Adam Neumann - Alessandra Pereyra Alexander J Maynard Alexey Dyadchenko + André Frélicot + andres eduardo lopez Andrew Bowen Asdf Ben Botwin @@ -147,50 +159,48 @@ generous deed immortalized in the next stable release of Godot Engine. Christoph Schröder Cody Parker D - Daniel Daniel Eichler David White - Deadly Lampshade Eric + Eric Churches Eric Monson Eugenio Hugo Salgüero Jáñez flesk - Francisco Javier Moreno Carracedo gavlig GGGames.org - Giles Montgomery Guilherme Felipe de C. G. da Silva Heath Hayes Hysteria Idzard Kwadijk Jared White - Jesse Nave + Joe Flood Jose Malheiro - Joshua Flores Joshua Lesperance Juan T Chen Juraj Móza Kasper Jeppesen + kinfox Klaus The. Klavdij Voncina - Leandro Voltolino Maarten Elings + Marcelo Dornbusch Lopes Markus Fehr Markus Wiesner Martin Eigel Marvin Matt Eunson - Matthew Hillier Max Bulai Max R.R. Collada M H Nick Nikitin Oliver Dick - Paolo Munoz + Patrick Ting Paul Hocker Paul Von Zimmerman Pete Goodwin + pl Ranoller + Romildo Franco Samuel Judd Scott Pilet spilldata @@ -198,60 +208,59 @@ generous deed immortalized in the next stable release of Godot Engine. Thomas Krampl Tobias Bocanegra Urho - WytRabbit - Xavier Fumado Beltran + Zie Weaver ## Silver donors 1D_Inc + Abby Jones Abraham Haskins Adam Brunnmeier Adam Carr Adam Nakonieczny Adam Smeltzer Adisibio + Adrian Demetrescu + Aggelos Arnaoutis Agustinus Arya Aidan O'Flannagain + Albin Jonasson Svärdsby Alder Stefano Alessandro Senese - Alexander Gillberg Alexander Koppe Alex Davies-Moore - Alice Robinson + Allen Schade Andreas Evers Andreas Krampitz + Andreas Lundmark Andreas Schüle - Andrew Peart + André Simões + Andrés Rodríguez + Andrzej Skalski Anthony Bongiovanni Anthony Staunton + Anton Kurkin Antony K. Jones + AP Condomines Arda Erol - Artem Bashev Arthur S. Muszynski - Artistofdeath Aubrey Falconer Avencherus B A Balázs Batári - Bastian Böhm Beliar Benedikt Ben Phelan Ben Vercammen - Ben Woodley - Berbank Bernd Jänichen Black Block Blair Allen Bobby CC Wong - Boyquotes - Branwyn Tylwyth Bryan Stevenson Caleb Dumitry Carwyn Edwards Chris Brown Chris Chapin - Chris Gonzales Christian Baune Christian Winter Christoffer Sundbom @@ -260,18 +269,22 @@ generous deed immortalized in the next stable release of Godot Engine. Cobaltum Collin Shooltz Dag Sundin Söderström + Dan H. Bentsen Daniel Johnson + DanielMaximiano + Daniel Pontillo Daniel Reed - Danny Welch - Dave Watts + Daniel Tebbutt David Bullock David Cravens David May - Dimitri Stanojevic + David Rapisarda + David Woodard Dominic Cooney Dominik Wetzel - DrevanTonder + Donovan Hutcheon Duobix + Eduardo Teixeira Edward Herbert Egon Elbre Ellen Marie Dash @@ -280,18 +293,21 @@ generous deed immortalized in the next stable release of Godot Engine. Eric Ellingson Eric Martini Eric Williams + EugeneTel Evan Rose Felix Kollmann fengjiongmax Flaredown FuDiggity G3Dev sàrl + Gadzhi Kharkharov + gamedev by Celio Gary Hulst - Gerrit Großkopf + George Marques gmmath - Grant Clarke Greg Olson Greg P + Greyson Richey Guldoman Hal A Heribert Hirth @@ -299,7 +315,6 @@ generous deed immortalized in the next stable release of Godot Engine. Hunter Jones Hylpher ialex32x - Igor Buzatovic Iiari IndustrialRobot Isaac Morton @@ -326,28 +341,33 @@ generous deed immortalized in the next stable release of Godot Engine. Jon Bonazza Jon Sully Jose Aleman + Jose Andrés Mejias Rojas Joseph Catrambone Josh 'Cheeseness' Bush Juanfran Juan Negrier + Juan Velandia Judd Jueast Julian Murgia - Kasier Bald0 + Justin Spedding + Kaiser Bald0 + Kamuna + Kauzig KC Chan + Keedong Park kickmaniac Kiyohiro Kawamura (kyorohiro) + Kjetil Haugland Klagsam Klassix KR McGinley KsyTek Games Kuan Cheang kycho - Leviathan Hunter Levi Lindsey Linus Lind Lundgren Lionel Gaillard - Luis Moraes LunaticInAHat Lurkars Macil @@ -355,61 +375,75 @@ generous deed immortalized in the next stable release of Godot Engine. Malcolm Malik Ahmed Malik Nejer - Marc Urlus Marcus Richter + Markus Lohaus Markus Michael Egger Martin Holas + Martin Liška + Matt Edwards Matthew Little Maxwell medecau mhilbrunner Michael Dürwald Michael Gringauz + Michael Haney Michael Labbe Mikael Olsson Mikayla Hutchinson + Mike Birkhead Mike Cunningham Mitchell J. Wagner - mlevin cantu MoM - Moritz Laass MuffinManKen + Nathan Fish Natrim nee Neil Blakey-Milner Nerdforge Nicholas + Nicholas Bettencourt + Nick Macholl Niclas Eriksen Nicolás Montaña Nicolas SAN AGUSTIN + Nima Farid Nithin Jino NZ + Olivier Omar Delarosa + omzee Oscar Norlander Pafka Pan Ip + Pat LaBine Patrick Forringer Patrick Nafarrete Paul Gieske Paul Mason Paweł Kowal - Philip O. Staiger + Philip Cohoe Pierre-Igor Berthet - Pietro Vertechi Pitsanu Tongprasin Point08 Poryg Rafa Laguna Rafal Wyszomirski Raphael Leroux + Remi Rampin Rémi Verschelde + Rezgi Ricardo Alcantara Robert Farr (Larington) Robert Hernandez + Robert Larnach Rodrigo Loli Roger Smith Roland Rząsa Roman Tinkov + Ronan Jouchet + Ryan + Ryan Brooks Ryan Groom Ryan Hentz Saad Khoudmi @@ -418,46 +452,51 @@ generous deed immortalized in the next stable release of Godot Engine. Sasori Olkof Scott D. Yelich Sebastian Michailidis + sgnsajgon + Shane Shane Sicienski Shane Spoor Simon Ledam Simon Wenner SK Sootstone - Stonepyre - Thibault Barbaroux + Taylor Fahlman thomas Thomas Bell Thomas Kelly Thomas Kurz tiansheng li - Tim Tim Drumheller Tim Gudex - Timo Schmidt Timothy B. MacDonald Tobbun + Tom Fulp + Tom Glenn Tom Larrow Torsten Crass Travis O'Brien Trent Skinner + Triptych + Troy Bonneau Tryggve Sollid Turgut Temucin Tyler Stafos UltyX Vaiktorg - Valeria Viana Gusmao - Veodok Victor Vigilant Watch + Vincent Cloutier waka nya + Walter Byers Wayne Haak werner mendizabal Wiley Thompson Will William Hogben Wout Standaert - Yeung Si Xiang + Wyatt Goodin + Yegor + 蕭惟允 ## Bronze donors diff --git a/SConstruct b/SConstruct index 92dc4d9da2..128c5b0e92 100644 --- a/SConstruct +++ b/SConstruct @@ -311,6 +311,10 @@ if selected_platform in platform_list: # must happen after the flags, so when flags are used by configure, stuff happens (ie, ssl on x11) detect.configure(env) + # Enable C++11 support + if not env.msvc: + env.Append(CXXFLAGS=['-std=c++11']) + # Configure compiler warnings if env.msvc: # Truncations, narrowing conversions, signed/unsigned comparisons... @@ -480,6 +484,13 @@ if selected_platform in platform_list: if env['minizip']: env.Append(CPPDEFINES=['MINIZIP_ENABLED']) + editor_module_list = ['regex'] + for x in editor_module_list: + if not env['module_' + x + '_enabled']: + if env['tools']: + print("Build option 'module_" + x + "_enabled=no' cannot be used with 'tools=yes' (editor), only with 'tools=no' (export template).") + sys.exit(255) + if not env['verbose']: methods.no_verbose(sys, env) 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/array.cpp b/core/array.cpp index a334af2c04..108d9f7386 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -133,18 +133,12 @@ void Array::erase(const Variant &p_value) { } Variant Array::front() const { - if (_p->array.size() == 0) { - ERR_EXPLAIN("Can't take value from empty array"); - ERR_FAIL_V(Variant()); - } + ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array."); return operator[](0); } Variant Array::back() const { - if (_p->array.size() == 0) { - ERR_EXPLAIN("Can't take value from empty array"); - ERR_FAIL_V(Variant()); - } + ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array."); return operator[](_p->array.size() - 1); } diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index b41b84ab1e..5161f8bab2 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" @@ -73,10 +73,7 @@ RES _ResourceLoader::load(const String &p_path, const String &p_type_hint, bool Error err = OK; RES ret = ResourceLoader::load(p_path, p_type_hint, p_no_cache, &err); - if (err != OK) { - ERR_EXPLAIN("Error loading resource: '" + p_path + "'"); - ERR_FAIL_V(ret); - } + ERR_FAIL_COND_V_MSG(err != OK, ret, "Error loading resource: '" + p_path + "'."); return ret; } @@ -148,10 +145,7 @@ _ResourceLoader::_ResourceLoader() { } Error _ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFlags p_flags) { - if (p_resource.is_null()) { - ERR_EXPLAIN("Can't save empty resource to path: " + String(p_path)) - ERR_FAIL_V(ERR_INVALID_PARAMETER); - } + ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path: " + String(p_path) + "."); return ResourceSaver::save(p_path, p_resource, p_flags); } @@ -191,10 +185,31 @@ _ResourceSaver::_ResourceSaver() { /////////////////OS +void _OS::global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta) { + + OS::get_singleton()->global_menu_add_item(p_menu, p_label, p_signal, p_meta); +} + +void _OS::global_menu_add_separator(const String &p_menu) { + + OS::get_singleton()->global_menu_add_separator(p_menu); +} + +void _OS::global_menu_remove_item(const String &p_menu, int p_idx) { + + OS::get_singleton()->global_menu_remove_item(p_menu, p_idx); +} + +void _OS::global_menu_clear(const String &p_menu) { + + OS::get_singleton()->global_menu_clear(p_menu); +} + Point2 _OS::get_mouse_position() const { return OS::get_singleton()->get_mouse_position(); } + void _OS::set_window_title(const String &p_title) { OS::get_singleton()->set_window_title(p_title); @@ -208,6 +223,7 @@ int _OS::get_mouse_button_state() const { String _OS::get_unique_id() const { return OS::get_singleton()->get_unique_id(); } + bool _OS::has_touchscreen_ui_hint() const { return OS::get_singleton()->has_touchscreen_ui_hint(); @@ -217,6 +233,7 @@ void _OS::set_clipboard(const String &p_text) { OS::get_singleton()->set_clipboard(p_text); } + String _OS::get_clipboard() const { return OS::get_singleton()->get_clipboard(); @@ -263,12 +280,14 @@ void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeab vm.resizable = p_resizeable; OS::get_singleton()->set_video_mode(vm, p_screen); } + Size2 _OS::get_video_mode(int p_screen) const { OS::VideoMode vm; vm = OS::get_singleton()->get_video_mode(p_screen); return Size2(vm.width, vm.height); } + bool _OS::is_video_mode_fullscreen(int p_screen) const { OS::VideoMode vm; @@ -727,22 +746,16 @@ int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; - ERR_EXPLAIN("Invalid second value of: " + itos(second)); - ERR_FAIL_COND_V(second > 59, 0); + ERR_FAIL_COND_V_MSG(second > 59, 0, "Invalid second value of: " + itos(second) + "."); - ERR_EXPLAIN("Invalid minute value of: " + itos(minute)); - ERR_FAIL_COND_V(minute > 59, 0); + ERR_FAIL_COND_V_MSG(minute > 59, 0, "Invalid minute value of: " + itos(minute) + "."); - ERR_EXPLAIN("Invalid hour value of: " + itos(hour)); - ERR_FAIL_COND_V(hour > 23, 0); + ERR_FAIL_COND_V_MSG(hour > 23, 0, "Invalid hour value of: " + itos(hour) + "."); - ERR_EXPLAIN("Invalid month value of: " + itos(month)); - ERR_FAIL_COND_V(month > 12 || month == 0, 0); + ERR_FAIL_COND_V_MSG(month > 12 || month == 0, 0, "Invalid month value of: " + itos(month) + "."); // Do this check after month is tested as valid - ERR_EXPLAIN("Invalid day value of: " + itos(day) + " which is larger than " + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1]) + " or 0"); - ERR_FAIL_COND_V(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1] || day == 0, 0); - + ERR_FAIL_COND_V_MSG(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1] || day == 0, 0, "Invalid day value of: " + itos(day) + " which is larger than " + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1]) + " or 0."); // Calculate all the seconds from months past in this year uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month - 1] * SECONDS_PER_DAY; @@ -1137,6 +1150,11 @@ void _OS::_bind_methods() { //ClassDB::bind_method(D_METHOD("is_video_mode_resizable","screen"),&_OS::is_video_mode_resizable,DEFVAL(0)); //ClassDB::bind_method(D_METHOD("get_fullscreen_mode_list","screen"),&_OS::get_fullscreen_mode_list,DEFVAL(0)); + ClassDB::bind_method(D_METHOD("global_menu_add_item", "menu", "label", "id", "meta"), &_OS::global_menu_add_item); + ClassDB::bind_method(D_METHOD("global_menu_add_separator", "menu"), &_OS::global_menu_add_separator); + ClassDB::bind_method(D_METHOD("global_menu_remove_item", "menu", "idx"), &_OS::global_menu_remove_item); + ClassDB::bind_method(D_METHOD("global_menu_clear", "menu"), &_OS::global_menu_clear); + ClassDB::bind_method(D_METHOD("get_video_driver_count"), &_OS::get_video_driver_count); ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name); ClassDB::bind_method(D_METHOD("get_current_video_driver"), &_OS::get_current_video_driver); @@ -1414,6 +1432,11 @@ PoolVector _Geometry::build_capsule_planes(float p_radius, float p_height return Geometry::build_capsule_planes(p_radius, p_height, p_sides, p_lats, p_axis); } +bool _Geometry::is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) { + + return Geometry::is_point_in_circle(p_point, p_circle_pos, p_circle_radius); +} + real_t _Geometry::segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) { return Geometry::segment_intersects_circle(p_from, p_to, p_circle_pos, p_circle_radius); @@ -1666,11 +1689,6 @@ Array _Geometry::offset_polyline_2d(const Vector &p_polygon, real_t p_d return ret; } -Vector _Geometry::transform_points_2d(const Vector &p_points, const Transform2D &p_mat) { - - return Geometry::transform_points_2d(p_points, p_mat); -} - Dictionary _Geometry::make_atlas(const Vector &p_rects) { Dictionary ret; @@ -1709,6 +1727,7 @@ void _Geometry::_bind_methods() { ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &_Geometry::build_box_planes); ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &_Geometry::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z)); ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &_Geometry::build_capsule_planes, DEFVAL(Vector3::AXIS_Z)); + ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &_Geometry::is_point_in_circle); ClassDB::bind_method(D_METHOD("segment_intersects_circle", "segment_from", "segment_to", "circle_position", "circle_radius"), &_Geometry::segment_intersects_circle); ClassDB::bind_method(D_METHOD("segment_intersects_segment_2d", "from_a", "to_a", "from_b", "to_b"), &_Geometry::segment_intersects_segment_2d); ClassDB::bind_method(D_METHOD("line_intersects_line_2d", "from_a", "dir_a", "from_b", "dir_b"), &_Geometry::line_intersects_line_2d); @@ -1749,8 +1768,6 @@ void _Geometry::_bind_methods() { ClassDB::bind_method(D_METHOD("offset_polygon_2d", "polygon", "delta", "join_type"), &_Geometry::offset_polygon_2d, DEFVAL(JOIN_SQUARE)); ClassDB::bind_method(D_METHOD("offset_polyline_2d", "polyline", "delta", "join_type", "end_type"), &_Geometry::offset_polyline_2d, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE)); - ClassDB::bind_method(D_METHOD("transform_points_2d", "points", "transform"), &_Geometry::transform_points_2d); - ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &_Geometry::make_atlas); BIND_ENUM_CONSTANT(OPERATION_UNION); @@ -2621,8 +2638,7 @@ void _Thread::_start_func(void *ud) { } } - ERR_EXPLAIN("Could not call function '" + t->target_method.operator String() + "'' starting thread ID: " + t->get_id() + " Reason: " + reason); - ERR_FAIL(); + ERR_FAIL_MSG("Could not call function '" + t->target_method.operator String() + "'' starting thread ID: " + t->get_id() + " Reason: " + reason + "."); } } @@ -2704,10 +2720,7 @@ _Thread::_Thread() { _Thread::~_Thread() { - if (active) { - ERR_EXPLAIN("Reference to a Thread object object was lost while the thread is still running..."); - } - ERR_FAIL_COND(active); + ERR_FAIL_COND_MSG(active, "Reference to a Thread object object was lost while the thread is still running..."); } ///////////////////////////////////// diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index f0f86e003f..693b85710a 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -109,11 +109,11 @@ public: }; enum PowerState { - POWERSTATE_UNKNOWN, /**< cannot determine power status */ - POWERSTATE_ON_BATTERY, /**< Not plugged in, running on the battery */ - POWERSTATE_NO_BATTERY, /**< Plugged in, no battery available */ - POWERSTATE_CHARGING, /**< Plugged in, charging battery */ - POWERSTATE_CHARGED /**< Plugged in, battery charged */ + POWERSTATE_UNKNOWN, // Cannot determine power status. + POWERSTATE_ON_BATTERY, // Not plugged in, running on the battery. + POWERSTATE_NO_BATTERY, // Plugged in, no battery available. + POWERSTATE_CHARGING, // Plugged in, charging battery. + POWERSTATE_CHARGED // Plugged in, battery charged. }; enum Weekday { @@ -127,8 +127,8 @@ public: }; enum Month { - /// Start at 1 to follow Windows SYSTEMTIME structure - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx + // Start at 1 to follow Windows SYSTEMTIME structure + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx MONTH_JANUARY = 1, MONTH_FEBRUARY, MONTH_MARCH, @@ -143,6 +143,11 @@ public: MONTH_DECEMBER }; + void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta); + void global_menu_add_separator(const String &p_menu); + void global_menu_remove_item(const String &p_menu, int p_idx); + void global_menu_clear(const String &p_menu); + Point2 get_mouse_position() const; void set_window_title(const String &p_title); int get_mouse_button_state() const; @@ -259,24 +264,6 @@ public: bool is_scancode_unicode(uint32_t p_unicode) const; int find_scancode_from_string(const String &p_code) const; - /* - struct Date { - - int year; - Month month; - int day; - Weekday weekday; - bool dst; - }; - - struct Time { - - int hour; - int min; - int sec; - }; -*/ - void set_use_file_access_save_and_swap(bool p_enable); void set_native_icon(const String &p_filename); @@ -404,6 +391,7 @@ public: PoolVector segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius); PoolVector segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius); PoolVector segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector &p_planes); + bool is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius); real_t segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius); int get_uv84_normal_bit(const Vector3 &p_vector); @@ -420,17 +408,17 @@ public: OPERATION_INTERSECTION, OPERATION_XOR }; - // 2D polygon boolean operations - Array merge_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // union (add) - Array clip_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // difference (subtract) - Array intersect_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // common area (multiply) - Array exclude_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // all but common area (xor) + // 2D polygon boolean operations. + Array merge_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // Union (add). + Array clip_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // Difference (subtract). + Array intersect_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // Common area (multiply). + Array exclude_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // All but common area (xor). - // 2D polyline vs polygon operations - Array clip_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon); // cut - Array intersect_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon); // chop + // 2D polyline vs polygon operations. + Array clip_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon); // Cut. + Array intersect_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon); // Chop. - // 2D offset polygons/polylines + // 2D offset polygons/polylines. enum PolyJoinType { JOIN_SQUARE, JOIN_ROUND, @@ -446,8 +434,6 @@ public: Array offset_polygon_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE); Array offset_polyline_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE, PolyEndType p_end_type = END_SQUARE); - Vector transform_points_2d(const Vector &p_points, const Transform2D &p_mat); - Dictionary make_atlas(const Vector &p_rects); _Geometry(); @@ -486,24 +472,24 @@ public: Error open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass); Error open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ); - Error open(const String &p_path, ModeFlags p_mode_flags); ///< open a file - void close(); ///< close a file - bool is_open() const; ///< true when file is open + Error open(const String &p_path, ModeFlags p_mode_flags); // open a file. + void close(); // Close a file. + bool is_open() const; // True when file is open. - String get_path() const; /// returns the path for the current open file - String get_path_absolute() const; /// returns the absolute path for the current open file + String get_path() const; // Returns the path for the current open file. + String get_path_absolute() const; // Returns the absolute path for the current open file. - void seek(int64_t p_position); ///< seek to a given position - void seek_end(int64_t p_position = 0); ///< seek from the end of file - int64_t get_position() const; ///< get position in the file - int64_t get_len() const; ///< get size of the file + void seek(int64_t p_position); // Seek to a given position. + void seek_end(int64_t p_position = 0); // Seek from the end of file. + int64_t get_position() const; // Get position in the file. + int64_t get_len() const; // Get size of the file. - bool eof_reached() const; ///< reading passed EOF + bool eof_reached() const; // Reading passed EOF. - uint8_t get_8() const; ///< get a byte - uint16_t get_16() const; ///< get 16 bits uint - uint32_t get_32() const; ///< get 32 bits uint - uint64_t get_64() const; ///< get 64 bits uint + uint8_t get_8() const; // Get a byte. + uint16_t get_16() const; // Get 16 bits uint. + uint32_t get_32() const; // Get 32 bits uint. + uint64_t get_64() const; // Get 64 bits uint. float get_float() const; double get_double() const; @@ -511,27 +497,27 @@ public: Variant get_var(bool p_allow_objects = false) const; - PoolVector get_buffer(int p_length) const; ///< get an array of bytes + PoolVector get_buffer(int p_length) const; // Get an array of bytes. String get_line() const; Vector get_csv_line(const String &p_delim = ",") const; String get_as_text() const; String get_md5(const String &p_path) const; String get_sha256(const String &p_path) const; - /**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac) + /* Use this for files WRITTEN in _big_ endian machines (ie, amiga/mac). * It's not about the current CPU type but file formats. - * this flags get reset to false (little endian) on each open + * This flags get reset to false (little endian) on each open. */ void set_endian_swap(bool p_swap); bool get_endian_swap(); - Error get_error() const; ///< get last error + Error get_error() const; // Get last error. - void store_8(uint8_t p_dest); ///< store a byte - void store_16(uint16_t p_dest); ///< store 16 bits uint - void store_32(uint32_t p_dest); ///< store 32 bits uint - void store_64(uint64_t p_dest); ///< store 64 bits uint + void store_8(uint8_t p_dest); // Store a byte. + void store_16(uint16_t p_dest); // Store 16 bits uint. + void store_32(uint32_t p_dest); // Store 32 bits uint. + void store_64(uint64_t p_dest); // Store 64 bits uint. void store_float(float p_dest); void store_double(double p_dest); @@ -544,11 +530,11 @@ public: virtual void store_pascal_string(const String &p_string); virtual String get_pascal_string(); - void store_buffer(const PoolVector &p_buffer); ///< store an array of bytes + void store_buffer(const PoolVector &p_buffer); // Store an array of bytes. void store_var(const Variant &p_var, bool p_full_objects = false); - bool file_exists(const String &p_name) const; ///< return true if a file exists + bool file_exists(const String &p_name) const; // Return true if a file exists. uint64_t get_modified_time(const String &p_file) const; @@ -570,18 +556,18 @@ protected: public: Error open(const String &p_path); - Error list_dir_begin(bool p_skip_navigational = false, bool p_skip_hidden = false); ///< This starts dir listing + Error list_dir_begin(bool p_skip_navigational = false, bool p_skip_hidden = false); // This starts dir listing. String get_next(); bool current_is_dir() const; - void list_dir_end(); ///< + void list_dir_end(); int get_drive_count(); String get_drive(int p_drive); int get_current_drive(); - Error change_dir(String p_dir); ///< can be relative or absolute, return false on success - String get_current_dir(); ///< return current dir location + Error change_dir(String p_dir); // Can be relative or absolute, return false on success. + String get_current_dir(); // Return current dir location. Error make_dir(String p_dir); Error make_dir_recursive(String p_dir); diff --git a/core/class_db.cpp b/core/class_db.cpp index 794d990083..3ad59bc309 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -480,6 +480,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { for (List::Element *F = snames.front(); F; F = F->next()) { PropertySetGet *psg = t->property_setget.getptr(F->get()); + ERR_FAIL_COND_V(!psg, 0); hash = hash_djb2_one_64(F->get().hash(), hash); hash = hash_djb2_one_64(psg->setter.hash(), hash); @@ -835,10 +836,7 @@ void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { #ifdef DEBUG_METHODS_ENABLED ClassInfo *check = type; while (check) { - if (check->signal_map.has(sname)) { - ERR_EXPLAIN("Type " + String(p_class) + " already has signal: " + String(sname)); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(check->signal_map.has(sname), "Type " + String(p_class) + " already has signal: " + String(sname) + "."); check = check->inherits_ptr; } #endif @@ -923,16 +921,11 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons if (p_setter) { mb_set = get_method(p_class, p_setter); #ifdef DEBUG_METHODS_ENABLED - if (!mb_set) { - ERR_EXPLAIN("Invalid Setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name); - ERR_FAIL(); - } else { - int exp_args = 1 + (p_index >= 0 ? 1 : 0); - if (mb_set->get_argument_count() != exp_args) { - ERR_EXPLAIN("Invalid Function for Setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name); - ERR_FAIL(); - } - } + + ERR_FAIL_COND_MSG(!mb_set, "Invalid setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name + "."); + + int exp_args = 1 + (p_index >= 0 ? 1 : 0); + ERR_FAIL_COND_MSG(mb_set->get_argument_count() != exp_args, "Invalid function for setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name + "."); #endif } @@ -942,25 +935,15 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons mb_get = get_method(p_class, p_getter); #ifdef DEBUG_METHODS_ENABLED - if (!mb_get) { - ERR_EXPLAIN("Invalid Getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name); - ERR_FAIL(); - } else { + ERR_FAIL_COND_MSG(!mb_get, "Invalid getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name + "."); - int exp_args = 0 + (p_index >= 0 ? 1 : 0); - if (mb_get->get_argument_count() != exp_args) { - ERR_EXPLAIN("Invalid Function for Getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name); - ERR_FAIL(); - } - } + int exp_args = 0 + (p_index >= 0 ? 1 : 0); + ERR_FAIL_COND_MSG(mb_get->get_argument_count() != exp_args, "Invalid function for getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name + "."); #endif } #ifdef DEBUG_METHODS_ENABLED - if (type->property_setget.has(p_pinfo.name)) { - ERR_EXPLAIN("Object " + p_class + " already has property: " + p_pinfo.name); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(type->property_setget.has(p_pinfo.name), "Object " + p_class + " already has property: " + p_pinfo.name + "."); #endif OBJTYPE_WLOCK @@ -1239,32 +1222,26 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c #ifdef DEBUG_ENABLED - if (has_method(instance_type, mdname)) { - ERR_EXPLAIN("Class " + String(instance_type) + " already has a method " + String(mdname)); - ERR_FAIL_V(NULL); - } + ERR_FAIL_COND_V_MSG(has_method(instance_type, mdname), NULL, "Class " + String(instance_type) + " already has a method " + String(mdname) + "."); #endif ClassInfo *type = classes.getptr(instance_type); if (!type) { - ERR_PRINTS("Couldn't bind method '" + mdname + "' for instance: " + instance_type); memdelete(p_bind); - ERR_FAIL_V(NULL); + ERR_FAIL_V_MSG(NULL, "Couldn't bind method '" + mdname + "' for instance: " + instance_type + "."); } if (type->method_map.has(mdname)) { memdelete(p_bind); // overloading not supported - ERR_EXPLAIN("Method already bound: " + instance_type + "::" + mdname); - ERR_FAIL_V(NULL); + ERR_FAIL_V_MSG(NULL, "Method already bound: " + instance_type + "::" + mdname + "."); } #ifdef DEBUG_METHODS_ENABLED if (method_name.args.size() > p_bind->get_argument_count()) { memdelete(p_bind); - ERR_EXPLAIN("Method definition provides more arguments than the method actually has: " + instance_type + "::" + mdname); - ERR_FAIL_V(NULL); + ERR_FAIL_V_MSG(NULL, "Method definition provides more arguments than the method actually has: " + instance_type + "::" + mdname + "."); } p_bind->set_argument_names(method_name.args); diff --git a/core/class_db.h b/core/class_db.h index 3d9a695f02..092469beb7 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -35,10 +35,6 @@ #include "core/object.h" #include "core/print_string.h" -/** - @author Juan Linietsky -*/ - /** To bind more then 6 parameters include this: * #include "core/method_bind_ext.gen.inc" */ @@ -310,8 +306,7 @@ public: if (type->method_map.has(p_name)) { memdelete(bind); // overloading not supported - ERR_EXPLAIN("Method already bound: " + instance_type + "::" + p_name); - ERR_FAIL_V(NULL); + ERR_FAIL_V_MSG(NULL, "Method already bound: " + instance_type + "::" + p_name + "."); } type->method_map[p_name] = bind; #ifdef DEBUG_METHODS_ENABLED diff --git a/core/color.cpp b/core/color.cpp index 1843532124..a54a3115cc 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -335,36 +335,23 @@ Color Color::html(const String &p_color) { } else if (color.length() == 6) { alpha = false; } else { - ERR_EXPLAIN("Invalid Color Code: " + p_color); - ERR_FAIL_V(Color()); + ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_color + "."); } int a = 255; if (alpha) { a = _parse_col(color, 0); - if (a < 0) { - ERR_EXPLAIN("Invalid Color Code: " + p_color); - ERR_FAIL_V(Color()); - } + ERR_FAIL_COND_V_MSG(a < 0, Color(), "Invalid color code: " + p_color + "."); } int from = alpha ? 2 : 0; int r = _parse_col(color, from + 0); - if (r < 0) { - ERR_EXPLAIN("Invalid Color Code: " + p_color); - ERR_FAIL_V(Color()); - } + ERR_FAIL_COND_V_MSG(r < 0, Color(), "Invalid color code: " + p_color + "."); int g = _parse_col(color, from + 2); - if (g < 0) { - ERR_EXPLAIN("Invalid Color Code: " + p_color); - ERR_FAIL_V(Color()); - } + ERR_FAIL_COND_V_MSG(g < 0, Color(), "Invalid color code: " + p_color + "."); int b = _parse_col(color, from + 4); - if (b < 0) { - ERR_EXPLAIN("Invalid Color Code: " + p_color); - ERR_FAIL_V(Color()); - } + ERR_FAIL_COND_V_MSG(b < 0, Color(), "Invalid color code: " + p_color + "."); return Color(r / 255.0, g / 255.0, b / 255.0, a / 255.0); } @@ -425,12 +412,8 @@ Color Color::named(const String &p_name) { name = name.to_lower(); const Map::Element *color = _named_colors.find(name); - if (color) { - return color->value(); - } else { - ERR_EXPLAIN("Invalid Color Name: " + p_name); - ERR_FAIL_V(Color()); - } + ERR_FAIL_NULL_V_MSG(color, Color(), "Invalid color name: " + p_name + "."); + return color->value(); } String _to_hex(float p_val) { @@ -523,8 +506,7 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { // FIXME: Remove once Godot 3.1 has been released float Color::gray() const { - ERR_EXPLAIN("Color.gray() is deprecated and will be removed in a future version. Use Color.get_v() for a better grayscale approximation."); - WARN_DEPRECATED; + WARN_DEPRECATED_MSG("Color.gray() is deprecated and will be removed in a future version. Use Color.get_v() for a better grayscale approximation."); return (r + g + b) / 3.0; } diff --git a/core/color.h b/core/color.h index 77f95b5dc9..8fb78d1ced 100644 --- a/core/color.h +++ b/core/color.h @@ -33,9 +33,7 @@ #include "core/math/math_funcs.h" #include "core/ustring.h" -/** - @author Juan Linietsky -*/ + struct Color { union { diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h index 3789eda5db..98f5bc56d7 100644 --- a/core/command_queue_mt.h +++ b/core/command_queue_mt.h @@ -37,10 +37,6 @@ #include "core/simple_type.h" #include "core/typedefs.h" -/** - @author Juan Linietsky -*/ - #define COMMA(N) _COMMA_##N #define _COMMA_0 #define _COMMA_1 , 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 Crypto::generate_rsa(int p_bytes) { + ERR_FAIL_V_MSG(NULL, "generate_rsa is not available when mbedtls module is disabled."); +} + +Ref Crypto::generate_self_signed_certificate(Ref 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 *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 cert = p_resource; + Ref 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 *p_extensions) const { + + const X509Certificate *cert = Object::cast_to(*p_resource); + const CryptoKey *key = Object::cast_to(*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(*p_resource) || Object::cast_to(*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 generate_rsa(int p_bytes); + virtual Ref generate_self_signed_certificate(Ref 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 *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 *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 similarity index 86% rename from core/math/crypto_core.cpp rename to 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 similarity index 90% rename from core/math/crypto_core.h rename to 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/engine.cpp b/core/engine.cpp index 0dd0459403..937439faaf 100644 --- a/core/engine.cpp +++ b/core/engine.cpp @@ -197,10 +197,7 @@ void Engine::add_singleton(const Singleton &p_singleton) { Object *Engine::get_singleton_object(const String &p_name) const { const Map::Element *E = singleton_ptrs.find(p_name); - if (!E) { - ERR_EXPLAIN("Failed to retrieve non-existent singleton '" + p_name + "'"); - ERR_FAIL_V(NULL); - } + ERR_FAIL_COND_V_MSG(!E, NULL, "Failed to retrieve non-existent singleton '" + p_name + "'."); return E->get(); }; diff --git a/core/error_macros.h b/core/error_macros.h index 69874e280b..65802de9d2 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -140,6 +140,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } while (0); // (*) +#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \ + do { \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + return; \ + } \ + _err_error_exists = false; \ + } while (0); // (*) + /** An index has failed if m_index<0 or m_index >=m_size, the function exits. * This function returns an error value, if returning Error, please select the most * appropriate error condition from error_macros.h @@ -154,6 +164,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } while (0); // (*) +#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ + do { \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + return m_retval; \ + } \ + _err_error_exists = false; \ + } while (0); // (*) + /** An index has failed if m_index >=m_size, the function exits. * This function returns an error value, if returning Error, please select the most * appropriate error condition from error_macros.h @@ -168,6 +188,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } while (0); // (*) +#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ + do { \ + if (unlikely((m_index) >= (m_size))) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + return m_retval; \ + } \ + _err_error_exists = false; \ + } while (0); // (*) + /** Use this one if there is no sensible fallback, that is, the error is unrecoverable. * We'll return a null reference and try to keep running. */ @@ -179,6 +209,15 @@ extern bool _err_error_exists; } \ } while (0); // (*) +#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \ + do { \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), true); \ + GENERATE_TRAP \ + } \ + } while (0); // (*) + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the function will exit. */ @@ -192,6 +231,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_FAIL_NULL_MSG(m_param, m_msg) \ + { \ + if (unlikely(!m_param)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter ' " _STR(m_param) " ' is null."); \ + return; \ + } \ + _err_error_exists = false; \ + } + #define ERR_FAIL_NULL_V(m_param, m_retval) \ { \ if (unlikely(!m_param)) { \ @@ -201,6 +250,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ + { \ + if (unlikely(!m_param)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter ' " _STR(m_param) " ' is null."); \ + return m_retval; \ + } \ + _err_error_exists = false; \ + } + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the function will exit. */ @@ -214,6 +273,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_FAIL_COND_MSG(m_cond, m_msg) \ + { \ + if (unlikely(m_cond)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true."); \ + return; \ + } \ + _err_error_exists = false; \ + } + /** Use this one if there is no sensible fallback, that is, the error is unrecoverable. */ @@ -225,6 +294,15 @@ extern bool _err_error_exists; } \ } +#define CRASH_COND_MSG(m_cond, m_msg) \ + { \ + if (unlikely(m_cond)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition ' " _STR(m_cond) " ' is true."); \ + GENERATE_TRAP \ + } \ + } + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the function will exit. * This function returns an error value, if returning Error, please select the most @@ -240,6 +318,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ + { \ + if (unlikely(m_cond)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. returned: " _STR(m_retval)); \ + return m_retval; \ + } \ + _err_error_exists = false; \ + } + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the loop will skip to the next iteration. */ @@ -253,6 +341,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_CONTINUE_MSG(m_cond, m_msg) \ + { \ + if (unlikely(m_cond)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Continuing..:"); \ + continue; \ + } \ + _err_error_exists = false; \ + } + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the loop will break */ @@ -266,6 +364,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_BREAK_MSG(m_cond, m_msg) \ + { \ + if (unlikely(m_cond)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \ + break; \ + } \ + _err_error_exists = false; \ + } + /** Print an error string and return */ @@ -276,6 +384,12 @@ extern bool _err_error_exists; return; \ } +#define ERR_FAIL_MSG(m_msg) \ + { \ + ERR_EXPLAIN(m_msg); \ + ERR_FAIL(); \ + } + /** Print an error string and return with value */ @@ -286,6 +400,12 @@ extern bool _err_error_exists; return m_value; \ } +#define ERR_FAIL_V_MSG(m_value, m_msg) \ + { \ + ERR_EXPLAIN(m_msg); \ + ERR_FAIL_V(m_value); \ + } + /** Use this one if there is no sensible fallback, that is, the error is unrecoverable. */ @@ -295,6 +415,12 @@ extern bool _err_error_exists; GENERATE_TRAP \ } +#define CRASH_NOW_MSG(m_msg) \ + { \ + ERR_EXPLAIN(m_msg); \ + CRASH_NOW(); \ + } + /** Print an error string. */ @@ -355,4 +481,15 @@ extern bool _err_error_exists; } \ } +#define WARN_DEPRECATED_MSG(m_msg) \ + { \ + static volatile bool warning_shown = false; \ + if (!warning_shown) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \ + _err_error_exists = false; \ + warning_shown = true; \ + } \ + } + #endif diff --git a/core/func_ref.cpp b/core/func_ref.cpp index 3d03137d09..66ef27f6b9 100644 --- a/core/func_ref.cpp +++ b/core/func_ref.cpp @@ -46,6 +46,17 @@ Variant FuncRef::call_func(const Variant **p_args, int p_argcount, Variant::Call return obj->call(function, p_args, p_argcount, r_error); } +Variant FuncRef::call_funcv(const Array &p_args) { + + ERR_FAIL_COND_V(id == 0, Variant()); + + Object *obj = ObjectDB::get_instance(id); + + ERR_FAIL_COND_V(!obj, Variant()); + + return obj->callv(function, p_args); +} + void FuncRef::set_instance(Object *p_obj) { ERR_FAIL_NULL(p_obj); @@ -77,6 +88,8 @@ void FuncRef::_bind_methods() { ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_func", &FuncRef::call_func, mi, defargs); } + ClassDB::bind_method(D_METHOD("call_funcv", "arg_array"), &FuncRef::call_funcv); + ClassDB::bind_method(D_METHOD("set_instance", "instance"), &FuncRef::set_instance); ClassDB::bind_method(D_METHOD("set_function", "name"), &FuncRef::set_function); ClassDB::bind_method(D_METHOD("is_valid"), &FuncRef::is_valid); diff --git a/core/func_ref.h b/core/func_ref.h index a143b58bf0..af0bf63203 100644 --- a/core/func_ref.h +++ b/core/func_ref.h @@ -44,6 +44,7 @@ protected: public: Variant call_func(const Variant **p_args, int p_argcount, Variant::CallError &r_error); + Variant call_funcv(const Array &p_args); void set_instance(Object *p_obj); void set_function(const StringName &p_func); bool is_valid() const; diff --git a/core/hash_map.h b/core/hash_map.h index 1513d7a65b..81ddc376d0 100644 --- a/core/hash_map.h +++ b/core/hash_map.h @@ -151,11 +151,7 @@ private: return; Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power)); - if (!new_hash_table) { - - ERR_PRINT("Out of Memory"); - return; - } + ERR_FAIL_COND_MSG(!new_hash_table, "Out of memory."); for (int i = 0; i < (1 << new_hash_table_power); i++) { @@ -208,10 +204,7 @@ private: /* if element doesn't exist, create it */ Element *e = memnew(Element); - if (!e) { - ERR_EXPLAIN("Out of memory"); - ERR_FAIL_V(NULL); - } + ERR_FAIL_COND_V_MSG(!e, NULL, "Out of memory."); uint32_t hash = Hasher::hash(p_key); uint32_t index = hash & ((1 << hash_table_power) - 1); e->next = hash_table[index]; @@ -498,10 +491,7 @@ public: } else { /* get the next key */ const Element *e = get_element(*p_key); - if (!e) { - ERR_EXPLAIN("Invalid key supplied") - ERR_FAIL_V(NULL); - } + ERR_FAIL_COND_V_MSG(!e, NULL, "Invalid key supplied."); if (e->next) { /* if there is a "next" in the list, return that */ return &e->next->pair.key; diff --git a/core/image.cpp b/core/image.cpp index 10778eced6..900efb0eb0 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -83,6 +83,7 @@ const char *Image::format_names[Image::FORMAT_MAX] = { }; SavePNGFunc Image::save_png_func = NULL; +SaveEXRFunc Image::save_exr_func = NULL; void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel) { @@ -422,8 +423,7 @@ void Image::convert(Format p_new_format) { if (format > FORMAT_RGBE9995 || p_new_format > FORMAT_RGBE9995) { - ERR_EXPLAIN("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead."); - ERR_FAIL(); + ERR_FAIL_MSG("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead."); } else if (format > FORMAT_RGBA8 || p_new_format > FORMAT_RGBA8) { @@ -753,15 +753,14 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict for (int32_t buffer_x = 0; buffer_x < dst_width; buffer_x++) { - float src_real_x = buffer_x * x_scale; - int32_t src_x = src_real_x; - - int32_t start_x = MAX(0, src_x - half_kernel + 1); - int32_t end_x = MIN(src_width - 1, src_x + half_kernel); + // The corresponding point on the source image + float src_x = (buffer_x + 0.5f) * x_scale; // Offset by 0.5 so it uses the pixel's center + int32_t start_x = MAX(0, int32_t(src_x) - half_kernel + 1); + int32_t end_x = MIN(src_width - 1, int32_t(src_x) + half_kernel); // Create the kernel used by all the pixels of the column for (int32_t target_x = start_x; target_x <= end_x; target_x++) - kernel[target_x - start_x] = _lanczos((src_real_x - target_x) / scale_factor); + kernel[target_x - start_x] = _lanczos((target_x + 0.5f - src_x) / scale_factor); for (int32_t buffer_y = 0; buffer_y < src_height; buffer_y++) { @@ -804,14 +803,12 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict for (int32_t dst_y = 0; dst_y < dst_height; dst_y++) { - float buffer_real_y = dst_y * y_scale; - int32_t buffer_y = buffer_real_y; - - int32_t start_y = MAX(0, buffer_y - half_kernel + 1); - int32_t end_y = MIN(src_height - 1, buffer_y + half_kernel); + float buffer_y = (dst_y + 0.5f) * y_scale; + int32_t start_y = MAX(0, int32_t(buffer_y) - half_kernel + 1); + int32_t end_y = MIN(src_height - 1, int32_t(buffer_y) + half_kernel); for (int32_t target_y = start_y; target_y <= end_y; target_y++) - kernel[target_y - start_y] = _lanczos((buffer_real_y - target_y) / scale_factor); + kernel[target_y - start_y] = _lanczos((target_y + 0.5f - buffer_y) / scale_factor); for (int32_t dst_x = 0; dst_x < dst_width; dst_x++) { @@ -866,10 +863,7 @@ bool Image::is_size_po2() const { void Image::resize_to_po2(bool p_square) { - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot resize in indexed, compressed or custom image formats."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats."); int w = next_power_of_2(width); int h = next_power_of_2(height); @@ -885,15 +879,9 @@ void Image::resize_to_po2(bool p_square) { void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { - if (data.size() == 0) { - ERR_EXPLAIN("Cannot resize image before creating it, use create() or create_from_data() first."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use create() or create_from_data() first."); - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot resize in indexed, compressed or custom image formats."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats."); bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; @@ -1106,10 +1094,8 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot crop in indexed, compressed or custom image formats."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot crop in compressed or custom image formats."); + ERR_FAIL_COND(p_x < 0); ERR_FAIL_COND(p_y < 0); ERR_FAIL_COND(p_width <= 0); @@ -1163,10 +1149,7 @@ void Image::crop(int p_width, int p_height) { void Image::flip_y() { - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot flip_y in indexed, compressed or custom image formats."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot flip_y in compressed or custom image formats."); bool used_mipmaps = has_mipmaps(); if (used_mipmaps) { @@ -1199,10 +1182,7 @@ void Image::flip_y() { void Image::flip_x() { - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot flip_x in indexed, compressed or custom image formats."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot flip_x in compressed or custom image formats."); bool used_mipmaps = has_mipmaps(); if (used_mipmaps) { @@ -1461,15 +1441,9 @@ void Image::normalize() { Error Image::generate_mipmaps(bool p_renormalize) { - if (!_can_modify(format)) { - ERR_EXPLAIN("Cannot generate mipmaps in indexed, compressed or custom image formats."); - ERR_FAIL_V(ERR_UNAVAILABLE); - } + ERR_FAIL_COND_V_MSG(!_can_modify(format), ERR_UNAVAILABLE, "Cannot generate mipmaps in compressed or custom image formats."); - if (width == 0 || height == 0) { - ERR_EXPLAIN("Cannot generate mipmaps with width or height equal to 0."); - ERR_FAIL_V(ERR_UNCONFIGURED); - } + ERR_FAIL_COND_V_MSG(width == 0 || height == 0, ERR_UNCONFIGURED, "Cannot generate mipmaps with width or height equal to 0."); int mmcount; @@ -1620,10 +1594,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma int mm; int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); - if (size != p_data.size()) { - ERR_EXPLAIN("Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes."); - ERR_FAIL_COND(p_data.size() != size); - } + ERR_FAIL_COND_MSG(p_data.size() != size, "Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes."); height = p_height; width = p_width; @@ -1917,6 +1888,14 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref((Image *)this)); } +Error Image::save_exr(const String &p_path, bool p_grayscale) const { + + if (save_exr_func == NULL) + return ERR_UNAVAILABLE; + + return save_exr_func(p_path, Ref((Image *)this), p_grayscale); +} + int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) { int mm; @@ -2405,10 +2384,7 @@ Color Image::get_pixel(int p_x, int p_y) const { uint8_t *ptr = write_lock.ptr(); #ifdef DEBUG_ENABLED - if (!ptr) { - ERR_EXPLAIN("Image must be locked with 'lock()' before using get_pixel()"); - ERR_FAIL_V(Color()); - } + ERR_FAIL_COND_V_MSG(!ptr, Color(), "Image must be locked with 'lock()' before using get_pixel()."); ERR_FAIL_INDEX_V(p_x, width, Color()); ERR_FAIL_INDEX_V(p_y, height, Color()); @@ -2524,8 +2500,7 @@ Color Image::get_pixel(int p_x, int p_y) const { return Color::from_rgbe9995(((uint32_t *)ptr)[ofs]); } default: { - ERR_EXPLAIN("Can't get_pixel() on compressed image, sorry."); - ERR_FAIL_V(Color()); + ERR_FAIL_V_MSG(Color(), "Can't get_pixel() on compressed image, sorry."); } } } @@ -2538,10 +2513,7 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { uint8_t *ptr = write_lock.ptr(); #ifdef DEBUG_ENABLED - if (!ptr) { - ERR_EXPLAIN("Image must be locked with 'lock()' before using set_pixel()"); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(!ptr, "Image must be locked with 'lock()' before using set_pixel()."); ERR_FAIL_INDEX(p_x, width); ERR_FAIL_INDEX(p_y, height); @@ -2653,8 +2625,7 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { } break; default: { - ERR_EXPLAIN("Can't set_pixel() on compressed image, sorry."); - ERR_FAIL(); + ERR_FAIL_MSG("Can't set_pixel() on compressed image, sorry."); } } } @@ -2746,6 +2717,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load", "path"), &Image::load); ClassDB::bind_method(D_METHOD("save_png", "path"), &Image::save_png); + ClassDB::bind_method(D_METHOD("save_exr", "path", "grayscale"), &Image::save_exr, DEFVAL(false)); ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha); ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible); diff --git a/core/image.h b/core/image.h index cc796789cd..f29a30cda0 100644 --- a/core/image.h +++ b/core/image.h @@ -49,11 +49,14 @@ class Image; typedef Error (*SavePNGFunc)(const String &p_path, const Ref &p_img); typedef Ref (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size); +typedef Error (*SaveEXRFunc)(const String &p_path, const Ref &p_img, bool p_grayscale); + class Image : public Resource { GDCLASS(Image, Resource); public: static SavePNGFunc save_png_func; + static SaveEXRFunc save_exr_func; enum { MAX_WIDTH = 16384, // force a limit somehow @@ -217,9 +220,7 @@ public: /** * Resize the image, using the preferred interpolation method. - * Indexed-Color images always use INTERPOLATE_NEAREST. */ - void resize_to_po2(bool p_square = false); void resize(int p_width, int p_height, Interpolation p_interpolation = INTERPOLATE_BILINEAR); void shrink_x2(); @@ -258,6 +259,7 @@ public: Error load(const String &p_path); Error save_png(const String &p_path) const; + Error save_exr(const String &p_path, bool p_grayscale) const; /** * create an empty image diff --git a/core/input_map.cpp b/core/input_map.cpp index 165999f081..2a8ac435fe 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -192,10 +192,7 @@ bool InputMap::event_is_action(const Ref &p_event, const StringName bool InputMap::event_get_action_status(const Ref &p_event, const StringName &p_action, bool *p_pressed, float *p_strength) const { Map::Element *E = input_map.find(p_action); - if (!E) { - ERR_EXPLAIN("Request for nonexistent InputMap action: " + String(p_action)); - ERR_FAIL_V(false); - } + ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action: " + String(p_action) + "."); Ref input_event_action = p_event; if (input_event_action.is_valid()) { diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index f7fb72c089..9063e028be 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -86,10 +86,7 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const { if (!values.has(p_section) || !values[p_section].has(p_key)) { - if (p_default.get_type() == Variant::NIL) { - ERR_EXPLAIN("Couldn't find the given section/key and no default was given"); - ERR_FAIL_V(p_default); - } + ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, p_default, "Couldn't find the given section/key and no default was given."); return p_default; } return values[p_section][p_key]; @@ -204,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); } @@ -271,7 +268,7 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { memdelete(f); return OK; } else if (err != OK) { - ERR_PRINTS("ConfgFile::load - " + p_path + ":" + itos(lines) + " error: " + error_text); + ERR_PRINTS("ConfgFile::load - " + p_path + ":" + itos(lines) + " error: " + error_text + "."); memdelete(f); return err; } diff --git a/core/io/file_access_buffered.cpp b/core/io/file_access_buffered.cpp index 15523a49a9..f72ad61da6 100644 --- a/core/io/file_access_buffered.cpp +++ b/core/io/file_access_buffered.cpp @@ -87,10 +87,8 @@ bool FileAccessBuffered::eof_reached() const { } uint8_t FileAccessBuffered::get_8() const { - if (!file.open) { - ERR_EXPLAIN("Can't get data, when file is not opened."); - ERR_FAIL_V(0); - } + + ERR_FAIL_COND_V_MSG(!file.open, 0, "Can't get data, when file is not opened."); uint8_t byte = 0; if (cache_data_left() >= 1) { @@ -104,10 +102,8 @@ uint8_t FileAccessBuffered::get_8() const { } int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const { - if (!file.open) { - ERR_EXPLAIN("Can't get buffer, when file is not opened."); - ERR_FAIL_V(-1); - } + + ERR_FAIL_COND_V_MSG(!file.open, -1, "Can't get buffer, when file is not opened."); if (p_length > cache_size) { diff --git a/core/io/file_access_buffered_fa.h b/core/io/file_access_buffered_fa.h index 6e806e7b3f..c8cee04208 100644 --- a/core/io/file_access_buffered_fa.h +++ b/core/io/file_access_buffered_fa.h @@ -40,10 +40,7 @@ class FileAccessBufferedFA : public FileAccessBuffered { int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const { - if (!f.is_open()) { - ERR_EXPLAIN("Can't read data block, when file is not opened."); - ERR_FAIL_V(-1); - } + ERR_FAIL_COND_V_MSG(!f.is_open(), -1, "Can't read data block when file is not opened."); ((T *)&f)->seek(p_offset); diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 6c4310a572..102cd9cf6c 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -208,7 +208,8 @@ void FileAccessCompressed::seek(size_t p_position) { if (p_position == read_total) { at_end = true; } else { - + at_end = false; + read_eof = false; int block_idx = p_position / block_size; if (block_idx != read_block) { diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index ccee6aeb15..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" @@ -94,8 +94,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vectorfiles.insert(filename); } @@ -171,10 +171,8 @@ bool PackedSourcePCK::try_open_pack(const String &p_path) { uint32_t ver_minor = f->get_32(); f->get_32(); // ver_rev - ERR_EXPLAIN("Pack version unsupported: " + itos(version)); - ERR_FAIL_COND_V(version != PACK_VERSION, false); - ERR_EXPLAIN("Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor)); - ERR_FAIL_COND_V(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false); + ERR_FAIL_COND_V_MSG(version != PACK_VERSION, false, "Pack version unsupported: " + itos(version) + "."); + ERR_FAIL_COND_V_MSG(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false, "Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + "."); for (int i = 0; i < 16; i++) { //reserved @@ -322,10 +320,9 @@ bool FileAccessPack::file_exists(const String &p_name) { FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) : pf(p_file), f(FileAccess::open(pf.pack, FileAccess::READ)) { - if (!f) { - ERR_EXPLAIN("Can't open pack-referenced file: " + String(pf.pack)); - ERR_FAIL_COND(!f); - } + + ERR_FAIL_COND_MSG(!f, "Can't open pack-referenced file: " + String(pf.pack) + "."); + f->seek(pf.offset); pos = 0; eof = false; @@ -463,11 +460,15 @@ String DirAccessPack::get_current_dir() { bool DirAccessPack::file_exists(String p_file) { + p_file = fix_path(p_file); + return current->files.has(p_file); } bool DirAccessPack::dir_exists(String p_dir) { + p_dir = fix_path(p_dir); + return current->subdirs.has(p_dir); } diff --git a/core/io/image_loader.h b/core/io/image_loader.h index ae4b72a534..af6b0551a3 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -37,24 +37,8 @@ #include "core/os/file_access.h" #include "core/ustring.h" -/** - @author Juan Linietsky -*/ - -/** - * @class ImageScanLineLoader - * @author Juan Linietsky - * - - */ class ImageLoader; -/** - * @class ImageLoader - * Base Class and singleton for loading images from disk - * Can load images in one go, or by scanline - */ - class ImageFormatLoader { friend class ImageLoader; friend class ResourceFormatLoaderImage; diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index 9305afac5f..df4be9b9fd 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -81,8 +81,7 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) { } else if (c == ':') { break; } else { - ERR_EXPLAIN("Invalid character in ipv6 address: " + p_string); - ERR_FAIL(); + ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + "."); }; ret = ret << 4; ret += n; @@ -126,9 +125,7 @@ void IP_Address::_parse_ipv6(const String &p_string) { ++parts_count; }; } else { - - ERR_EXPLAIN("Invalid character in IPv6 address: " + p_string); - ERR_FAIL(); + ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + "."); }; }; @@ -166,10 +163,7 @@ void IP_Address::_parse_ipv4(const String &p_string, int p_start, uint8_t *p_ret }; int slices = ip.get_slice_count("."); - if (slices != 4) { - ERR_EXPLAIN("Invalid IP Address String: " + ip); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(slices != 4, "Invalid IP address string: " + ip + "."); for (int i = 0; i < 4; i++) { p_ret[i] = ip.get_slicec('.', i).to_int(); } @@ -229,7 +223,7 @@ IP_Address::IP_Address(const String &p_string) { valid = true; } else { - ERR_PRINT("Invalid IP address"); + ERR_PRINT("Invalid IP address."); } } diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index dc5581ea01..b386feb14c 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -377,11 +377,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } } break; - /*case Variant::RESOURCE: { - - ERR_EXPLAIN("Can't marshallize resources"); - ERR_FAIL_V(ERR_INVALID_DATA); //no, i'm sorry, no go - } break;*/ case Variant::_RID: { r_variant = RID(); @@ -1066,11 +1061,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 * 4; } break; - /*case Variant::RESOURCE: { - - ERR_EXPLAIN("Can't marshallize resources"); - ERR_FAIL_V(ERR_INVALID_DATA); //no, i'm sorry, no go - } break;*/ case Variant::_RID: { } break; diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 33dc4dbde4..ed6905c9a6 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -33,6 +33,10 @@ #include "core/io/marshalls.h" #include "scene/main/node.h" +#ifdef DEBUG_ENABLED +#include "core/os/os.h" +#endif + _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) { switch (mode) { @@ -146,8 +150,7 @@ void MultiplayerAPI::set_network_peer(const Ref &p_pee network_peer = p_peer; - ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected."); - ERR_FAIL_COND(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED); + ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Supplied NetworkedNetworkPeer must be connecting or connected."); if (network_peer.is_valid()) { network_peer->connect("peer_connected", this, "_add_peer"); @@ -164,10 +167,16 @@ Ref MultiplayerAPI::get_network_peer() const { void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_EXPLAIN("Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it"); - ERR_FAIL_COND(root_node == NULL); - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_packet_len < 1); + ERR_FAIL_COND_MSG(root_node == NULL, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); + ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); + +#ifdef DEBUG_ENABLED + if (profiling) { + bandwidth_incoming_data.write[bandwidth_incoming_pointer].timestamp = OS::get_singleton()->get_ticks_msec(); + bandwidth_incoming_data.write[bandwidth_incoming_pointer].packet_size = p_packet_len; + bandwidth_incoming_pointer = (bandwidth_incoming_pointer + 1) % bandwidth_incoming_data.size(); + } +#endif uint8_t packet_type = p_packet[0]; @@ -186,13 +195,11 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ case NETWORK_COMMAND_REMOTE_CALL: case NETWORK_COMMAND_REMOTE_SET: { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_packet_len < 6); + ERR_FAIL_COND_MSG(p_packet_len < 6, "Invalid packet received. Size too small."); Node *node = _process_get_node(p_from, p_packet, p_packet_len); - ERR_EXPLAIN("Invalid packet received. Requested node was not found."); - ERR_FAIL_COND(node == NULL); + ERR_FAIL_COND_MSG(node == NULL, "Invalid packet received. Requested node was not found."); // Detect cstring end. int len_end = 5; @@ -202,8 +209,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ } } - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(len_end >= p_packet_len); + ERR_FAIL_COND_MSG(len_end >= p_packet_len, "Invalid packet received. Size too small."); StringName name = String::utf8((const char *)&p_packet[5]); @@ -235,8 +241,7 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int int ofs = target & 0x7FFFFFFF; - ERR_EXPLAIN("Invalid packet received. Size smaller than declared."); - ERR_FAIL_COND_V(ofs >= p_packet_len, NULL); + ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, NULL, "Invalid packet received. Size smaller than declared."); String paths; paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); @@ -246,33 +251,30 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int node = root_node->get_node(np); if (!node) - ERR_PRINTS("Failed to get path from RPC: " + String(np)); + ERR_PRINTS("Failed to get path from RPC: " + String(np) + "."); } else { // Use cached path. int id = target; Map::Element *E = path_get_cache.find(p_from); - ERR_EXPLAIN("Invalid packet received. Requests invalid peer cache."); - ERR_FAIL_COND_V(!E, NULL); + ERR_FAIL_COND_V_MSG(!E, NULL, "Invalid packet received. Requests invalid peer cache."); Map::Element *F = E->get().nodes.find(id); - ERR_EXPLAIN("Invalid packet received. Unabled to find requested cached node."); - ERR_FAIL_COND_V(!F, NULL); + ERR_FAIL_COND_V_MSG(!F, NULL, "Invalid packet received. Unabled to find requested cached node."); PathGetCache::NodeInfo *ni = &F->get(); // Do proper caching later. node = root_node->get_node(ni->path); if (!node) - ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path)); + ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path) + "."); } return node; } void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_offset >= p_packet_len); + ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); // Check that remote can call the RPC on this node. RPCMode rpc_mode = RPC_MODE_DISABLED; @@ -284,8 +286,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ } bool can_call = _can_call_mode(p_node, rpc_mode, p_from); - ERR_EXPLAIN("RPC '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); - ERR_FAIL_COND(!can_call); + ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); int argc = p_packet[p_offset]; Vector args; @@ -295,15 +296,21 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ p_offset++; +#ifdef DEBUG_ENABLED + if (profiling) { + ObjectID id = p_node->get_instance_id(); + _init_node_profile(id); + profiler_frame_data[id].incoming_rpc += 1; + } +#endif + for (int i = 0; i < argc; i++) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_offset >= p_packet_len); + ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); int vlen; Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen, allow_object_decoding || network_peer->is_object_decoding_allowed()); - ERR_EXPLAIN("Invalid packet received. Unable to decode RPC argument."); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument."); argp.write[i] = &args[i]; p_offset += vlen; @@ -321,8 +328,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_offset >= p_packet_len); + ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); // Check that remote can call the RSET on this node. RPCMode rset_mode = RPC_MODE_DISABLED; @@ -334,28 +340,33 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p } bool can_call = _can_call_mode(p_node, rset_mode, p_from); - ERR_EXPLAIN("RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); - ERR_FAIL_COND(!can_call); + ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); + +#ifdef DEBUG_ENABLED + if (profiling) { + ObjectID id = p_node->get_instance_id(); + _init_node_profile(id); + profiler_frame_data[id].incoming_rset += 1; + } +#endif Variant value; Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed()); - ERR_EXPLAIN("Invalid packet received. Unable to decode RSET value."); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RSET value."); bool valid; p_node->set(p_name, value, &valid); if (!valid) { - String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class(); + String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class() + "."; ERR_PRINTS(error); } } void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_packet_len < 5); + ERR_FAIL_COND_MSG(p_packet_len < 5, "Invalid packet received. Size too small."); int id = decode_uint32(&p_packet[1]); String paths; @@ -390,8 +401,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_packet_len < 2); + ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); String paths; paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1); @@ -399,12 +409,10 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, NodePath path = paths; PathSentCache *psc = path_send_cache.getptr(path); - ERR_EXPLAIN("Invalid packet received. Tries to confirm a path which was not found in cache."); - ERR_FAIL_COND(!psc); + ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); Map::Element *E = psc->confirmed_peers.find(p_from); - ERR_EXPLAIN("Invalid packet received. Source peer was not found in cache for the given path."); - ERR_FAIL_COND(!E); + ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path."); E->get() = true; } @@ -460,39 +468,22 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) { - if (network_peer.is_null()) { - ERR_EXPLAIN("Attempt to remote call/set when networking is not active in SceneTree."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree."); - if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING) { - ERR_EXPLAIN("Attempt to remote call/set when networking is not connected yet in SceneTree."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); - if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) { - ERR_EXPLAIN("Attempt to remote call/set when networking is disconnected."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected."); - if (p_argcount > 255) { - ERR_EXPLAIN("Too many arguments >255."); - ERR_FAIL(); - } + ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255."); if (p_to != 0 && !connected_peers.has(ABS(p_to))) { - if (p_to == network_peer->get_unique_id()) { - ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id())); - } else { - ERR_EXPLAIN("Attempt to remote call unexisting ID: " + itos(p_to)); - } + ERR_FAIL_COND_MSG(p_to == network_peer->get_unique_id(), "Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()) + "."); - ERR_FAIL(); + ERR_FAIL_MSG("Attempt to remote call unexisting ID: " + itos(p_to) + "."); } NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path()); - ERR_EXPLAIN("Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); - ERR_FAIL_COND(from_path.is_empty()); + ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); // See if the path is cached. PathSentCache *psc = path_send_cache.getptr(from_path); @@ -530,8 +521,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p if (p_set) { // Set argument. Error err = encode_variant(*p_arg[0], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed()); - ERR_EXPLAIN("Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!"); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!"); MAKE_ROOM(ofs + len); encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed()); ofs += len; @@ -543,14 +533,21 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p ofs += 1; for (int i = 0; i < p_argcount; i++) { Error err = encode_variant(*p_arg[i], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed()); - ERR_EXPLAIN("Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); MAKE_ROOM(ofs + len); encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed()); ofs += len; } } +#ifdef DEBUG_ENABLED + if (profiling) { + bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].timestamp = OS::get_singleton()->get_ticks_msec(); + bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].packet_size = ofs; + bandwidth_outgoing_pointer = (bandwidth_outgoing_pointer + 1) % bandwidth_outgoing_data.size(); + } +#endif + // See if all peers have cached path (is so, call can be fast). bool has_all_peers = _send_confirm_path(from_path, psc, p_to); @@ -626,12 +623,9 @@ void MultiplayerAPI::_server_disconnected() { void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { - ERR_EXPLAIN("Trying to call an RPC while no network peer is active."); - ERR_FAIL_COND(!network_peer.is_valid()); - ERR_EXPLAIN("Trying to call an RPC on a node which is not inside SceneTree."); - ERR_FAIL_COND(!p_node->is_inside_tree()); - ERR_EXPLAIN("Trying to call an RPC via a network peer which is not connected."); - ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED); + ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active."); + ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); + ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); int node_id = network_peer->get_unique_id(); bool skip_rpc = node_id == p_peer_id; @@ -657,6 +651,15 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const } if (!skip_rpc) { + +#ifdef DEBUG_ENABLED + if (profiling) { + ObjectID id = p_node->get_instance_id(); + _init_node_profile(id); + profiler_frame_data[id].outgoing_rpc += 1; + } +#endif + _send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount); } @@ -668,7 +671,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const rpc_sender_id = temp_id; if (ce.error != Variant::CallError::CALL_OK) { String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in local call: - " + error; + error = "rpc() aborted in local call: - " + error + "."; ERR_PRINTS(error); return; } @@ -683,24 +686,20 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const rpc_sender_id = temp_id; if (ce.error != Variant::CallError::CALL_OK) { String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in script local call: - " + error; + error = "rpc() aborted in script local call: - " + error + "."; ERR_PRINTS(error); return; } } - ERR_EXPLAIN("RPC '" + p_method + "' on yourself is not allowed by selected mode"); - ERR_FAIL_COND(skip_rpc && !(call_local_native || call_local_script)); + ERR_FAIL_COND_MSG(skip_rpc && !(call_local_native || call_local_script), "RPC '" + p_method + "' on yourself is not allowed by selected mode."); } void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { - ERR_EXPLAIN("Trying to RSET while no network peer is active."); - ERR_FAIL_COND(!network_peer.is_valid()); - ERR_EXPLAIN("Trying to RSET on a node which is not inside SceneTree."); - ERR_FAIL_COND(!p_node->is_inside_tree()); - ERR_EXPLAIN("Trying to send an RSET via a network peer which is not connected."); - ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED); + ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to RSET while no network peer is active."); + ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to RSET on a node which is not inside SceneTree."); + ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to send an RSET via a network peer which is not connected."); int node_id = network_peer->get_unique_id(); bool is_master = p_node->is_network_master(); @@ -724,7 +723,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const rpc_sender_id = temp_id; if (!valid) { - String error = "rset() aborted in local set, property not found: - " + String(p_property); + String error = "rset() aborted in local set, property not found: - " + String(p_property) + "."; ERR_PRINTS(error); return; } @@ -742,7 +741,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const rpc_sender_id = temp_id; if (!valid) { - String error = "rset() aborted in local script set, property not found: - " + String(p_property); + String error = "rset() aborted in local script set, property not found: - " + String(p_property) + "."; ERR_PRINTS(error); return; } @@ -751,11 +750,18 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const } if (skip_rset) { - ERR_EXPLAIN("RSET for '" + p_property + "' on yourself is not allowed by selected mode"); - ERR_FAIL_COND(!set_local); + ERR_FAIL_COND_MSG(!set_local, "RSET for '" + p_property + "' on yourself is not allowed by selected mode."); return; } +#ifdef DEBUG_ENABLED + if (profiling) { + ObjectID id = p_node->get_instance_id(); + _init_node_profile(id); + profiler_frame_data[id].outgoing_rset += 1; + } +#endif + const Variant *vptr = &p_value; _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1); @@ -763,12 +769,9 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const Error MultiplayerAPI::send_bytes(PoolVector p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { - ERR_EXPLAIN("Trying to send an empty raw packet."); - ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA); - ERR_EXPLAIN("Trying to send a raw packet while no network peer is active."); - ERR_FAIL_COND_V(!network_peer.is_valid(), ERR_UNCONFIGURED); - ERR_EXPLAIN("Trying to send a raw packet via a network peer which is not connected."); - ERR_FAIL_COND_V(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); + ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active."); + ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); MAKE_ROOM(p_data.size() + 1); PoolVector::Read r = p_data.read(); @@ -783,8 +786,7 @@ Error MultiplayerAPI::send_bytes(PoolVector p_data, int p_to, Networked void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_EXPLAIN("Invalid packet received. Size too small."); - ERR_FAIL_COND(p_packet_len < 2); + ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); PoolVector out; int len = p_packet_len - 1; @@ -798,37 +800,32 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac int MultiplayerAPI::get_network_unique_id() const { - ERR_EXPLAIN("No network peer is assigned. Unable to get unique network ID."); - ERR_FAIL_COND_V(!network_peer.is_valid(), 0); + ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID."); return network_peer->get_unique_id(); } bool MultiplayerAPI::is_network_server() const { // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier? - ERR_EXPLAIN("No network peer is assigned. I can't be a server."); - ERR_FAIL_COND_V(!network_peer.is_valid(), false); + ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. I can't be a server."); return network_peer->is_server(); } void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { - ERR_EXPLAIN("No network peer is assigned. Unable to set 'refuse_new_connections'."); - ERR_FAIL_COND(!network_peer.is_valid()); + ERR_FAIL_COND_MSG(!network_peer.is_valid(), "No network peer is assigned. Unable to set 'refuse_new_connections'."); network_peer->set_refuse_new_connections(p_refuse); } bool MultiplayerAPI::is_refusing_new_network_connections() const { - ERR_EXPLAIN("No network peer is assigned. Unable to get 'refuse_new_connections'."); - ERR_FAIL_COND_V(!network_peer.is_valid(), false); + ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. Unable to get 'refuse_new_connections'."); return network_peer->is_refusing_new_connections(); } Vector MultiplayerAPI::get_network_connected_peers() const { - ERR_EXPLAIN("No network peer is assigned. Assume no peers are connected."); - ERR_FAIL_COND_V(!network_peer.is_valid(), Vector()); + ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), Vector(), "No network peer is assigned. Assume no peers are connected."); Vector ret; for (Set::Element *E = connected_peers.front(); E; E = E->next()) { @@ -848,6 +845,96 @@ bool MultiplayerAPI::is_object_decoding_allowed() const { return allow_object_decoding; } +void MultiplayerAPI::profiling_start() { +#ifdef DEBUG_ENABLED + profiling = true; + profiler_frame_data.clear(); + + bandwidth_incoming_pointer = 0; + bandwidth_incoming_data.resize(16384); // ~128kB + for (int i = 0; i < bandwidth_incoming_data.size(); ++i) { + bandwidth_incoming_data.write[i].packet_size = -1; + } + + bandwidth_outgoing_pointer = 0; + bandwidth_outgoing_data.resize(16384); // ~128kB + for (int i = 0; i < bandwidth_outgoing_data.size(); ++i) { + bandwidth_outgoing_data.write[i].packet_size = -1; + } +#endif +} + +void MultiplayerAPI::profiling_end() { +#ifdef DEBUG_ENABLED + profiling = false; + bandwidth_incoming_data.clear(); + bandwidth_outgoing_data.clear(); +#endif +} + +int MultiplayerAPI::get_profiling_frame(ProfilingInfo *r_info) { + int i = 0; +#ifdef DEBUG_ENABLED + for (Map::Element *E = profiler_frame_data.front(); E; E = E->next()) { + r_info[i] = E->get(); + ++i; + } + profiler_frame_data.clear(); +#endif + return i; +} + +int MultiplayerAPI::get_incoming_bandwidth_usage() { +#ifdef DEBUG_ENABLED + return _get_bandwidth_usage(bandwidth_incoming_data, bandwidth_incoming_pointer); +#else + return 0; +#endif +} + +int MultiplayerAPI::get_outgoing_bandwidth_usage() { +#ifdef DEBUG_ENABLED + return _get_bandwidth_usage(bandwidth_outgoing_data, bandwidth_outgoing_pointer); +#else + return 0; +#endif +} + +#ifdef DEBUG_ENABLED +int MultiplayerAPI::_get_bandwidth_usage(const Vector &p_buffer, int p_pointer) { + int total_bandwidth = 0; + + uint32_t timestamp = OS::get_singleton()->get_ticks_msec(); + uint32_t final_timestamp = timestamp - 1000; + + int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size(); + + while (i != p_pointer && p_buffer[i].packet_size > 0) { + if (p_buffer[i].timestamp < final_timestamp) { + return total_bandwidth; + } + total_bandwidth += p_buffer[i].packet_size; + i = (i + p_buffer.size() - 1) % p_buffer.size(); + } + + ERR_EXPLAIN("Reached the end of the bandwidth profiler buffer, values might be inaccurate."); + ERR_FAIL_COND_V(i == p_pointer, total_bandwidth); + return total_bandwidth; +} + +void MultiplayerAPI::_init_node_profile(ObjectID p_node) { + if (profiler_frame_data.has(p_node)) + return; + profiler_frame_data.insert(p_node, ProfilingInfo()); + profiler_frame_data[p_node].node = p_node; + profiler_frame_data[p_node].node_path = Object::cast_to(ObjectDB::get_instance(p_node))->get_path(); + profiler_frame_data[p_node].incoming_rpc = 0; + profiler_frame_data[p_node].incoming_rset = 0; + profiler_frame_data[p_node].outgoing_rpc = 0; + profiler_frame_data[p_node].outgoing_rset = 0; +} +#endif + void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); @@ -898,6 +985,9 @@ MultiplayerAPI::MultiplayerAPI() : allow_object_decoding(false) { rpc_sender_id = 0; root_node = NULL; +#ifdef DEBUG_ENABLED + profiling = false; +#endif clear(); } diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index 5258dde5d7..b824456e0f 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -38,6 +38,16 @@ class MultiplayerAPI : public Reference { GDCLASS(MultiplayerAPI, Reference); +public: + struct ProfilingInfo { + ObjectID node; + String node_path; + int incoming_rpc; + int incoming_rset; + int outgoing_rpc; + int outgoing_rset; + }; + private: //path sent caches struct PathSentCache { @@ -55,6 +65,23 @@ private: Map nodes; }; +#ifdef DEBUG_ENABLED + struct BandwidthFrame { + uint32_t timestamp; + int packet_size; + }; + + int bandwidth_incoming_pointer; + Vector bandwidth_incoming_data; + int bandwidth_outgoing_pointer; + Vector bandwidth_outgoing_data; + Map profiler_frame_data; + bool profiling; + + void _init_node_profile(ObjectID p_node); + int _get_bandwidth_usage(const Vector &p_buffer, int p_pointer); +#endif + Ref network_peer; int rpc_sender_id; Set connected_peers; @@ -130,6 +157,13 @@ public: void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; + void profiling_start(); + void profiling_end(); + + int get_profiling_frame(ProfilingInfo *r_info); + int get_incoming_bandwidth_usage(); + int get_outgoing_bandwidth_usage(); + MultiplayerAPI(); ~MultiplayerAPI(); }; diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index c77c81f9e2..1c792c43d1 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -110,10 +110,11 @@ Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) { Variant PacketPeer::_bnd_get_var(bool p_allow_objects) { Variant var; - get_var(var, p_allow_objects); + Error err = get_var(var, p_allow_objects); + ERR_FAIL_COND_V(err != OK, Variant()); return var; -}; +} Error PacketPeer::_put_packet(const PoolVector &p_buffer) { return put_packet_buffer(p_buffer); @@ -279,8 +280,7 @@ Ref PacketPeerStream::get_stream_peer() const { void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { //warning may lose packets - ERR_EXPLAIN("Buffer in use, resizing would cause loss of data"); - ERR_FAIL_COND(ring_buffer.data_left()); + ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data."); ring_buffer.resize(nearest_shift(p_max_size + 4)); input_buffer.resize(next_power_of_2(p_max_size + 4)); } diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index c16d89d695..1c89bc6268 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -64,10 +64,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment) { file = FileAccess::open(p_file, FileAccess::WRITE); - if (!file) { - ERR_EXPLAIN("Can't open file to write: " + String(p_file)); - ERR_FAIL_V(ERR_CANT_CREATE); - } + ERR_FAIL_COND_V_MSG(!file, ERR_CANT_CREATE, "Can't open file to write: " + String(p_file) + "."); alignment = p_alignment; diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 38bef2768e..0ad2479b05 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -490,8 +490,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { #endif } else { - ERR_EXPLAIN("Vector2 size is NOT 8!"); - ERR_FAIL_V(ERR_UNAVAILABLE); + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Vector2 size is NOT 8!"); } w.release(); r_v = array; @@ -518,8 +517,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { #endif } else { - ERR_EXPLAIN("Vector3 size is NOT 12!"); - ERR_FAIL_V(ERR_UNAVAILABLE); + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Vector3 size is NOT 12!"); } w.release(); r_v = array; @@ -546,8 +544,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { #endif } else { - ERR_EXPLAIN("Color size is NOT 16!"); - ERR_FAIL_V(ERR_UNAVAILABLE); + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Color size is NOT 16!"); } w.release(); r_v = array; @@ -571,7 +568,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { const uint32_t current_version = 0; if (format_version > current_version) { - ERR_PRINT("Format version for encoded binary image is too new"); + ERR_PRINT("Format version for encoded binary image is too new."); return ERR_PARSE_ERROR; } @@ -655,8 +652,7 @@ Error ResourceInteractiveLoaderBinary::poll() { } else { error = ERR_FILE_MISSING_DEPENDENCIES; - ERR_EXPLAIN("Can't load dependency: " + path); - ERR_FAIL_V(error); + ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + "."); } } else { @@ -711,16 +707,15 @@ Error ResourceInteractiveLoaderBinary::poll() { Object *obj = ClassDB::instance(t); if (!obj) { error = ERR_FILE_CORRUPT; - ERR_EXPLAIN(local_path + ":Resource of unrecognized type in file: " + t); - ERR_FAIL_V(ERR_FILE_CORRUPT); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + "."); } Resource *r = Object::cast_to(obj); if (!r) { + String obj_class = obj->get_class(); error = ERR_FILE_CORRUPT; memdelete(obj); //bye - ERR_EXPLAIN(local_path + ":Resource type in resource field not a resource, type is: " + obj->get_class()); - ERR_FAIL_V(ERR_FILE_CORRUPT); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource type in resource field not a resource, type is: " + obj_class + "."); } RES res = RES(r); @@ -850,8 +845,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { //not normal error = ERR_FILE_UNRECOGNIZED; - ERR_EXPLAIN("Unrecognized binary resource file: " + local_path); - ERR_FAIL(); + ERR_FAIL_MSG("Unrecognized binary resource file: " + local_path + "."); } bool big_endian = f->get_32(); @@ -877,8 +871,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { f->close(); - ERR_EXPLAIN("File Format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path); - ERR_FAIL(); + ERR_FAIL_MSG("File format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path + "."); } type = get_unicode_string(); @@ -926,8 +919,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { if (f->eof_reached()) { error = ERR_FILE_CORRUPT; - ERR_EXPLAIN("Premature End Of File: " + local_path); - ERR_FAIL(); + ERR_FAIL_MSG("Premature end of file (EOF): " + local_path + "."); } } @@ -1084,8 +1076,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons //error=ERR_FILE_UNRECOGNIZED; memdelete(f); - ERR_EXPLAIN("Unrecognized binary resource file: " + local_path); - ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file: " + local_path + "."); } else { fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE); if (!fw) { @@ -1122,7 +1113,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons memdelete(da); //use the old approach - WARN_PRINT(("This file is old, so it can't refactor dependencies, opening and resaving: " + p_path).utf8().get_data()); + WARN_PRINTS("This file is old, so it can't refactor dependencies, opening and resaving: " + p_path + "."); Error err; f = FileAccess::open(p_path, FileAccess::READ, &err); @@ -1153,8 +1144,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons memdelete(f); memdelete(fw); - ERR_EXPLAIN("File Format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path); - ERR_FAIL_V(ERR_FILE_UNRECOGNIZED); + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "File format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path + "."); } // Since we're not actually converting the file contents, leave the version @@ -1477,7 +1467,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia case Variant::_RID: { f->store_32(VARIANT_RID); - WARN_PRINT("Can't save RIDs"); + WARN_PRINT("Can't save RIDs."); RID val = p_property; f->store_32(val.get_id()); } break; @@ -1497,8 +1487,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia if (!resource_set.has(res)) { f->store_32(OBJECT_EMPTY); - ERR_EXPLAIN("Resource was not pre cached for the resource section, most likely due to circular refedence."); - ERR_FAIL(); + ERR_FAIL_MSG("Resource was not pre cached for the resource section, most likely due to circular reference."); } f->store_32(OBJECT_INTERNAL_RESOURCE); @@ -1629,8 +1618,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; default: { - ERR_EXPLAIN("Invalid variant"); - ERR_FAIL(); + ERR_FAIL_MSG("Invalid variant."); } } } @@ -1798,6 +1786,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) { f->close(); + memdelete(f); return ERR_CANT_CREATE; } @@ -1950,10 +1939,12 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) { f->close(); + memdelete(f); return ERR_CANT_CREATE; } f->close(); + memdelete(f); return OK; } diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index a29b9d1ddb..9e954890bc 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -275,12 +275,9 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c return res; } - if (found) { - ERR_EXPLAIN("Failed loading resource: " + p_path); - } else { - ERR_EXPLAIN("No loader found for resource: " + p_path); - } - ERR_FAIL_V(RES()); + ERR_FAIL_COND_V_MSG(found, RES(), "Failed loading resource: " + p_path + "."); + + ERR_FAIL_V_MSG(RES(), "No loader found for resource: " + p_path + "."); } bool ResourceLoader::_add_to_loading_map(const String &p_path) { @@ -355,10 +352,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p { bool success = _add_to_loading_map(local_path); - if (!success) { - ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); - ERR_FAIL_V(RES()); - } + ERR_FAIL_COND_V_MSG(!success, RES(), "Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); } //lock first if possible @@ -395,8 +389,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p if (!p_no_cache) { _remove_from_loading_map(local_path); } - ERR_EXPLAIN("Remapping '" + local_path + "'failed."); - ERR_FAIL_V(RES()); + ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed."); } print_verbose("Loading resource: " + path); @@ -479,10 +472,7 @@ Ref ResourceLoader::load_interactive(const String &p_ if (!p_no_cache) { bool success = _add_to_loading_map(local_path); - if (!success) { - ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); - ERR_FAIL_V(RES()); - } + ERR_FAIL_COND_V_MSG(!success, RES(), "Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); if (ResourceCache::has(local_path)) { @@ -503,8 +493,7 @@ Ref ResourceLoader::load_interactive(const String &p_ if (!p_no_cache) { _remove_from_loading_map(local_path); } - ERR_EXPLAIN("Remapping '" + local_path + "'failed."); - ERR_FAIL_V(RES()); + ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed."); } print_verbose("Loading resource: " + path); @@ -534,12 +523,9 @@ Ref ResourceLoader::load_interactive(const String &p_ _remove_from_loading_map(local_path); } - if (found) { - ERR_EXPLAIN("Failed loading resource: " + path); - } else { - ERR_EXPLAIN("No loader found for resource: " + path); - } - ERR_FAIL_V(Ref()); + ERR_FAIL_COND_V_MSG(found, Ref(), "Failed loading resource: " + path + "."); + + ERR_FAIL_V_MSG(Ref(), "No loader found for resource: " + path + "."); } void ResourceLoader::add_resource_format_loader(Ref p_format_loader, bool p_at_front) { @@ -801,7 +787,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem if (err == ERR_FILE_EOF) { break; } else if (err != OK) { - ERR_PRINTS("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text); + ERR_PRINTS("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + "."); break; } @@ -932,16 +918,13 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) { Ref